diff options
230 files changed, 7292 insertions, 2266 deletions
diff --git a/.gitignore b/.gitignore index 0865f981b1..77610b3193 100644 --- a/.gitignore +++ b/.gitignore @@ -162,6 +162,8 @@ uptime-*.json /src/lib/libtor-buf-testing.a /src/lib/libtor-compress.a /src/lib/libtor-compress-testing.a +/src/lib/libtor-confmgt.a +/src/lib/libtor-confmgt-testing.a /src/lib/libtor-container.a /src/lib/libtor-container-testing.a /src/lib/libtor-crypt-ops.a diff --git a/.travis.yml b/.travis.yml index c2f1637e04..36fb3a1b76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -199,6 +199,10 @@ install: - if [[ "$CHUTNEY" != "" ]]; then pushd "$CHUTNEY_PATH"; git log -1 ; popd ; fi ## If we're running stem, show the stem version and commit - if [[ "$TEST_STEM" != "" ]]; then pushd stem; python -c "from stem import stem; print(stem.__version__);"; git log -1; popd; fi + ## We don't want Tor tests to depend on default configuration file at + ## ~/.torrc. So we put some random bytes in there, to make sure we get build + ## failures in case Tor is reading it during CI jobs. + - dd ibs=1 count=1024 if=/dev/urandom > ~/.torrc script: # Skip test_rebind on macOS @@ -1,3 +1,175 @@ +Changes in version 0.4.1.5 - 2019-08-20 + This is the first stable release in the 0.4.1.x series. This series + adds experimental circuit-level padding, authenticated SENDME cells to + defend against certain attacks, and several performance improvements + to save on CPU consumption. It fixes bugs in bootstrapping and v3 + onion services. It also includes numerous smaller features and + bugfixes on earlier versions. + + Per our support policy, we will support the 0.4.1.x series for nine + months, or until three months after the release of a stable 0.4.2.x: + whichever is longer. If you need longer-term support, please stick + with 0.3.5.x, which will we plan to support until Feb 2022. + + Below are the changes since 0.4.1.4-rc. For a complete list of changes + since 0.4.0.5, see the ReleaseNotes file. + + o Directory authority changes: + - The directory authority "dizum" has a new IP address. Closes + ticket 31406. + + o Minor features (circuit padding logging): + - Demote noisy client-side warn logs about circuit padding to + protocol warnings. Add additional log messages and circuit ID + fields to help with bug 30992 and any other future issues. + + o Minor bugfixes (circuit padding negotiation): + - Bump the circuit padding protocol version to explicitly signify + that the HS setup machine support is finalized in 0.4.1.x-stable. + This also means that 0.4.1.x-alpha clients will not negotiate + padding with 0.4.1.x-stable relays, and 0.4.1.x-stable clients + will not negotiate padding with 0.4.1.x-alpha relays (or 0.4.0.x + relays). Fixes bug 31356; bugfix on 0.4.1.1-alpha. + + o Minor bugfixes (circuit padding): + - Ignore non-padding cells on padding circuits. This addresses + various warning messages from subsystems that were not expecting + padding circuits. Fixes bug 30942; bugfix on 0.4.1.1-alpha. + + o Minor bugfixes (clock skew detection): + - Don't believe clock skew results from NETINFO cells that appear to + arrive before we sent the VERSIONS cells they are responding to. + Previously, we would accept them up to 3 minutes "in the past". + Fixes bug 31343; bugfix on 0.2.4.4-alpha. + + o Minor bugfixes (compatibility, standards compliance): + - Fix a bug that would invoke undefined behavior on certain + operating systems when trying to asprintf() a string exactly + INT_MAX bytes long. We don't believe this is exploitable, but it's + better to fix it anyway. Fixes bug 31001; bugfix on 0.2.2.11-alpha. + Found and fixed by Tobias Stoeckmann. + + o Minor bugfixes (compilation warning): + - Fix a compilation warning on Windows about casting a function + pointer for GetTickCount64(). Fixes bug 31374; bugfix + on 0.2.9.1-alpha. + + o Minor bugfixes (compilation): + - Avoid using labs() on time_t, which can cause compilation warnings + on 64-bit Windows builds. Fixes bug 31343; bugfix on 0.2.4.4-alpha. + + o Minor bugfixes (distribution): + - Do not ship any temporary files found in the + scripts/maint/practracker directory. Fixes bug 31311; bugfix + on 0.4.1.1-alpha. + + o Testing (continuous integration): + - In Travis, make stem log a controller trace to the console, and + tail stem's tor log after failure. Closes ticket 30591. + - In Travis, only run the stem tests that use a tor binary. Closes + ticket 30694. + + +Changes in version 0.4.1.4-rc - 2019-07-25 + Tor 0.4.1.4-rc fixes a few bugs from previous versions of Tor, and + updates to a new list of fallback directories. If no new bugs are + found, the next release in the 0.4.1.x serious should be stable. + + o Major bugfixes (circuit build, guard): + - When considering upgrading circuits from "waiting for guard" to + "open", always ignore circuits that are marked for close. Otherwise, + we can end up in the situation where a subsystem is notified that + a closing circuit has just opened, leading to undesirable + behavior. Fixes bug 30871; bugfix on 0.3.0.1-alpha. + + o Minor features (continuous integration): + - Our Travis configuration now uses Chutney to run some network + integration tests automatically. Closes ticket 29280. + + o Minor features (fallback directory list): + - Replace the 157 fallbacks originally introduced in Tor 0.3.5.6-rc + in December 2018 (of which ~122 were still functional), with a + list of 148 fallbacks (70 new, 78 existing, 79 removed) generated + in June 2019. Closes ticket 28795. + + o Minor bugfixes (circuit padding): + - On relays, properly check that a padding machine is absent before + logging a warning about it being absent. Fixes bug 30649; bugfix + on 0.4.1.1-alpha. + - Add two NULL checks in unreachable places to silence Coverity (CID + 144729 and 1447291) and better future-proof ourselves. Fixes bug + 31024; bugfix on 0.4.1.1-alpha. + + o Minor bugfixes (crash on exit): + - Avoid a set of possible code paths that could try to use freed + memory in routerlist_free() while Tor was exiting. Fixes bug + 31003; bugfix on 0.1.2.2-alpha. + + o Minor bugfixes (logging): + - Fix a conflict between the flag used for messaging-domain log + messages, and the LD_NO_MOCK testing flag. Fixes bug 31080; bugfix + on 0.4.1.1-alpha. + + o Minor bugfixes (memory leaks): + - Fix a trivial memory leak when parsing an invalid value from a + download schedule in the configuration. Fixes bug 30894; bugfix + on 0.3.4.1-alpha. + + o Code simplification and refactoring: + - Remove some dead code from circpad_machine_remove_token() to fix + some Coverity warnings (CID 1447298). Fixes bug 31027; bugfix + on 0.4.1.1-alpha. + + +Changes in version 0.4.1.3-alpha - 2019-06-25 + Tor 0.4.1.3-alpha resolves numerous bugs left over from the previous + alpha, most of them from earlier release series. + + o Major bugfixes (Onion service reachability): + - Properly clean up the introduction point map when circuits change + purpose from onion service circuits to pathbias, measurement, or + other circuit types. This should fix some service-side instances + of introduction point failure. Fixes bug 29034; bugfix + on 0.3.2.1-alpha. + + o Minor features (geoip): + - Update geoip and geoip6 to the June 10 2019 Maxmind GeoLite2 + Country database. Closes ticket 30852. + + o Minor features (logging): + - Give a more useful assertion failure message if we think we have + minherit() but we fail to make a region non-inheritable. Give a + compile-time warning if our support for minherit() is incomplete. + Closes ticket 30686. + + o Minor bugfixes (circuit isolation): + - Fix a logic error that prevented the SessionGroup sub-option from + being accepted. Fixes bug 22619; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (continuous integration): + - Allow the test-stem job to fail in Travis, because it sometimes + hangs. Fixes bug 30744; bugfix on 0.3.5.4-alpha. + - Skip test_rebind on macOS in Travis, because it is unreliable on + macOS on Travis. Fixes bug 30713; bugfix on 0.3.5.1-alpha. + - Skip test_rebind when the TOR_SKIP_TEST_REBIND environment + variable is set. Fixes bug 30713; bugfix on 0.3.5.1-alpha. + + o Minor bugfixes (directory authorities): + - Stop crashing after parsing an unknown descriptor purpose + annotation. We think this bug can only be triggered by modifying a + local file. Fixes bug 30781; bugfix on 0.2.0.8-alpha. + + o Minor bugfixes (pluggable transports): + - When running as a bridge with pluggable transports, always publish + pluggable transport information in our extrainfo descriptor, even + if ExtraInfoStatistics is 0. This information is needed by + BridgeDB. Fixes bug 30956; bugfix on 0.4.1.1-alpha. + + o Documentation: + - Mention URLs for Travis/Appveyor/Jenkins in ReleasingTor.md. + Closes ticket 30630. + + Changes in version 0.4.1.2-alpha - 2019-06-06 Tor 0.4.1.2-alpha resolves numerous bugs--some of them from the previous alpha, and some much older. It also contains minor testing diff --git a/Makefile.am b/Makefile.am index 10bd4b45c2..bb5dee52e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ TOR_UTIL_LIBS = \ src/lib/libtor-geoip.a \ src/lib/libtor-process.a \ src/lib/libtor-buf.a \ + src/lib/libtor-confmgt.a \ src/lib/libtor-pubsub.a \ src/lib/libtor-dispatch.a \ src/lib/libtor-time.a \ @@ -74,6 +75,7 @@ TOR_UTIL_TESTING_LIBS = \ src/lib/libtor-geoip-testing.a \ src/lib/libtor-process-testing.a \ src/lib/libtor-buf-testing.a \ + src/lib/libtor-confmgt-testing.a \ src/lib/libtor-pubsub-testing.a \ src/lib/libtor-dispatch-testing.a \ src/lib/libtor-time-testing.a \ @@ -169,7 +171,17 @@ EXTRA_DIST+= \ scripts/maint/practracker/exceptions.txt \ scripts/maint/practracker/metrics.py \ scripts/maint/practracker/practracker.py \ + scripts/maint/practracker/practracker_tests.py \ scripts/maint/practracker/problem.py \ + scripts/maint/practracker/testdata/a.c \ + scripts/maint/practracker/testdata/b.c \ + scripts/maint/practracker/testdata/ex0-expected.txt \ + scripts/maint/practracker/testdata/ex0.txt \ + scripts/maint/practracker/testdata/ex1-expected.txt \ + scripts/maint/practracker/testdata/ex1.txt \ + scripts/maint/practracker/testdata/ex.txt \ + scripts/maint/practracker/testdata/not_c_file \ + scripts/maint/practracker/test_practracker.sh \ scripts/maint/practracker/util.py ## This tells etags how to find mockable function definitions. @@ -188,7 +200,7 @@ TEST_CFLAGS= TEST_CPPFLAGS=-DTOR_UNIT_TESTS @TOR_MODULES_ALL_ENABLED@ TEST_NETWORK_FLAGS=--hs-multi-client 1 endif -TEST_NETWORK_WARNING_FLAGS=--quiet --only-warnings +TEST_NETWORK_SHOW_WARNINGS_FOR_LAST_RUN_FLAGS=--quiet --only-warnings if LIBFUZZER_ENABLED TEST_CFLAGS += -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div @@ -227,13 +239,22 @@ test: all shellcheck: # Only use shellcheck if it is present if command -v shellcheck; then \ - find $(top_srcdir)/scripts/ -name "*.sh" -exec shellcheck {} +; \ + find "$(top_srcdir)" -name "*.sh" -not -path "$(top_srcdir)/src/ext/*" -not -path "$(top_srcdir)/src/rust/registry/*" -exec shellcheck {} +; \ if [ -d "$(top_srcdir)/scripts/test" ]; then \ shellcheck $(top_srcdir)/scripts/test/cov-diff $(top_srcdir)/scripts/test/coverage; \ fi; \ + if [ -e "$(top_srcdir)/contrib/dirauth-tools/nagios-check-tor-authority-cert" ]; then \ + shellcheck "$(top_srcdir)/contrib/dirauth-tools/nagios-check-tor-authority-cert"; \ + fi; \ + if [ -e "$(top_srcdir)/contrib/client-tools/torify" ]; then \ + shellcheck "$(top_srcdir)/contrib/client-tools/torify"; \ + fi; \ + if [ -d "$(top_srcdir)/scripts/git" ]; then \ + shellcheck $(top_srcdir)/scripts/git/*.git-hook; \ + fi; \ fi -check-local: check-spaces check-changes check-includes shellcheck +check-local: check-spaces check-changes check-includes check-best-practices shellcheck need-chutney-path: @if test ! -d "$$CHUTNEY_PATH"; then \ @@ -253,12 +274,15 @@ test-network: need-chutney-path $(TESTING_TOR_BINARY) src/tools/tor-gencert $(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_FLAGS) # Run all available tests using automake's test-driver -# only run IPv6 tests if we can ping6 ::1 (localhost) -# only run IPv6 tests if we can ping ::1 (localhost) -# some IPv6 tests will fail without an IPv6 DNS server (see #16971 and #17011) -# only run mixed tests if we have a tor-stable binary -# Try the syntax for BSD ping6, Linux ping6, and Linux ping -6, -# because they're incompatible +# - only run IPv6 tests if we can ping6 or ping -6 ::1 (localhost) +# we try the syntax for BSD ping6, Linux ping6, and Linux ping -6, +# because they're incompatible +# - some IPv6 tests may fail without an IPv6 DNS server +# (see #16971 and #17011) +# - only run mixed tests if we have a tor-stable binary +# - show tor warnings on the console after each network run +# (otherwise, warnings go to the logs, and people don't see them unless +# there is a network failure) test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert mkdir -p $(TEST_NETWORK_ALL_LOG_DIR) rm -f $(TEST_NETWORK_ALL_LOG_DIR)/*.log $(TEST_NETWORK_ALL_LOG_DIR)/*.trs @@ -282,7 +306,7 @@ test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/ done; \ for f in $$flavors; do \ $(SHELL) $(top_srcdir)/test-driver --test-name $$f --log-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.log --trs-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.trs $(TEST_NETWORK_ALL_DRIVER_FLAGS) $(top_srcdir)/src/test/test-network.sh --flavor $$f $(TEST_NETWORK_FLAGS); \ - $(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_WARNING_FLAGS); \ + $(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_SHOW_WARNINGS_FOR_LAST_RUN_FLAGS); \ done; \ echo "Log and result files are available in $(TEST_NETWORK_ALL_LOG_DIR)."; \ ! grep -q FAIL $(TEST_NETWORK_ALL_LOG_DIR)/*.trs @@ -360,7 +384,7 @@ endif check-best-practices: if USEPYTHON - $(PYTHON) $(top_srcdir)/scripts/maint/practracker/practracker.py $(top_srcdir) + @$(PYTHON) $(top_srcdir)/scripts/maint/practracker/practracker.py $(top_srcdir) $(TOR_PRACTRACKER_OPTIONS) endif practracker-regen: diff --git a/ReleaseNotes b/ReleaseNotes index 788804236b..b378fd07a9 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -2,6 +2,557 @@ 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.4.1.5 - 2019-08-20 + This is the first stable release in the 0.4.1.x series. This series + adds experimental circuit-level padding, authenticated SENDME cells to + defend against certain attacks, and several performance improvements + to save on CPU consumption. It fixes bugs in bootstrapping and v3 + onion services. It also includes numerous smaller features and + bugfixes on earlier versions. + + Per our support policy, we will support the 0.4.1.x series for nine + months, or until three months after the release of a stable 0.4.2.x: + whichever is longer. If you need longer-term support, please stick + with 0.3.5.x, which will we plan to support until Feb 2022. + + Below are the changes since 0.4.0.5. For a list of only the changes + since 0.4.1.4-rc, see the ChangeLog file. + + o Directory authority changes: + - The directory authority "dizum" has a new IP address. Closes + ticket 31406. + + o Major features (circuit padding): + - Onion service clients now add padding cells at the start of their + INTRODUCE and RENDEZVOUS circuits, to make those circuits' traffic + look more like general purpose Exit traffic. The overhead for this + is 2 extra cells in each direction for RENDEZVOUS circuits, and 1 + extra upstream cell and 10 downstream cells for INTRODUCE + circuits. This feature is only enabled when also supported by the + circuit's middle node. (Clients may specify fixed middle nodes + with the MiddleNodes option, and may force-disable this feature + with the CircuitPadding option.) Closes ticket 28634. + + o Major features (code organization): + - Tor now includes a generic publish-subscribe message-passing + subsystem that we can use to organize intermodule dependencies. We + hope to use this to reduce dependencies between modules that don't + need to be related, and to generally simplify our codebase. Closes + ticket 28226. + + o Major features (controller protocol): + - Controller commands are now parsed using a generalized parsing + subsystem. Previously, each controller command was responsible for + parsing its own input, which led to strange inconsistencies. + Closes ticket 30091. + + o Major features (flow control): + - Implement authenticated SENDMEs as detailed in proposal 289. A + SENDME cell now includes the digest of the traffic that it + acknowledges, so that once an end point receives the SENDME, it + can confirm the other side's knowledge of the previous cells that + were sent, and prevent certain types of denial-of-service attacks. + This behavior is controlled by two new consensus parameters: see + the proposal for more details. Fixes ticket 26288. + + o Major features (performance): + - Our node selection algorithm now excludes nodes in linear time. + Previously, the algorithm was quadratic, which could slow down + heavily used onion services. Closes ticket 30307. + + o Major features (performance, RNG): + - Tor now constructs a fast secure pseudorandom number generator for + each thread, to use when performance is critical. This PRNG is + based on AES-CTR, using a buffering construction similar to + libottery and the (newer) OpenBSD arc4random() code. It + outperforms OpenSSL 1.1.1a's CSPRNG by roughly a factor of 100 for + small outputs. Although we believe it to be cryptographically + strong, we are only using it when necessary for performance. + Implements tickets 29023 and 29536. + + o Major bugfixes (bridges): + - Consider our directory information to have changed when our list + of bridges changes. Previously, Tor would not re-compute the + status of its directory information when bridges changed, and + therefore would not realize that it was no longer able to build + circuits. Fixes part of bug 29875. + - Do not count previously configured working bridges towards our + total of working bridges. Previously, when Tor's list of bridges + changed, it would think that the old bridges were still usable, + and delay fetching router descriptors for the new ones. Fixes part + of bug 29875; bugfix on 0.3.0.1-alpha. + + o Major bugfixes (circuit build, guard): + - When considering upgrading circuits from "waiting for guard" to + "open", always ignore circuits that are marked for close. Otherwise, + we can end up in the situation where a subsystem is notified that + a closing circuit has just opened, leading to undesirable + behavior. Fixes bug 30871; bugfix on 0.3.0.1-alpha. + + o Major bugfixes (onion service reachability): + - Properly clean up the introduction point map when circuits change + purpose from onion service circuits to pathbias, measurement, or + other circuit types. This should fix some service-side instances + of introduction point failure. Fixes bug 29034; bugfix + on 0.3.2.1-alpha. + + o Major bugfixes (onion service v3): + - Fix an unreachable bug in which an introduction point could try to + send an INTRODUCE_ACK with a status code that Trunnel would refuse + to encode, leading the relay to assert(). We've consolidated the + ABI values into Trunnel now. Fixes bug 30454; bugfix + on 0.3.0.1-alpha. + - Clients can now handle unknown status codes from INTRODUCE_ACK + cells. (The NACK behavior will stay the same.) This will allow us + to extend status codes in the future without breaking the normal + client behavior. Fixes another part of bug 30454; bugfix + on 0.3.0.1-alpha. + + o Minor features (authenticated SENDME): + - Ensure that there is enough randomness on every circuit to prevent + an attacker from successfully predicting the hashes they will need + to include in authenticated SENDME cells. At a random interval, if + we have not sent randomness already, we now leave some extra space + at the end of a cell that we can fill with random bytes. Closes + ticket 26846. + + o Minor features (circuit padding logging): + - Demote noisy client-side warn logs about circuit padding to protocol + warnings. Add additional log messages and circuit ID fields to help + with bug 30992 and any other future issues. + + o Minor features (circuit padding): + - We now use a fast PRNG when scheduling circuit padding. Part of + ticket 28636. + - Allow the padding machine designer to pick the edges of their + histogram instead of trying to compute them automatically using an + exponential formula. Resolves some undefined behavior in the case + of small histograms and allows greater flexibility on machine + design. Closes ticket 29298; bugfix on 0.4.0.1-alpha. + - Allow circuit padding machines to hold a circuit open until they + are done padding it. Closes ticket 28780. + + o Minor features (compile-time modules): + - Add a "--list-modules" command to print a list of which compile- + time modules are enabled. Closes ticket 30452. + + o Minor features (continuous integration): + - Our Travis configuration now uses Chutney to run some network + integration tests automatically. Closes ticket 29280. + - When running coverage builds on Travis, we now set + TOR_TEST_RNG_SEED, to avoid RNG-based coverage differences. Part + of ticket 28878. + - Remove sudo configuration lines from .travis.yml as they are no + longer needed with current Travis build environment. Resolves + issue 30213. + - In Travis, show stem's tor log after failure. Closes ticket 30234. + + o Minor features (controller): + - Add onion service version 3 support to the HSFETCH command. + Previously, only version 2 onion services were supported. Closes + ticket 25417. Patch by Neel Chauhan. + + o Minor features (debugging): + - Introduce tor_assertf() and tor_assertf_nonfatal() to enable + logging of additional information during assert failure. Now we + can use format strings to include information for trouble + shooting. Resolves ticket 29662. + + o Minor features (defense in depth): + - In smartlist_remove_keeporder(), set unused pointers to NULL, in + case a bug causes them to be used later. Closes ticket 30176. + Patch from Tobias Stoeckmann. + - Tor now uses a cryptographically strong PRNG even for decisions + that we do not believe are security-sensitive. Previously, for + performance reasons, we had used a trivially predictable linear + congruential generator algorithm for certain load-balancing and + statistical sampling decisions. Now we use our fast RNG in those + cases. Closes ticket 29542. + + o Minor features (developer tools): + - Tor's "practracker" test script now checks for files and functions + that seem too long and complicated. Existing overlong functions + and files are accepted for now, but should eventually be + refactored. Closes ticket 29221. + - Add some scripts used for git maintenance to scripts/git. Closes + ticket 29391. + - Call practracker from pre-push and pre-commit git hooks to let + developers know if they made any code style violations. Closes + ticket 30051. + - Add a script to check that each header has a well-formed and + unique guard macro. Closes ticket 29756. + + o Minor features (fallback directory list): + - Replace the 157 fallbacks originally introduced in Tor 0.3.5.6-rc + in December 2018 (of which ~122 were still functional), with a + list of 148 fallbacks (70 new, 78 existing, 79 removed) generated + in June 2019. Closes ticket 28795. + + o Minor features (geoip): + - Update geoip and geoip6 to the June 10 2019 Maxmind GeoLite2 + Country database. Closes ticket 30852. + - Update geoip and geoip6 to the May 13 2019 Maxmind GeoLite2 + Country database. Closes ticket 30522. + + o Minor features (HTTP tunnel): + - Return an informative web page when the HTTPTunnelPort is used as + an HTTP proxy. Closes ticket 27821, patch by "eighthave". + + o Minor features (IPv6, v3 onion services): + - Make v3 onion services put IPv6 addresses in service descriptors. + Before this change, service descriptors only contained IPv4 + addresses. Implements 26992. + + o Minor features (logging): + - Give a more useful assertion failure message if we think we have + minherit() but we fail to make a region non-inheritable. Give a + compile-time warning if our support for minherit() is incomplete. + Closes ticket 30686. + + o Minor features (maintenance): + - Add a new "make autostyle" target that developers can use to apply + all automatic Tor style and consistency conversions to the + codebase. Closes ticket 30539. + + o Minor features (modularity): + - The "--disable-module-dirauth" compile-time option now disables + even more dirauth-only code. Closes ticket 30345. + + o Minor features (performance): + - Use OpenSSL's implementations of SHA3 when available (in OpenSSL + 1.1.1 and later), since they tend to be faster than tiny-keccak. + Closes ticket 28837. + + o Minor features (testing): + - The circuitpadding tests now use a reproducible RNG implementation, + so that if a test fails, we can learn why. Part of ticket 28878. + - Tor's tests now support an environment variable, TOR_TEST_RNG_SEED, + to set the RNG seed for tests that use a reproducible RNG. Part of + ticket 28878. + - When running tests in coverage mode, take additional care to make + our coverage deterministic, so that we can accurately track + changes in code coverage. Closes ticket 30519. + - Tor's unit test code now contains helper functions to replace the + PRNG with a deterministic or reproducible version for testing. + Previously, various tests implemented this in various ways. + Implements ticket 29732. + - We now have a script, cov-test-determinism.sh, to identify places + where our unit test coverage has become nondeterministic. Closes + ticket 29436. + - Check that representative subsets of values of `int` and `unsigned + int` can be represented by `void *`. Resolves issue 29537. + + o Minor bugfixes (bridge authority): + - Bridge authorities now set bridges as running or non-running when + about to dump their status to a file. Previously, they set bridges + as running in response to a GETINFO command, but those shouldn't + modify data structures. Fixes bug 24490; bugfix on 0.2.0.13-alpha. + Patch by Neel Chauhan. + + o Minor bugfixes (channel padding statistics): + - Channel padding write totals and padding-enabled totals are now + counted properly in relay extrainfo descriptors. Fixes bug 29231; + bugfix on 0.3.1.1-alpha. + + o Minor bugfixes (circuit isolation): + - Fix a logic error that prevented the SessionGroup sub-option from + being accepted. Fixes bug 22619; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (circuit padding): + - Add a "CircuitPadding" torrc option to disable circuit padding. + Fixes bug 28693; bugfix on 0.4.0.1-alpha. + - Allow circuit padding machines to specify that they do not + contribute much overhead, and provide consensus flags and torrc + options to force clients to only use these low overhead machines. + Fixes bug 29203; bugfix on 0.4.0.1-alpha. + - Provide a consensus parameter to fully disable circuit padding, to + be used in emergency network overload situations. Fixes bug 30173; + bugfix on 0.4.0.1-alpha. + - The circuit padding subsystem will no longer schedule padding if + dormant mode is enabled. Fixes bug 28636; bugfix on 0.4.0.1-alpha. + - Inspect a circuit-level cell queue before sending padding, to + avoid sending padding while too much data is already queued. Fixes + bug 29204; bugfix on 0.4.0.1-alpha. + - Avoid calling monotime_absolute_usec() in circuit padding machines + that do not use token removal or circuit RTT estimation. Fixes bug + 29085; bugfix on 0.4.0.1-alpha. + + o Minor bugfixes (clock skew detection): + - Don't believe clock skew results from NETINFO cells that appear to + arrive before we sent the VERSIONS cells they are responding to. + Previously, we would accept them up to 3 minutes "in the past". + Fixes bug 31343; bugfix on 0.2.4.4-alpha. + + o Minor bugfixes (compatibility, standards compliance): + - Fix a bug that would invoke undefined behavior on certain + operating systems when trying to asprintf() a string exactly + INT_MAX bytes long. We don't believe this is exploitable, but it's + better to fix it anyway. Fixes bug 31001; bugfix on 0.2.2.11-alpha. + Found and fixed by Tobias Stoeckmann. + + o Minor bugfixes (compilation warning): + - Fix a compilation warning on Windows about casting a function + pointer for GetTickCount64(). Fixes bug 31374; bugfix on + 0.2.9.1-alpha. + + o Minor bugfixes (compilation): + - Avoid using labs() on time_t, which can cause compilation warnings + on 64-bit Windows builds. Fixes bug 31343; bugfix on 0.2.4.4-alpha. + + o Minor bugfixes (compilation, unusual configurations): + - Avoid failures when building with the ALL_BUGS_ARE_FATAL option + due to missing declarations of abort(), and prevent other such + failures in the future. Fixes bug 30189; bugfix on 0.3.4.1-alpha. + + o Minor bugfixes (configuration, proxies): + - Fix a bug that prevented us from supporting SOCKS5 proxies that + want authentication along with configured (but unused!) + ClientTransportPlugins. Fixes bug 29670; bugfix on 0.2.6.1-alpha. + + o Minor bugfixes (continuous integration): + - Allow the test-stem job to fail in Travis, because it sometimes + hangs. Fixes bug 30744; bugfix on 0.3.5.4-alpha. + - Skip test_rebind on macOS in Travis, because it is unreliable on + macOS on Travis. Fixes bug 30713; bugfix on 0.3.5.1-alpha. + - Skip test_rebind when the TOR_SKIP_TEST_REBIND environment + variable is set. Fixes bug 30713; bugfix on 0.3.5.1-alpha. + + o Minor bugfixes (controller protocol): + - Teach the controller parser to distinguish an object preceded by + an argument list from one without. Previously, it couldn't + distinguish an argument list from the first line of a multiline + object. Fixes bug 29984; bugfix on 0.2.3.8-alpha. + + o Minor bugfixes (crash on exit): + - Avoid a set of possible code paths that could try to use freed + memory in routerlist_free() while Tor was exiting. Fixes bug + 31003; bugfix on 0.1.2.2-alpha. + + o Minor bugfixes (developer tooling): + - Fix pre-push hook to allow fixup and squash commits when pushing + to non-upstream git remote. Fixes bug 30286; bugfix + on 0.4.0.1-alpha. + + o Minor bugfixes (directory authorities): + - Stop crashing after parsing an unknown descriptor purpose + annotation. We think this bug can only be triggered by modifying a + local file. Fixes bug 30781; bugfix on 0.2.0.8-alpha. + - Move the "bandwidth-file-headers" line in directory authority + votes so that it conforms to dir-spec.txt. Fixes bug 30316; bugfix + on 0.3.5.1-alpha. + - Directory authorities with IPv6 support now always mark themselves + as reachable via IPv6. Fixes bug 24338; bugfix on 0.2.4.1-alpha. + Patch by Neel Chauhan. + + o Minor bugfixes (documentation): + - Improve the documentation for using MapAddress with ".exit". Fixes + bug 30109; bugfix on 0.1.0.1-rc. + - Improve the monotonic time module and function documentation to + explain what "monotonic" actually means, and document some results + that have surprised people. Fixes bug 29640; bugfix + on 0.2.9.1-alpha. + - Use proper formatting when providing an example on quoting options + that contain whitespace. Fixes bug 29635; bugfix on 0.2.3.18-rc. + + o Minor bugfixes (logging): + - Do not log a warning when running with an OpenSSL version other + than the one Tor was compiled with, if the two versions should be + compatible. Previously, we would warn whenever the version was + different. Fixes bug 30190; bugfix on 0.2.4.2-alpha. + - Warn operators when the MyFamily option is set but ContactInfo is + missing, as the latter should be set too. Fixes bug 25110; bugfix + on 0.3.3.1-alpha. + + o Minor bugfixes (memory leaks): + - Avoid a minor memory leak that could occur on relays when failing + to create a "keys" directory. Fixes bug 30148; bugfix + on 0.3.3.1-alpha. + - Fix a trivial memory leak when parsing an invalid value from a + download schedule in the configuration. Fixes bug 30894; bugfix + on 0.3.4.1-alpha. + + o Minor bugfixes (NetBSD): + - Fix usage of minherit() on NetBSD and other platforms that define + MAP_INHERIT_{ZERO,NONE} instead of INHERIT_{ZERO,NONE}. Fixes bug + 30614; bugfix on 0.4.0.2-alpha. Patch from Taylor Campbell. + + o Minor bugfixes (onion services): + - Avoid a GCC 9.1.1 warning (and possible crash depending on libc + implemenation) when failing to load an onion service client + authorization file. Fixes bug 30475; bugfix on 0.3.5.1-alpha. + - When refusing to launch a controller's HSFETCH request because of + rate-limiting, respond to the controller with a new response, + "QUERY_RATE_LIMITED". Previously, we would log QUERY_NO_HSDIR for + this case. Fixes bug 28269; bugfix on 0.3.1.1-alpha. Patch by + Neel Chauhan. + - When relaunching a circuit to a rendezvous service, mark the + circuit as needing high-uptime routers as appropriate. Fixes bug + 17357; bugfix on 0.1.0.1-rc. Patch by Neel Chauhan. + - Stop ignoring IPv6 link specifiers sent to v3 onion services. + (IPv6 support for v3 onion services is still incomplete: see + ticket 23493 for details.) Fixes bug 23588; bugfix on + 0.3.2.1-alpha. Patch by Neel Chauhan. + + o Minor bugfixes (onion services, performance): + - When building circuits to onion services, call tor_addr_parse() + less often. Previously, we called tor_addr_parse() in + circuit_is_acceptable() even if its output wasn't used. This + change should improve performance when building circuits. Fixes + bug 22210; bugfix on 0.2.8.12. Patch by Neel Chauhan. + + o Minor bugfixes (out-of-memory handler): + - When purging the DNS cache because of an out-of-memory condition, + try purging just the older entries at first. Previously, we would + always purge the whole thing. Fixes bug 29617; bugfix + on 0.3.5.1-alpha. + + o Minor bugfixes (performance): + - When checking whether a node is a bridge, use a fast check to make + sure that its identity is set. Previously, we used a constant-time + check, which is not necessary in this case. Fixes bug 30308; + bugfix on 0.3.5.1-alpha. + + o Minor bugfixes (pluggable transports): + - Tor now sets TOR_PT_EXIT_ON_STDIN_CLOSE=1 for client transports as + well as servers. Fixes bug 25614; bugfix on 0.2.7.1-alpha. + + o Minor bugfixes (portability): + - Avoid crashing in our tor_vasprintf() implementation on systems + that define neither vasprintf() nor _vscprintf(). (This bug has + been here long enough that we question whether people are running + Tor on such systems, but we're applying the fix out of caution.) + Fixes bug 30561; bugfix on 0.2.8.2-alpha. Found and fixed by + Tobias Stoeckmann. + + o Minor bugfixes (probability distributions): + - Refactor and improve parts of the probability distribution code + that made Coverity complain. Fixes bug 29805; bugfix + on 0.4.0.1-alpha. + + o Minor bugfixes (python): + - Stop assuming that /usr/bin/python3 exists. For scripts that work + with python2, use /usr/bin/python. Otherwise, use /usr/bin/env + python3. Fixes bug 29913; bugfix on 0.2.5.3-alpha. + + o Minor bugfixes (relay): + - When running as a relay, if IPv6Exit is set to 1 while ExitRelay + is auto, act as if ExitRelay is 1. Previously, we would ignore + IPv6Exit if ExitRelay was 0 or auto. Fixes bug 29613; bugfix on + 0.3.5.1-alpha. Patch by Neel Chauhan. + + o Minor bugfixes (static analysis): + - Fix several spurious Coverity warnings about the unit tests, to + lower our chances of missing real warnings in the future. Fixes + bug 30150; bugfix on 0.3.5.1-alpha and various other Tor versions. + + o Minor bugfixes (stats): + - When ExtraInfoStatistics is 0, stop including bandwidth usage + statistics, GeoIPFile hashes, ServerTransportPlugin lines, and + bridge statistics by country in extra-info documents. Fixes bug + 29018; bugfix on 0.2.4.1-alpha. + + o Minor bugfixes (testing): + - Call setrlimit() to disable core dumps in test_bt_cl.c. Previously + we used `ulimit -c` in test_bt.sh, which violates POSIX shell + compatibility. Fixes bug 29061; bugfix on 0.3.5.1-alpha. + - Fix some incorrect code in the v3 onion service unit tests. Fixes + bug 29243; bugfix on 0.3.2.1-alpha. + - In the "routerkeys/*" tests, check the return values of mkdir() + for possible failures. Fixes bug 29939; bugfix on 0.2.7.2-alpha. + Found by Coverity as CID 1444254. + - Split test_utils_general() into several smaller test functions. + This makes it easier to perform resource deallocation on assert + failure, and fixes Coverity warnings CID 1444117 and CID 1444118. + Fixes bug 29823; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (tor-resolve): + - Fix a memory leak in tor-resolve that could happen if Tor gave it + a malformed SOCKS response. (Memory leaks in tor-resolve don't + actually matter, but it's good to fix them anyway.) Fixes bug + 30151; bugfix on 0.4.0.1-alpha. + + o Code simplification and refactoring: + - Abstract out the low-level formatting of replies on the control + port. Implements ticket 30007. + - Add several assertions in an attempt to fix some Coverity + warnings. Closes ticket 30149. + - Introduce a connection_dir_buf_add() helper function that checks + for compress_state of dir_connection_t and automatically writes a + string to directory connection with or without compression. + Resolves issue 28816. + - Make the base32_decode() API return the number of bytes written, + for consistency with base64_decode(). Closes ticket 28913. + - Move most relay-only periodic events out of mainloop.c into the + relay subsystem. Closes ticket 30414. + - Refactor and encapsulate parts of the codebase that manipulate + crypt_path_t objects. Resolves issue 30236. + - Refactor several places in our code that Coverity incorrectly + believed might have memory leaks. Closes ticket 30147. + - Remove redundant return values in crypto_format, and the + associated return value checks elsewhere in the code. Make the + implementations in crypto_format consistent, and remove redundant + code. Resolves ticket 29660. + - Rename tor_mem_is_zero() to fast_mem_is_zero(), to emphasize that + it is not a constant-time function. Closes ticket 30309. + - Replace hs_desc_link_specifier_t with link_specifier_t, and remove + all hs_desc_link_specifier_t-specific code. Fixes bug 22781; + bugfix on 0.3.2.1-alpha. + - Simplify v3 onion service link specifier handling code. Fixes bug + 23576; bugfix on 0.3.2.1-alpha. + - Split crypto_digest.c into NSS code, OpenSSL code, and shared + code. Resolves ticket 29108. + - Split control.c into several submodules, in preparation for + distributing its current responsibilities throughout the codebase. + Closes ticket 29894. + - Start to move responsibility for knowing about periodic events to + the appropriate subsystems, so that the mainloop doesn't need to + know all the periodic events in the rest of the codebase. + Implements tickets 30293 and 30294. + + o Documentation: + - Mention URLs for Travis/Appveyor/Jenkins in ReleasingTor.md. + Closes ticket 30630. + - Document how to find git commits and tags for bug fixes in + CodingStandards.md. Update some file documentation. Closes + ticket 30261. + + o Removed features: + - Remove the linux-tor-prio.sh script from contrib/operator-tools + directory. Resolves issue 29434. + - Remove the obsolete OpenSUSE initscript. Resolves issue 30076. + - Remove the obsolete script at contrib/dist/tor.sh.in. Resolves + issue 30075. + + o Testing: + - Specify torrc paths (with empty files) when launching tor in + integration tests; refrain from reading user and system torrcs. + Resolves issue 29702. + + o Code simplification and refactoring (shell scripts): + - Clean up many of our shell scripts to fix shellcheck warnings. + These include autogen.sh (ticket 26069), test_keygen.sh (ticket + 29062), test_switch_id.sh (ticket 29065), test_rebind.sh (ticket + 29063), src/test/fuzz/minimize.sh (ticket 30079), test_rust.sh + (ticket 29064), torify (ticket 29070), asciidoc-helper.sh (29926), + fuzz_multi.sh (30077), fuzz_static_testcases.sh (ticket 29059), + nagios-check-tor-authority-cert (ticket 29071), + src/test/fuzz/fixup_filenames.sh (ticket 30078), test-network.sh + (ticket 29060), test_key_expiration.sh (ticket 30002), + zero_length_keys.sh (ticket 29068), and test_workqueue_*.sh + (ticket 29067). + + o Testing (chutney): + - In "make test-network-all", test IPv6-only v3 single onion + services, using the chutney network single-onion-v23-ipv6-md. + Closes ticket 27251. + + o Testing (continuous integration): + - In Travis, make stem log a controller trace to the console, and tail + stem's tor log after failure. Closes ticket 30591. + - In Travis, only run the stem tests that use a tor binary. + Closes ticket 30694. + + Changes in version 0.4.0.5 - 2019-05-02 This is the first stable release in the 0.4.0.x series. It contains improvements for power management and bootstrap reporting, as well as diff --git a/changes/bug22619 b/changes/bug22619 deleted file mode 100644 index 9c71996f5b..0000000000 --- a/changes/bug22619 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (circuit isolation): - - Fix a logic error that prevented the SessionGroup sub-option from - being accepted. Fixes bug 22619; bugfix on 0.2.7.2-alpha. diff --git a/changes/bug29034 b/changes/bug29034 deleted file mode 100644 index e7aa9af00b..0000000000 --- a/changes/bug29034 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (Onion service reachability): - - Properly clean up the introduction point map when circuits change purpose - from onion service circuits to pathbias, measurement, or other circuit types. - This should fix some service-side instances of introduction point failure. - Fixes bug 29034; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug30455 b/changes/bug30455 new file mode 100644 index 0000000000..aecbde5a33 --- /dev/null +++ b/changes/bug30455 @@ -0,0 +1,5 @@ + o Minor bugfixes (chutney, makefiles, documentation): + - "make test-network-all" shows the warnings from each test-network.sh + run on the console, so developers see new warnings early. Improve the + documentation for this feature, and rename a Makefile variable so the + code is self-documenting. Fixes bug 30455; bugfix on 0.3.0.4-rc. diff --git a/changes/bug30713 b/changes/bug30713 deleted file mode 100644 index e00b98da65..0000000000 --- a/changes/bug30713 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (testing): - - Skip test_rebind when the TOR_SKIP_TEST_REBIND environmental variable is - set. Fixes bug 30713; bugfix on 0.3.5.1-alpha. - - Skip test_rebind on macOS in Travis, because it is unreliable on - macOS on Travis. Fixes bug 30713; bugfix on 0.3.5.1-alpha. diff --git a/changes/bug30721 b/changes/bug30721 new file mode 100644 index 0000000000..5ea4a14625 --- /dev/null +++ b/changes/bug30721 @@ -0,0 +1,10 @@ + o Minor bugfixes (networking, IP addresses): + - When parsing addreses via Tor's internal DNS lookup API, reject IPv4 + addresses in square brackets, and accept IPv6 addresses in square + brackets. This change completes the work started in 23082, making + address parsing consistent between tor's internal DNS lookup and address + parsing APIs. Fixes bug 30721; bugfix on 0.2.1.5-alpha. + - When parsing addreses via Tor's internal address:port parsing and + DNS lookup APIs, require IPv6 addresses with ports to have square + brackets. But allow IPv6 addresses without ports, whether or not they + have square brackets. Fixes bug 30721; bugfix on 0.2.1.5-alpha. diff --git a/changes/bug30744 b/changes/bug30744 deleted file mode 100644 index 9f07d4855f..0000000000 --- a/changes/bug30744 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (continuous integration): - - Allow the test-stem job to fail in Travis, because it sometimes hangs. - Fixes bug 30744; bugfix on 0.3.5.4-alpha. diff --git a/changes/bug30780 b/changes/bug30780 new file mode 100644 index 0000000000..5731d201a2 --- /dev/null +++ b/changes/bug30780 @@ -0,0 +1,3 @@ + o Minor bugfixes (directory authorities): + - Return a distinct status when formatting annotations fails. + Fixes bug 30780; bugfix on 0.2.0.8-alpha. diff --git a/changes/bug30781 b/changes/bug30781 deleted file mode 100644 index 7c7adf470e..0000000000 --- a/changes/bug30781 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (directory authorities): - - Stop crashing after parsing an unknown descriptor purpose annotation. - We think this bug can only be triggered by modifying a local file. - Fixes bug 30781; bugfix on 0.2.0.8-alpha. diff --git a/changes/bug30799 b/changes/bug30799 new file mode 100644 index 0000000000..b10420a953 --- /dev/null +++ b/changes/bug30799 @@ -0,0 +1,4 @@ + o Minor bugfixes (memory management): + - Stop leaking a small amount of memory in nt_service_install(), in + unreachable code. Fixes bug 30799; bugfix on 0.2.0.7-alpha. + Patch by Xiaoyin Liu. diff --git a/changes/bug30804 b/changes/bug30804 new file mode 100644 index 0000000000..ba4a3e8b8c --- /dev/null +++ b/changes/bug30804 @@ -0,0 +1,4 @@ + o Minor bugfixes (testing): + - Teach the util/socketpair_ersatz test to work correctly when we + have no network stack configured. Fixes bug 30804; bugfix on + 0.2.5.1-alpha. diff --git a/changes/bug30840 b/changes/bug30840 new file mode 100644 index 0000000000..562b0fbd93 --- /dev/null +++ b/changes/bug30840 @@ -0,0 +1,4 @@ + o Minor bugfixes (git scripts): + - Stop hard-coding the bash path in the git scripts. Some OSes don't + have bash in /usr/bin, others have an ancient bash at this path. + Fixes bug 30840; bugfix on 0.4.0.1-alpha. diff --git a/changes/bug30841 b/changes/bug30841 new file mode 100644 index 0000000000..c6d1c51469 --- /dev/null +++ b/changes/bug30841 @@ -0,0 +1,3 @@ + o Minor bugfixes (git scripts): + - Stop hard-coding the tor master branch name and worktree path in the + git scripts. Fixes bug 30841; bugfix on 0.4.0.1-alpha. diff --git a/changes/bug30956 b/changes/bug30956 deleted file mode 100644 index 8f52a81de3..0000000000 --- a/changes/bug30956 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (pluggable transports): - - Always publish bridge pluggable transport information in the extra info - descriptor, even if ExtraInfoStatistics is 0. This information is - needed by BridgeDB. Fixes bug 30956; bugfix on 0.4.1.1-alpha. diff --git a/changes/bug30958 b/changes/bug30958 new file mode 100644 index 0000000000..374c8e46f7 --- /dev/null +++ b/changes/bug30958 @@ -0,0 +1,5 @@ + o Minor bugfixes (statistics): + - Stop removing the ed25519 signature if the extra info file is too big. + If the signature data was removed, but the keyword was kept, this could + result in an unparseable extra info file. Fixes bug 30958; + bugfix on 0.2.7.2-alpha. diff --git a/changes/bug31040 b/changes/bug31040 new file mode 100644 index 0000000000..81f6d7e795 --- /dev/null +++ b/changes/bug31040 @@ -0,0 +1,3 @@ + o Minor bugfixes (developer tooling): + - Only log git script changes in post-merge script when merge was to the + master branch. Fixes bug 31040; bugfix on 0.4.1.1-alpha. diff --git a/changes/bug31112 b/changes/bug31112 new file mode 100644 index 0000000000..882efaad59 --- /dev/null +++ b/changes/bug31112 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Various simplifications and minor improvements to the circuit padding + machines. Patch by Tobias Pulls. Closes tickets 31112 and 31098. diff --git a/changes/bug31113 b/changes/bug31113 new file mode 100644 index 0000000000..f48328f0fe --- /dev/null +++ b/changes/bug31113 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Improve documentation in circuit padding subsystem. Patch by Tobias + Pulls. Closes ticket 31113. diff --git a/changes/doc30630 b/changes/doc30630 deleted file mode 100644 index 0fbd8d4dd4..0000000000 --- a/changes/doc30630 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation: - - Mention URLs for Travis/Appveyor/Jenkins in ReleasingTor.md. Closes - ticket 30630. diff --git a/changes/doc31089 b/changes/doc31089 new file mode 100644 index 0000000000..2fc0ba4f7d --- /dev/null +++ b/changes/doc31089 @@ -0,0 +1,4 @@ + o Documentation: + - Use RFC 2397 data URL scheme to embed image into tor-exit-notice.html + so that operators would no longer have to host it themselves. + Closes ticket 31089. diff --git a/changes/geoip-2019-06-10 b/changes/geoip-2019-06-10 deleted file mode 100644 index 2d1e065649..0000000000 --- a/changes/geoip-2019-06-10 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the June 10 2019 Maxmind GeoLite2 - Country database. Closes ticket 30852. - diff --git a/changes/ticket19381 b/changes/ticket19381 new file mode 100644 index 0000000000..ee51e2a3e2 --- /dev/null +++ b/changes/ticket19381 @@ -0,0 +1,4 @@ + o Minor features (build system): + - Add --disable-manpage and --disable-html-manual options to configure + script. This will enable shortening build times by not building + documentation. Resolves issue 19381. diff --git a/changes/ticket24963 b/changes/ticket24963 new file mode 100644 index 0000000000..50adcfaaf4 --- /dev/null +++ b/changes/ticket24963 @@ -0,0 +1,5 @@ + o Minor feature (onion service): + - Disallow single hop clients to introduce directly at the introduction + point. We've removed Tor2web a while back and rendezvous are blocked at + the relays. This is to remove load off the network from spammy clients. + Close ticket 24963. diff --git a/changes/ticket24964 b/changes/ticket24964 new file mode 100644 index 0000000000..171c86eb1d --- /dev/null +++ b/changes/ticket24964 @@ -0,0 +1,4 @@ + o Minor feature (onion service v3): + - Do not allow single hop client to fetch or post an HS descriptor from an + HSDir. Closes ticket 24964; + diff --git a/changes/ticket27530 b/changes/ticket27530 new file mode 100644 index 0000000000..8ae4f52668 --- /dev/null +++ b/changes/ticket27530 @@ -0,0 +1,4 @@ + o Minor features (compilation): + - Log a more useful error message when we are compiling and one of the + compile-time hardening options we have selected can be linked but + not executed. Closes ticket 27530. diff --git a/changes/ticket29533 b/changes/ticket29533 new file mode 100644 index 0000000000..27ef681218 --- /dev/null +++ b/changes/ticket29533 @@ -0,0 +1,3 @@ + o Testing: + - Run shellcheck for all non-third-party shell scripts that are shipped + with Tor. Closes ticket 29533. diff --git a/changes/ticket29738 b/changes/ticket29738 new file mode 100644 index 0000000000..9217cc9a5f --- /dev/null +++ b/changes/ticket29738 @@ -0,0 +1,6 @@ + o Minor features (recommended packages): + - No longer include recommended packages in votes as detailed in proposal + 301. The RecommendedPackages torrc option is deprecated and will no + longer have any effect. "package" lines will still be considered when + computing consensuses for consensus methods that include them. Fixes + ticket 29738. diff --git a/changes/ticket29746 b/changes/ticket29746 new file mode 100644 index 0000000000..63b9edb391 --- /dev/null +++ b/changes/ticket29746 @@ -0,0 +1,4 @@ + o Minor bugfixes (best practices tracker): + - Fix a few issues in the best-practices script, including tests, tab + tolerance, error reporting, and directory-exclusion logic. Fixes bug + 29746; bugfix on 0.4.1.1-alpha. diff --git a/changes/ticket29976 b/changes/ticket29976 new file mode 100644 index 0000000000..9991bfb1ff --- /dev/null +++ b/changes/ticket29976 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Rework bootstrap tracking to use the new publish-subscribe + subsystem. Closes ticket 29976. diff --git a/changes/ticket30102 b/changes/ticket30102 new file mode 100644 index 0000000000..c8b1148da3 --- /dev/null +++ b/changes/ticket30102 @@ -0,0 +1,4 @@ + o Minor features (continuous integration): + - When running CI builds on Travis, put some random data in ~/.torrc, + to make sure no tests are dependent on default Tor configuration. + Resolves issue 30102. diff --git a/changes/ticket30550 b/changes/ticket30550 new file mode 100644 index 0000000000..f356c4048e --- /dev/null +++ b/changes/ticket30550 @@ -0,0 +1,2 @@ + o Removed features: + - Remove torctl.in from contrib/dist directory. Resolves ticket 30550. diff --git a/changes/ticket30686 b/changes/ticket30686 deleted file mode 100644 index 36473c1a02..0000000000 --- a/changes/ticket30686 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor features (logging): - - Give a more useful assertion failure message if we think we have - minherit() but we fail to make a region non-inheritable. Give a - compile-time warning if our support for minherit() is - incomplete. Closes ticket 30686. diff --git a/changes/ticket30687 b/changes/ticket30687 new file mode 100644 index 0000000000..c3124eb64b --- /dev/null +++ b/changes/ticket30687 @@ -0,0 +1,3 @@ + o Minor feature (token bucket): + - Implement a generic token bucket that uses a single counter. This will be + useful for the anti-DoS onion service work. Closes ticket 30687. diff --git a/changes/ticket30752 b/changes/ticket30752 new file mode 100644 index 0000000000..044c7c7d93 --- /dev/null +++ b/changes/ticket30752 @@ -0,0 +1,6 @@ + o Minor features (best practices tracker): + - Give a warning rather than an error when a practracker exception is + violated by a small amount; add a --list-overbroad option to + practracker that lists exceptions that are stricter than they need to + be, and provide an environment variable for disabling + practracker. Closes ticekt 30752. diff --git a/changes/ticket30769 b/changes/ticket30769 new file mode 100644 index 0000000000..74f63a1465 --- /dev/null +++ b/changes/ticket30769 @@ -0,0 +1,4 @@ + o Minor bugfixes (sendme, code structure): + - Rename the trunnel SENDME file definition from sendme.trunnel to + sendme_cell.trunnel to avoid having twice sendme.{c|h} in the repository. + Fixes bug 30769; bugfix on 0.4.1.1-alpha. diff --git a/changes/ticket30806 b/changes/ticket30806 new file mode 100644 index 0000000000..4f09ea2af3 --- /dev/null +++ b/changes/ticket30806 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Use the subsystems mechanism to manage the main event loop code. + Closes ticket 30806. diff --git a/changes/ticket30864 b/changes/ticket30864 new file mode 100644 index 0000000000..b8fb571300 --- /dev/null +++ b/changes/ticket30864 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Extract our variable manipulation code from confparse.c to a new + lower-level typedvar.h module. Closes ticket 30864. diff --git a/changes/ticket30889 b/changes/ticket30889 new file mode 100644 index 0000000000..8582e2bcac --- /dev/null +++ b/changes/ticket30889 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Eliminate some uses of lower-level control reply abstractions, + primarily in the onion_helper functions. Closes ticket 30889. diff --git a/changes/ticket30893 b/changes/ticket30893 new file mode 100644 index 0000000000..638b99a9f7 --- /dev/null +++ b/changes/ticket30893 @@ -0,0 +1,3 @@ + o Minor features (testing): + - Improve test coverage for our existing configuration parsing and + management API. Closes ticket 30893. diff --git a/changes/ticket30921 b/changes/ticket30921 new file mode 100644 index 0000000000..50ec570ffe --- /dev/null +++ b/changes/ticket30921 @@ -0,0 +1,5 @@ + o Minor bugfixes (onion service v3): + - When purging the client descriptor cache, always also close any + introduction point circuits associated with it. This avoids picking those + when connecting to them later while not having the descriptor to complete + the introduction. Fixes bug 30921; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket30955 b/changes/ticket30955 new file mode 100644 index 0000000000..7715a07569 --- /dev/null +++ b/changes/ticket30955 @@ -0,0 +1,3 @@ + o Documentation (hard-coded directories): + - Improve the documentation for the DirAuthority and FallbackDir torrc + options. Closes ticket 30955. diff --git a/changes/ticket30956_refactor b/changes/ticket30956_refactor new file mode 100644 index 0000000000..81151c6cc9 --- /dev/null +++ b/changes/ticket30956_refactor @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Split extrainfo_dump_to_string() into smaller functions. + Closes ticket 30956. diff --git a/changes/ticket30979 b/changes/ticket30979 new file mode 100644 index 0000000000..8ae9b3c418 --- /dev/null +++ b/changes/ticket30979 @@ -0,0 +1,5 @@ + o Minor features (git hooks): + - Our pre-push git hook now checks for a special file + before running practracker, so that it only runs on branches + that are based on master. + Closes ticket 30979. diff --git a/changes/ticket31008 b/changes/ticket31008 new file mode 100644 index 0000000000..c7077de6c6 --- /dev/null +++ b/changes/ticket31008 @@ -0,0 +1,3 @@ + o Documentation (tor.1 man page): + - Fix typo -help to --help in tor.1 man page. Fixes bug 31008; bugfix on + 0.2.2.9-alpha. diff --git a/changes/ticket31012 b/changes/ticket31012 new file mode 100644 index 0000000000..61ea30d8da --- /dev/null +++ b/changes/ticket31012 @@ -0,0 +1,4 @@ + o Minor bugfixes (operator tools): + - Make tor-print-ed-signing-cert(1) print certificate expiration date in + RFC 1123 and UNIX timestamp formats, to make output machine readable. + Fixes bug 31012; bugfix on 0.3.5.1-alpha. diff --git a/changes/ticket31025 b/changes/ticket31025 new file mode 100644 index 0000000000..c572288239 --- /dev/null +++ b/changes/ticket31025 @@ -0,0 +1,5 @@ + o Minor bugfixes (coverity): + - In our siphash implementation, when building for coverity, use memcpy + in place of a switch statement, so that coverity can tell we are not + accessing out-of-bounds memory. Fixes bug 31025; bugfix on + 0.2.8.1-alpha. This is tracked as CID 1447293 and 1447295. diff --git a/changes/ticket31026 b/changes/ticket31026 new file mode 100644 index 0000000000..6f6abcffba --- /dev/null +++ b/changes/ticket31026 @@ -0,0 +1,5 @@ + o Minor bugfixes (coverity compliance): + - Add an assertion when parsing a BEGIN cell so that coverity can be sure + that we are not about to dereference a NULL address. + Fixes bug 31026; bugfix on 0.2.4.7-alpha. This is CID + 1447296. diff --git a/changes/ticket31030 b/changes/ticket31030 new file mode 100644 index 0000000000..4d99323b4e --- /dev/null +++ b/changes/ticket31030 @@ -0,0 +1,3 @@ + o Minor bugfixes (coverity, tests): + - Fix several coverity warnings from our unit tests. Fixes bug 31030; + bugfix on 0.2.4.1-alpha, 0.3.2.1-alpha, and 0.4.0.1-alpha. diff --git a/changes/ticket31175 b/changes/ticket31175 new file mode 100644 index 0000000000..cff13761a4 --- /dev/null +++ b/changes/ticket31175 @@ -0,0 +1,3 @@ + o Minor features (development tools): + - Our best-practices tracker now looks at headers as well as + C files. Closes ticket 31175. diff --git a/changes/ticket31304 b/changes/ticket31304 new file mode 100644 index 0000000000..ca60148b0c --- /dev/null +++ b/changes/ticket31304 @@ -0,0 +1,3 @@ + o Minor features (tests): + - The practracker tests are now run as part of the Tor test suite. + Closes ticket 31304. diff --git a/changes/ticket31309 b/changes/ticket31309 new file mode 100644 index 0000000000..8e1c9f27ea --- /dev/null +++ b/changes/ticket31309 @@ -0,0 +1,4 @@ + o Minor features (best practices tracker): + - Add a TOR_PRACTRACKER_OPTIONS variable for passing arguments + to practracker from the environment. We may want this for + continuous integration. Closes ticket 31309. diff --git a/changes/ticket31320 b/changes/ticket31320 new file mode 100644 index 0000000000..07847e5624 --- /dev/null +++ b/changes/ticket31320 @@ -0,0 +1,3 @@ + o Documentation: + - Include an example usage for IPv6 ORPort in our sample torrc. + Closes ticket 31320; patch from Ali Raheem. diff --git a/configure.ac b/configure.ac index 2295a9131d..9ec123f51e 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2019, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.4.1.5-dev]) +AC_INIT([tor],[0.4.2.0-alpha-dev]) AC_CONFIG_SRCDIR([src/app/main/tor_main.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -14,7 +14,7 @@ AC_CONFIG_MACRO_DIR([m4]) # version number changes. Tor uses it to make sure that it # only shuts down for missing "required protocols" when those protocols # are listed as required by a consensus after this date. -AC_DEFINE(APPROX_RELEASE_DATE, ["2019-08-20"], # for 0.4.1.5-dev +AC_DEFINE(APPROX_RELEASE_DATE, ["2019-06-10"], # for 0.4.2.0-alpha-dev [Approximate date when this software was released. (Updated when the version changes.)]) # "foreign" means we don't follow GNU package layout standards @@ -105,6 +105,12 @@ if test "$enable_memory_sentinels" = "no"; then [Defined if we're turning off memory safety code to look for bugs]) fi +AC_ARG_ENABLE(manpage, + AS_HELP_STRING(--disable-manpage, [Disable manpage generation.])) + +AC_ARG_ENABLE(html-manual, + AS_HELP_STRING(--disable-html-manual, [Disable HTML documentation.])) + AC_ARG_ENABLE(asciidoc, AS_HELP_STRING(--disable-asciidoc, [don't use asciidoc (disables building of manpages)]), [case "${enableval}" in @@ -299,6 +305,8 @@ AC_PATH_PROG([ASCIIDOC], [asciidoc], none) AC_PATH_PROGS([A2X], [a2x a2x.py], none) AM_CONDITIONAL(USE_ASCIIDOC, test "x$asciidoc" = "xtrue") +AM_CONDITIONAL(BUILD_MANPAGE, [test "x$enable_manpage" != "xno"]) +AM_CONDITIONAL(BUILD_HTML_DOCS, [test "x$enable_html_manual" != "xno"]) AM_PROG_CC_C_O AC_PROG_CC_C99 @@ -1188,6 +1196,17 @@ m4_ifdef([AS_VAR_IF],[ TOR_CHECK_LDFLAGS(-pie, "$all_ldflags_for_check", "$all_libs_for_check") fi TOR_TRY_COMPILE_WITH_CFLAGS(-fwrapv, also_link, CFLAGS_FWRAPV="-fwrapv", true) + + AC_MSG_CHECKING([whether we can run hardened binaries]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([dnl + We can link with compiler hardening options, but we can't run with them. + That's a bad sign! If you must, you can pass --disable-gcc-hardening to + configure, but it would be better to figure out what the underlying problem + is.])], + [AC_MSG_RESULT([cross])]) fi if test "$fragile_hardening" = "yes"; then @@ -2458,7 +2477,6 @@ AC_CONFIG_FILES([ Makefile config.rust contrib/operator-tools/tor.logrotate - contrib/dist/torctl contrib/dist/tor.service src/config/torrc.sample src/config/torrc.minimal diff --git a/contrib/dist/torctl.in b/contrib/dist/torctl.in deleted file mode 100644 index 4cc137da46..0000000000 --- a/contrib/dist/torctl.in +++ /dev/null @@ -1,195 +0,0 @@ -#!/bin/sh -# -# TOR control script designed to allow an easy command line interface -# to controlling The Onion Router -# -# The exit codes returned are: -# 0 - operation completed successfully. For "status", tor running. -# 1 - For "status", tor not running. -# 2 - Command not supported -# 3 - Could not be started or reloaded -# 4 - Could not be stopped -# 5 - -# 6 - -# 7 - -# 8 - -# -# When multiple arguments are given, only the error from the _last_ -# one is reported. -# -# -# |||||||||||||||||||| START CONFIGURATION SECTION |||||||||||||||||||| -# -------------------- -------------------- -# Name of the executable -EXEC=tor -# -# the path to your binary, including options if necessary -TORBIN="@BINDIR@/$EXEC" -# -# the path to the configuration file -TORCONF="@CONFDIR@/torrc" -# -# the path to your PID file -PIDFILE="@LOCALSTATEDIR@/run/tor/tor.pid" -# -# The path to the log file -LOGFILE="@LOCALSTATEDIR@/log/tor/tor.log" -# -# The path to the datadirectory -TORDATA="@LOCALSTATEDIR@/lib/tor" -# -TORARGS="--pidfile $PIDFILE --log \"notice file $LOGFILE\" --runasdaemon 1" -TORARGS="$TORARGS --datadirectory $TORDATA" - -# If user name is set in the environment, then use it; -# otherwise run as the invoking user (or whatever user the config -# file says)... unless the invoking user is root. The idea here is to -# let an unprivileged user run tor for her own use using this script, -# while still providing for it to be used as a system daemon. -if [ "x`id -u`" = "x0" ]; then - TORUSER=@TORUSER@ -fi - -if [ "x$TORUSER" != "x" ]; then - TORARGS="$TORARGS --user $TORUSER" -fi - -# We no longer wrap the Tor daemon startup in an su when running as -# root, because it's too painful to make the use of su portable. -# Just let the daemon set the UID and GID. -START="$TORBIN -f $TORCONF $TORARGS" - -# -# -------------------- -------------------- -# |||||||||||||||||||| END CONFIGURATION SECTION |||||||||||||||||||| - -ERROR=0 -ARGV="$@" -if [ "x$ARGV" = "x" ] ; then - ARGS="help" -fi - -checkIfRunning ( ) { - # check for pidfile - PID=unknown - if [ -f $PIDFILE ] ; then - PID=`/bin/cat $PIDFILE` - if [ "x$PID" != "x" ] ; then - if kill -0 $PID 2>/dev/null ; then - STATUS="$EXEC (pid $PID) running" - RUNNING=1 - else - STATUS="PID file ($PIDFILE) present, but $EXEC ($PID) not running" - RUNNING=0 - fi - else - STATUS="$EXEC (pid $PID?) not running" - RUNNING=0 - fi - else - STATUS="$EXEC apparently not running (no pid file)" - RUNNING=0 - fi - return -} - -for ARG in $@ $ARGS -do - checkIfRunning - - case $ARG in - start) - if [ $RUNNING -eq 1 ]; then - echo "$0 $ARG: $EXEC (pid $PID) already running" - continue - fi - if eval "$START" ; then - echo "$0 $ARG: $EXEC started" - # Make sure it stayed up! - /bin/sleep 1 - checkIfRunning - if [ $RUNNING -eq 0 ]; then - echo "$0 $ARG: $EXEC (pid $PID) quit unexpectedly" - fi - else - echo "$0 $ARG: $EXEC could not be started" - ERROR=3 - fi - ;; - stop) - if [ $RUNNING -eq 0 ]; then - echo "$0 $ARG: $STATUS" - continue - fi - if kill -15 $PID ; then - echo "$0 $ARG: $EXEC stopped" - else - /bin/sleep 1 - if kill -9 $PID ; then - echo "$0 $ARG: $EXEC stopped" - else - echo "$0 $ARG: $EXEC could not be stopped" - ERROR=4 - fi - fi - # Make sure it really died! - /bin/sleep 1 - checkIfRunning - if [ $RUNNING -eq 1 ]; then - echo "$0 $ARG: $EXEC (pid $PID) unexpectedly still running" - ERROR=4 - fi - ;; - restart) - $0 stop start - ;; - reload) - if [ $RUNNING -eq 0 ]; then - echo "$0 $ARG: $STATUS" - continue - fi - if kill -1 $PID; then - /bin/sleep 1 - echo "$EXEC (PID $PID) reloaded" - else - echo "Can't reload $EXEC" - ERROR=3 - fi - ;; - status) - echo $STATUS - if [ $RUNNING -eq 1 ]; then - ERROR=0 - else - ERROR=1 - fi - ;; - log) - cat $LOGFILE - ;; - help) - echo "usage: $0 (start|stop|restart|status|help)" - /bin/cat <<EOF - -start - start $EXEC -stop - stop $EXEC -restart - stop and restart $EXEC if running or start if not running -reload - cause the running process to reinitialize itself -status - tell whether $EXEC is running or not -log - display the contents of the log file -help - this text - -EOF - ERROR=0 - ;; - *) - $0 help - ERROR=2 - ;; - - esac - -done - -exit $ERROR - diff --git a/contrib/include.am b/contrib/include.am index 8dd8593304..784f5427b8 100644 --- a/contrib/include.am +++ b/contrib/include.am @@ -3,7 +3,6 @@ EXTRA_DIST+= \ contrib/README \ contrib/client-tools/torify \ contrib/dist/rc.subr \ - contrib/dist/torctl \ contrib/dist/tor.service.in \ contrib/operator-tools/tor-exit-notice.html \ contrib/or-tools/exitlist \ diff --git a/contrib/operator-tools/tor-exit-notice.html b/contrib/operator-tools/tor-exit-notice.html index 8cf5c294f2..f0f9a6344c 100644 --- a/contrib/operator-tools/tor-exit-notice.html +++ b/contrib/operator-tools/tor-exit-notice.html @@ -37,13 +37,180 @@ privacy</a> to people who need it most: average computer users. This router IP should be generating no other traffic, unless it has been compromised.</p> - -<!-- FIXME: you should probably grab your own copy of how_tor_works_thumb.png - and serve it locally --> - <p style="text-align:center"> <a href="https://www.torproject.org/about/overview"> -<img src="https://www.torproject.org/images/how_tor_works_thumb.png" alt="How Tor works" style="border-style:none"/> +<img src="data:image/png;base64, +iVBORw0KGgoAAAANSUhEUgAAAQQAAACQCAMAAADZVuXZAAAABGdBTUEAANbY1E9Y +MgAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGAUExURej4 +6Zycm+Hh4XZ2duTk5LS0tfP79OLc3P/r/6iopv///2ZmZgMDAw8PD/b29v39/cTE +whUVFS8vL/Ly8by8uvv7+vn5+CUlJcXCm+/v7xsbGuzs7E5OTq2trASbBVpaWszM +yaKipDk5ONra2iAhH8/OrX9/fPf35vv77j4+Pvj2zPz82Onp6PPz3t7e3ra2n66s +if7+9b+/v+3t7efn529vb9DQ0GNjY4iIiLi9xtfX15aWldTU1Ofmyy6wMZCQjtkG +Bufl4ZTcmYiLjujo4dbVwmppZmvLcd3czoaHhaSstsjIyMTL07Pmt9ny2sjty/// +/EVBPe/x05WcpZGRkVKzV+Pj3vDuw9vasqOfet/extbd5uPivOVXVvr9+dHY35WU +hefm1IjJi8zR2e+bm/vg4C5PMvfFxf3w8DlyQP/4+Nvj6/77/IuTnZ7KolF8Vv39 +/+bi5v/9/HvCgCU7KLm5uMraylZfa3CZdf3//P3//7TQtu3k7ZwICPn9/eLu7gnB +e20AAB3ZSURBVHja7F2JQ9rY1hdCOh/33YRAQhBDIAGJbLJYwAUqFhfUulu1taO1 ++zZdpp193ve9f/0754ZNwG4683yvHjAJycnJvb977pb8chz6x5X8Y+gf5JuXKxCu +QPgYCE5KiEBGs980CKGACSBYRA4ZhA/5v0kQPJRQIsijQZql8qhMv0kQqtRLNSoG +IPtZ3huZ/CZBoH5SclN11BX1gU9Eole9w1UX+a2CIN/cvwny4/6PN9maLW+ep4d4 +908wsG+bBbu4BYvvLjEI/iJRNU3LZBZuOrP7rn2X62Yms0XkgWcfb9Rnw+HwLC7C +jc1rg6+hfrclawto0rWPcnNhYUHbWvD09kn/XECwFopwHDf29xdgq6/ngn37CwzP +hYXW2nPBIIjlLXeJs1a4pclIKBn1BUa9At0vZPpP3WiEw3sPN1o/r/3ZmA3vbW70 +K8rlLQ4sWmAyFEr6mEkv2Vd71PzZrZlyWVJ0t6NScZhuXpHk93JP/jxBv1g2JIXX +Taalg5a4JV80CNKWOblUKiEG2y0QjC1nT2J+b4Rn62qfufW9cPjhcc9OTSIlsFha +igCszOQIgLAs9mFFSgAWV2ppeQV3QevRUrOJKpQRx01GIskoaI14qVEoei4WBFkq +nEQikUlMsA0CXGa44Dp1mYez4fr/nWFRrYdnT9eLjLQ1GZkEiYT29p76ApBwQRgA +gkRAZ4mhb19XqCUWegpZrSaUphL6KQNB8jv9F+0JiZMQoBBCP7DT4qW5hKsbgvDe +xkeNboTD3QpavBBBkwBrvfE0wLInbPWBEJQKTAsujblDLasPBLE8lm8qQc1CWwJV +Cln/RXtCYiW5HdreDjEMWKlRPbHf8fjw3qfNXpsNb3ZAMAohNPg06Vtfj0C6R4SB +ICiJ7RATLGIEX+DGHvSDULO1oMoEsM5AEY1lL9wT0itRqAgIAfM2SDB1256wgIW8 +93mGxb02DAhCEuRtfX1zY3O97kVcC4e9IGhKArVYg4AXBq3SIBBWmNK2jRTgCX6a +fXfRnpC2fAwAXzMpVKD5tBMONcK/z4Y3Pts084bGQ2wY02Av6gMQNoY2AARhIAhB +KRH1Rdvgo9ZS+oHYB8IJJC+KWuCmUEaU5tIXXx3SXACTAfWNuSRtgQANf/jOFxkP +4vjhAzSM6SiY8z19GtrY+O3FU8SAJgaDYGOPXo4XLo33gzBuNXVYc4VO5R77C0Cw +RgPsEiN2XQDRx5zgBzAq+lLzdRxEBaU0phlRXV9/ITCbwkAQmmrtC58FAmqNtDD4 +CzwB2gRrZGSUQWBfg+JlXA0cGM5e+0LzOJIM7wEIIyPMYL0OfkDR6CAQ0lDJ8cre +Fvjczg25HwSWuK7k/RXVIWV5mX1MSROE4ZTzj6++xLVflHTAa0vbpJA+/KUXhHg6 +wLLW0bJSN3o9oTp+FzRsnGw4aW784scJqRVMRxcEAMK48xzX+J942id0MLCNpu/s +rfdXh9aFm9dd2ekHIZUXOqaYopL6C9qEu1hnGcqtxNxJuc5xDS2e8qG9Vsk1PeFl +uGssgSAMpwNCN/SU5lPf91WHHUloS7OI/gIQUgcH36PcYcI2D1I/ngeEpsk7TZPM +ZuqOCH3H7EY3CKAUH8477p6crJzczQ/H7xwcHPZ5wsHBnTvDw3lbycwN3/n+4ODC +q4ORy+cd+eE8yDCTu458XsqcY4oSVMBkPgcpH0aTYBcs5iRxExrNh93VYTiHx+PD +TcnlhnPxXhBiCuyOd5SGwbDkumAQ/K41Xnc8WF5cNIxFWCzu53leqZ5nsirHJN59 +iCYXbZMLJs8bLhX6jtnwqfzp7mW4bBzEiP+i6LqixHpnkUVJdytVOC7F45JhVHNu +ni9rFzyLJKLmqubu5ttA35WcRU0+D9R+OeMs37170jFZdbk00UMam6TeGXp4ZFes +nMsjBPH1eDyfm3G6ir33E4gItnK5eEvyejXr1NSLvr3mf6cWlUQhkYC/QgHWEiBw +PqQ9fjE7hsZsSRSWVX/L4my9cxsus18eHxtLJMaYLH9X1Po7Zo9YXF4ea0p6bCw+ +UxTPeWNpAAg3dVZc8XhrCRUwN36uiyy2/KpplFX4VqbaFcIjLaoJURxjXybqsNKH +gaokEqDAVGy9Yz540SCoZUX647jwQ8H/g7+AC/8PJKcsn+caIq/s+9GS/7i1Cip8 +q4I1HnbUFn+HS6KrFPxqQS0oCt/bFoGXKgUVTKjHqqqiX23xynf+CwZBlqZ5RR1j +ONsfNcfz1fNcJujmpw9F8F40iiKO6zzfavE2Wq2CDE2cMTYOFQL+ZFDjQbReQGd4 +PgdHZVAbAzU5AambUS8cBF3Xc+nx9Dh+QdJuXXdXz1PtNLCgL47bglk8dLtXc+1m +v1Ufgrrpdg8bceweFrHNM0H6QKiabjOPKobdMjpAqSxe+N1mR61SqdUqJzWQE7as +6K7zgBBUKjVmDFYnTcMOpe3o4ePWjVa3A2S3An+4cFRMReqt7mqWNysVplbZhQ9u +5mIX7Qmqy1BgYDCtoPBsqUjV4HlAEGOS0hKet+1K2XbpzQ61qnvVMNbgUzYMo1w2 +JKkcc/WWsV+bwcNdsmbMaBfdJnhELVMsFjOZTDHDlvgjeK6rwDihyMwwi0VmU+sM +AFpDZ48qBmU5CH9MgrAlqp7+3rZ5EA7jB76i/6J7B7wQfD3wZX8ee8c5xWMb8dgG +PZ5um3sb5N8sl+CB7OzmJQFhW6CKbnZ2U+qzqzMlxOsR2rvZchKmryNdNhxmtF11 +ff/BIFCn5vYKJCCQaAVyKkcILS7B/uTkJBHICEnSIBFGYcPOPXUSjsacpVFCdKrX +3BZRqENNUtNHv5zRsXdZQNCitGa6a5Ye8Eo1pOdEIx676CkRVMGxQkIB96RJ9IiO +nRbNOkOEun1MY8R0R8FjvBkv/PgKVsulAYGSMnXr1pJS9gb5CHi1gx2doT5BQRDc +ZIVWFBeJSQGDgTCDILjZmSUAIUj1edn3dSA0LgsIJqWxIlUoNUZ84Pky1PrsNtSG +DJFKAb9ABKpnKM2QIqUaA4EE6IqEjUiEJnf5JFkSRuUoOs6XU90a65eud/D+7Ulo +1K+6SFJ/6Lk8IHg/0cUFBu302d1mkDvH/Gp243KAYBkVYhGjpApkhVQwR3wJK7+0 +QpYssqKYRHFQaB+IanAe7D1MDZRKFZkatAxNg2NELJe+Ng3dd1v/rb2DGVIE12iV +eiejRFirQRFnoeQVqkeyPhFaTL8gQasn0Qx1ey3Ivy80yY06RzI0S3VoSd0+FQ5+ +ZRr2wpelizRNIRYhmpeGoDeETlJYgmERv0ImvaMwCEgWwe8Ni1AYRgWUEtaNbCg6 +Img4kIgESSwEjvC1IGzOqvbcqCXv2lt+z4Ablv3iuZC7zXSEzggijApgTFimSxHi +otuQJ2WFCCvgByQk0ijkN7SdDZEADyD4RqjkrcGhAICgkSzlaejsMYIoiyKb/Yls +i90/7Dr8poGZW+g/b4uovXNpdX+A/a1zTfXbIFhJEiFWNBYiZpCRmZO4lHXCBxQp +SkrqUiRCuKhTq3i4KjQCAYePWD5eKUHWLUiozw0nn4nBuJbJaAuZhQx+2V+m+1GB +Gma3S7Y8pCBrONNGomNQfE/I+0Jv9sQHP5AtFYyBEnw1DdU853ok0KkOXyhfMlHy +Z4lYNQxJUpo3VySj+l7svoWK/BfZOFYiv5aQnBfa9vmQOSgkE8c93EGPtkwik0uc +VVqyaZYjAqWjBU2+ABD+UlGrW2aJs1i6Q01mZGGsm3sozzLuoLIEGJQiSOFjDEUh +mihke0AoLm9FUKs0GYm0KIYjhfGLAcHim3tC2Ujbr5Py/PSnbayHwx9t31Vjq9Li +hyI7D7k4+2Onihim07KUcNtsx+iLFz/ZvLmAlugHoQBT26W2GnK36MWAINEIX6Uc +8VGZCu5i1BJIhS5Be6m7ynSFeKMh7C8rpESz2aWoANVBwt205cx7JDy7sRc+/iMc +Xr9W72F3iVKhxuihkSard0QQlNMgXAvPhqWEI4R8x1C00fgpwKhb3uWx2AAQbCIj +qD21jdFrC/KFtAnbEuQfxj4zIWck5i1HqpEYrUXlpRjMpXE+7SEwZnAmVZxA+4K1 +aUoCLtq6Zz4bPg5vbOxthGc3r4WHwsenuU2ikbA6DEVWxFQf737Yf4wMLyl9EgK1 +7aivvg46o8jDWOwHYTHhCyEtMhmo16NQFZCzM7ZzMSAsSdRdIYYVBRBmKJmswuho +PiqWYJv6KY6ZHSbN+nTLdBMefIEqNc9I+05pfSNM1vfW12fr9fpGYwAIye0k46Ai +Ow9BcJ8CgTxEEMasZDIZ3Wu82dxcf7jHCEnx9CAQQO3p3oc3mxuohjS3sQvxhBr1 +KQ4cJAjKPI04EQQ6CqNCapZXKM4PRj0kMuIlPoGbhg4SR1QC3nFivrCBbUKY/B4O +16+Fw7NDjeNwHwjRpM1QDNi0QMEc//FU7hrh8J9jFraHjfr6hrr5Z4Nx3OLp3jbB +tZgORKPRp3uN9Q0PMkORrtLH+bx8vYNopJF7yOiXNj2P0vx4D/flYRhZpKDx9reH +mxvKh98YHyc+PsATRsDYT3t7HzY33nzYY6Sd8f8IELgmjxFZ26zszIPelwcaStqy +tQLr64EmOW0xFev3BG+bFPm0yVvqozu+83xE3jWfJPSCAG0bm9p//k2eSHNdO3X5 +uR/O8ITR0dPUyNzBqx4QgtAwjjAe40/1+ghlat47qXu9nhBPe9EWWHvYJkXuHHZ7 +gidrmtPTq9MVaXqad+s8f7/6SHr0aFpf5cvGmoR7p/ndVbUfhPrmtQb5sLlO6jiQ +V5Y02c1leTY9LinEsaLAPo9L4edJJSiVVgh1EK5CXJM+nHG/fHxEHj8jTx4fvRwI +QoqzCYreNoMtfqMXBFnaOfEylLxPnzaJaSM7A0EYYXrepz95myy31CnOp8hz3Pz8 +69XXq6u7q6s6vzoNIkm8vgpbPOzc3X3N/fprZ+DeAuF49uFefe9DIzw0O7RORJgZ +a6OS7qvC8NgbY5Posq9KV6I4oTbozChPyyHemqaGgC8GiXPPjp7978u5J3NngGAJ +p4mR9MaTXlIggHBXaPIYW1qBndT9h71tQgqVuric4DCnQPDIqxYHuZ9eW3vkuH/v +3r1Xznuv7r969epeFn7wj9bWpldXX3OlDuWtBcLvs43NjT1oo0k9XCciLVEtSWoj +2yXsCpJ4B1XiiMuSSEgJKRxxV6DXDIVgpJDUGAhk7ujo+Rw5yxNOTvMTKU0f/Njn +Cam80MNj3N45+OdpQrXHFU/1sh1p4LQnyGuO0vz8CnzmYT1fq813pGbBkRVrxbJ2 +Xf7+NoGBsDlbDzcaJEaTNAO1npZgpBiK4E0DKtLRAI6tKQwdRygZodYolQI+qkI3 ++fyIHB0BEHNneUIqlU6l06kuOfhnPwhMDfXSHe0fYRzVxaj2ZOKppgpTZavUwSkQ +VFdZ+qhAuyAZsaDnq3sHB8W7DN3yyUcNfqfuqN0d7shJrWIqvZ2aeN+s1KwurXyt +4nAoQRxHdd2PDiqOSm24W0DNbXQ3zx5VDmrwaQluaRp+g/aCPdLuont9OQgiCZa/ +7BTk5q3l+Nx+FuS7fScvlWPZPtqdP+iMGW6FV3I4287lcooxE4sVxaHwqTeq1Eys +Kin8cA4UUTcnGTMzzuBF3E8Ad8bVs2cDVEL2yiD8qaASS1+Agiq7lIWFmw8ePMDF +gqSJAzgFHlXM5BaXD/FzuLy8bGiiqoJauB4e6gYLjC3OwPFD9l1c/JetdoZUI58N +whx59vL9c+jmXpKXzxFvzeMRg34RHzwqREagQ5I1DY1gEB1PDIpagMjg0VqQBOVP +3dtSh3O6eReJwnnH3Txs5HLD/cn27y9K7hsdWTQWlx/g7ArG5d3VZrGsLN648T18 +mBxKi8sfSUE5QgR8zOyFho56PgrC/6pzR3NzR48fzz2fO3pG/NSiQepz00nsIoPQ +TBIPHYFZBEzj8Tk9hSl3gPdFHSEOmkyf8XEM3sWme/lXC8pib4L8RanvzLhkjyvf +7HXNy6t9aobyMU9QRG434Ao5aTDr+7gnAAhPnhw9noN6MfeEkEkvdpEW9SUBBPak +lUTKJcM5SQNUIjUFXCAQEUYtxUs/fWtONBT+QSIhqgkRliJsLPO80cfXrSq8qqoJ +f6GAK/hsSYpSttVm20NZTZmOb/kTCdBR/QnU3ZJ4Xj7TD5TQUqTi8KluJ4V29uMg +iOAAT46Ons09hnEPkaI61UrEFcCHD0KQgbA0WeKzkZDOBUmWcqPFQC2iR6CDlGHK +6fkECLzOHzYpfAvwt8jzfNnTr8Xr44zn+ByZfmPIUGxj1a4QLtg7LA4hi/H5GKpd +Gwa1szitfkqzppCM+ETTsEao+6MgfEeePH/58smT5+9exp7B+N+Ixd45CXGio8c8 +WVJGbl/R73cRA73RWY6RLJkpk4wkymUS+wQIVSiDCrYIbpR8vlJxVLK958iGCQd3 +dnZuHOwwycFvs2X64WxrxKi7TVNhChsHBzs3mJr7fMTev2MW6c8YNneP8QKn2XY/ +KVCM8W6zR/jOexbh1t1mQzfNU4oOk6/Klx4Ejxq0iXsZmxjIGHx9TZknmJ2pzpyW +bIfHyOZ1iFUx1q+m+S89CC3OXvdyIFZiW5qbXf3/tZYr+LFJVG0Ne62qFx1E4tLK +X/jU9j8HhC9+MfW/EoSNKxDIqQnENwvCVZtwBUJ3F/lNgzA0ewUC2WxcgUDq9SsQ +SP3hFQjkX1/UJjwnzx//F4JwHP58AN7PPSYqgDD3+BKBIAY1kHFcaMFx9ihgPPil +L3TOHn6e3tzc8/b2JQJBHRj/uS8shSfDsNLGM5qG3wWErHOH9pdZe74tD5AOH/B5 +LyRPLgkI8uLWCmdZVmlyMrLdjKiV7I/HKFa3JF5ncRZrK/jY0KHL77tuvrDJgz9T +eE/GZySp9eaq5BTJD+9bd+GO5nqMPnl8SUDQ4qRU4jiuhGEuk3ZAtxGS7QtFaKCa +xS2xQJCM36X7u+IxMlcQY4ll1CqVJm0aGBKgYsetF9qffFUC/xYQpC0MoYg0xqdP +f7Kz5+0HIYjxGJdKNlTRwOioV1g5FYoQJ9NqVZ0BJQ6sMconRusTjILB3OrZ8wFX +fzb3SWj+JhAizRiK9foLO9LigKCUQWmrFY+xyVQVLHFBPD2HClYTixHkfEYiL96+ +tUOdCnzCYJ4wN/Dyzx7PXQYQMB6jTWNcr7+w+WtCoQ8ETQI1DDa5nWzGpBRKY6dA +qM/ONqqJP0N2hMu39Q/NAJd3myAMlqO5T3WVf48nJJClmXzx22+bmw9/22M0k0Eg +sHiMdjhGRvGiS6cIir8j5fP+2PA2WAOdt/U3gs2BOkmU/e/mnr0cePXHcyCXAgTo +E5J2PMbN9cbZIETbkRZHMXsAQjct7Q8E4d6YEgWg9hpv1pHziUH9qDVmqFDeP5wx +eAIQnn0+CJ5PyDvPV71ArzUDLb798GZjY/1Ng5FtBocibIVjbEaCXOohKO6Fw9Vx +BRmRjcb65tDQev0t0nasdPkPLO+zGsCXR3OfB4InaL5WeH668maNnzZ1nS8/qj4y +Hj2CrbVyeQ0JYKv87u7XBO7Q4uloM8jl5nqIUjso5eIAEAIBXzsSJJKRSqmeUISN +sJHKYczMFy/evtn85cWLn9DWStp4Mtep+R55zSivVWOPsrEZfFATe/XqO6fTmYWt +bPZeNluNzTxak6oDmCrqo9Kv3Ovd3Qry3pD8Nz29Zk5L06vIe9tF3tvr+Xlu6Wui +nIEn+EYZkdFbr4eapLN0f4zWeDpgh1rsxGPsBYH8+SY1HLDJnL/V39h40pNU+air +8VN5rvR69/VuydzddczXKq+xAOH7+nWFZxw+E/LC/Sr3c5ZEifu1yXvT1+7fv3fv +1b1X+uY9EPhhPgJXgHPnS9WvCF+iDSd8yD2EzL198RNtxWPsbxPSAZuh2GbnWYNC +EcabMTPfNj40OXz5nftHnQbBI69y3C6SF9ce7WJGXkFWfrnXzMobdOpVJDL2Ufgw +do+jBENbC8ar3AojvdVqnE1+W5nnkPcGK8t0ql9VHXxeoRPn82wQfALjMdKW4kmf +J4jVnTgLSdmKyWjHIix3hdL2BHmL2zUBhbW13UeP7qPE7BX8mmYgrO5y3M1+EPyZ +KpLb1hjBbY19mmS3bpn5mtfOMFov7REhfae/TUiFGAG8FXHSOzI8IDLnk3iLytnG +Kp46NU4QszizYMKegLMPzzaakW1ws6oNoPD55TblTWuy3ewVUuC0JhPuq2K4aHEk +HB4cfH8nHo8Px+98f4A/B4FgExM7kuoH4dHB99tNujwKtDQB3/epcne6WGwcV7FY +dOECtlwgbGX/tDeC6oAu0uO3n/eqamvdJa1dX/X4Vxs+SKXsb5PImTo4SA0AIY6h +Fu/Y4d4ALvglxQcFpYxjrMYcI4KxM1KnPYH4VVFuvYDZeg+zvaO1T/3EYAkagwud +Ss9MrzoOHzy4gTQUWNwYr7hXpb7WRXSV1/hrhcIPhcRxoaBuZaclo9obblLMKnk3 +EkNZTDtc3HXnc8POM0tH5+x32TPzXzhipNokBTiRR+An6rlf7PcHi1kjXztZbEal +zLvLTlemj1ziUWVt5m5H8k5NlvspnzOG5EiMjeG9lCFYisq0YWTPZqpElBkKXk7K +S2dnZCAIAjKYhckSH1F925P03P8Myy/KMXWLbLWEVMVB9Et1IWvcOFw+vHHjxvLy +jcP4zP6Adz7BVlFy381hhcD4pTmlKH8s3FRkxQoQ6vOWqY/OfDEIk9Sn0SWN+gTx +vBgUWWTWpmCMVqjI/dxHv5R7wPJ/eDizzHiaTxRFHeAvP/KFBIuaiVKIu+SPddsR +KUaNChFik0Swvqg6lJI+frsyaoQooVnunP9MhIi61OeJBt9XeLLOxz0FP0Zb9B9j +XEai9BMUPaL7zuIvdgA+Ow7fncXD/PLZVTYyWaKioAtl6qbKl4BgSFBOSlXKEIn4 +eem8tSHoVpSEmFCRxwllpyYSfqUTj7Gjlud5qRmu8Pn40OB4jKoT0vNDoQAoed7D +Ej5EGuAwbclKkkZivEeOKeK/dSoNuePt3OHi2tBQjtdzff4V5E3TzG20qM07jKg3 +ICilkgMYAc2xhIhxLhM4EDpfLMK/A4RgzmE6zHyO9e05+GDYRbOvXJDIqJ8SXprp +eyuiCjo5xHLHZsju5NyrunH5QWDDWGV6WlLYC1mM0bnWF2kRJj7O7CmJZYt97zGI +MdOBsSgrjoq9cDgqDnPm8oOAw1gMG5HJdFZB8YxAISq2i2yE6setvi5EY1McycAl +LiRjrftNnksLArHjOzZvXJHzBUBRm1McFr2yuSVeePDqb1CuQLgC4QqEKxCuQLgC +4QqEKxA+AsKV/GPoyhEIuQLhCoRPgcBdJ+RW39vFUxxK5/fEbULcnItMdCLBOyb6 +jcWmmL2uo82fvWJyE5+TbLBn31+Y4s74j22xibMucX4QINt696/rnEisqQliTnwK +hKbsfhyEz054r92PJ/OcIJgcZxKrXOUmRBt0sH79NgcFxk1hTM7bLpcFbmC5YlPc +VAx3ViZuTbkmOO62OFHBE9ygzDwBNG5fd+BRNH+bm7ougqFbxDHFldkGAMhxtnHY +F4OfoPQzx7na+1p6aA+ud52Dv1ZiUFkHZTQ6EYM/dsWpWMyyOMfXgoDiuDV1/frU +LfNnnjNv3W5BrDtIjLvOUkN+npgwXZZrhYA7TEDSIE+3retkql0Q1anrLq6MINwG +h/mZHUXzE6Qy8bNJXFPirk54N24w6G3jFXa+wyQOKIef7X0Ondgn2CAAKghuOzGg +XHEQNx8rk6qFyeSuszTFABcrdh5PQK/SJ27ddjgsk2+B4IDsg22WmaoJ7m/pJupf +53DJCvGWxU25On7JPIGdsMuKmJnXJxwIdAzM2Rtsd4UZdzCEYalPEF3fbe1r6dlt +jI4gtBPTUr6ugyvYILA0YZWZOhcIzBOgJlgxO5EdT3DZIIgW1BI3FAuibjEQJnjm +M7ft2l8GC9ZE0xN0d+soywOvExdLvb3R8QSXDUKlma/2vp+bel0gtBPTAsFtkgmu +4wnWuUHYReyh4b9t4ZecbhPsNuw25GkCgIhZrE24Dl4uWrfgtNvXJ1hV1Fttggtr +MTvaAgGrryVC6u0NtrtZ/095QntfS68LhHZiWsq3OM7NibdYm7CCbQIDYcJxNU64 +GixdgXAFwhUIny3/L8AAEAy9MIp/F7YAAAAASUVORK5CYII=" +alt="How Tor works" style="border-style:none"/> </a></p> <p> diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index 25c911d937..1c96b18682 100644 --- a/contrib/win32build/tor-mingw.nsi.in +++ b/contrib/win32build/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.4.1.5-dev" +!define VERSION "0.4.2.0-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/doc/HACKING/EndOfLifeTor.md b/doc/HACKING/EndOfLifeTor.md new file mode 100644 index 0000000000..c7492f0ca0 --- /dev/null +++ b/doc/HACKING/EndOfLifeTor.md @@ -0,0 +1,50 @@ + +End of Life on an old release series +------------------------------------ + +Here are the steps that the maintainer should take when an old Tor release +series reaches End of Life. Note that they are _only_ for entire series that +have reached their planned EOL: they do not apply to security-related +deprecations of individual versions. + +=== 0. Preliminaries + +0. A few months before End of Life: + Write a deprecation announcement. + Send the announcement out with every new release announcement. + +1. A month before End of Life: + Send the announcement to tor-announce, tor-talk, tor-relays, and the + packagers. + +=== 1. On the day + +1. Open tickets to remove the release from: + - the jenkins builds + - tor's Travis CI cron jobs + - chutney's Travis CI tests (#) + - stem's Travis CI tests (#) + +2. Close the milestone in Trac. To do this, go to Trac, log in, + select "Admin" near the top of the screen, then select "Milestones" from + the menu on the left. Click on the milestone for this version, and + select the "Completed" checkbox. By convention, we select the date as + the End of Life date. + +3. Replace NNN-backport with NNN-unreached-backport in all open trac tickets. + +4. If there are any remaining tickets in the milestone: + - merge_ready tickets are for backports: + - if there are no supported releases for the backport, close the ticket + - if there is an earlier (LTS) release for the backport, move the ticket + to that release + - other tickets should be closed (if we won't fix them) or moved to a + supported release (if we will fix them) + +5. Mail the end of life announcement to tor-announce, the packagers list, + and tor-relays. The current list of packagers is in ReleasingTor.md. + +6. Ask at least two of weasel/arma/Sebastian to remove the old version + number from their approved versions list. + +7. Update the CoreTorReleases wiki page. diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md index a97ca08ce9..15c8ac55d8 100644 --- a/doc/HACKING/ReleasingTor.md +++ b/doc/HACKING/ReleasingTor.md @@ -247,9 +247,11 @@ new Tor release: `maint-x.y.z` branch to "newversion-dev", and do a `merge -s ours` merge to avoid taking that change into master. -2. Forward-port the ChangeLog (and ReleaseNotes if appropriate) to the - master branch. - -3. Keep an eye on the blog post, to moderate comments and answer questions. +2. If there is a new `maint-x.y.z` branch, create a Travis CI cron job that + builds the release every week. (It's ok to skip the weekly build if the + branch was updated in the last 24 hours.) +3. Forward-port the ChangeLog (and ReleaseNotes if appropriate) to the + master branch. +4. Keep an eye on the blog post, to moderate comments and answer questions. diff --git a/doc/include.am b/doc/include.am index 0a123aae11..a9d3fa1c98 100644 --- a/doc/include.am +++ b/doc/include.am @@ -15,17 +15,32 @@ all_mans = doc/tor doc/tor-gencert doc/tor-resolve doc/torify doc/tor-print-ed-signing-cert if USE_ASCIIDOC -nodist_man1_MANS = $(all_mans:=.1) -doc_DATA = $(all_mans:=.html) +txt_in = $(all_mans:=.1.txt) + +if BUILD_HTML_DOCS html_in = $(all_mans:=.html.in) +doc_DATA = $(all_mans:=.html) +else +html_in = +doc_DATA = +endif + +if BUILD_MANPAGE +nodist_man1_MANS = $(all_mans:=.1) man_in = $(all_mans:=.1.in) -txt_in = $(all_mans:=.1.txt) else +nodist_man1_MANS = +man_in = +endif + +else + html_in = +doc_DATA = man_in = txt_in = nodist_man1_MANS = -doc_DATA = + endif EXTRA_DIST+= doc/asciidoc-helper.sh \ diff --git a/doc/tor-print-ed-signing-cert.1.txt b/doc/tor-print-ed-signing-cert.1.txt index 1a3109df95..48a3f095d5 100644 --- a/doc/tor-print-ed-signing-cert.1.txt +++ b/doc/tor-print-ed-signing-cert.1.txt @@ -21,6 +21,12 @@ DESCRIPTION **tor-print-ed-signing-cert** is utility program for Tor relay operators to check expiration date of ed25519 signing certificate. +Expiration date is printed in three formats: + +* Human-readable timestamp, localized to current timezone. +* RFC 1123 timestamp (at GMT timezone). +* Unix time value. + SEE ALSO -------- **tor**(1) + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 95d56c6dbd..362c409903 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -37,7 +37,7 @@ Project's website. COMMAND-LINE OPTIONS -------------------- -[[opt-h]] **-h**, **-help**:: +[[opt-h]] **-h**, **--help**:: Display a short help message and exit. [[opt-f]] **-f** __FILE__:: @@ -447,13 +447,18 @@ GENERAL OPTIONS setting for DataDirectoryGroupReadable when the CacheDirectory is the same as the DataDirectory, and 0 otherwise. (Default: auto) -[[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. - Clients also simultaneously try a FallbackDir, to avoid hangs on client - startup if a directory authority is down. Clients retry FallbackDirs more - often than directory authorities, to reduce the load on the directory - authorities. +[[FallbackDir]] **FallbackDir** __ipv4address__:__dirport__ orport=__orport__ id=__fingerprint__ [weight=__num__] [ipv6=**[**__ipv6address__**]**:__orport__]:: + When tor is unable to connect to any directory cache for directory info + (usually because it doesn't know about any yet) it tries a hard-coded + directory. Relays try one directory authority at a time. Clients try + multiple directory authorities and FallbackDirs, to avoid hangs on + startup if a hard-coded directory is down. Clients wait for a few seconds + between each attempt, and retry FallbackDirs more often than directory + authorities, to reduce the load on the directory authorities. + + + + FallbackDirs should be stable relays with stable IP addresses, ports, + and identity keys. They must have a DirPort. + + + By default, the directory authorities are also FallbackDirs. Specifying a FallbackDir replaces Tor's default hard-coded FallbackDirs (if any). (See the **DirAuthority** entry for an explanation of each flag.) @@ -463,30 +468,30 @@ GENERAL OPTIONS FallbackDir line is present, it replaces the hard-coded FallbackDirs, regardless of the value of UseDefaultFallbackDirs.) (Default: 1) -[[DirAuthority]] **DirAuthority** [__nickname__] [**flags**] __ipv4address__:__port__ __fingerprint__:: +[[DirAuthority]] **DirAuthority** [__nickname__] [**flags**] __ipv4address__:__dirport__ __fingerprint__:: Use a nonstandard authoritative directory server at the provided address and port, with the specified key fingerprint. This option can be repeated many times, for multiple authoritative directory servers. Flags are separated by spaces, and determine what kind of an authority this directory is. By default, an authority is not authoritative for any directory style - or version unless an appropriate flag is given. + or version unless an appropriate flag is given. + + + Tor will use this authority as a bridge authoritative directory if the - "bridge" flag is set. If a flag "orport=**port**" is given, Tor will use the - given port when opening encrypted tunnels to the dirserver. If a flag - "weight=**num**" is given, then the directory server is chosen randomly - with probability proportional to that weight (default 1.0). If a + "bridge" flag is set. If a flag "orport=**orport**" is given, Tor will + use the given port when opening encrypted tunnels to the dirserver. If a + flag "weight=**num**" is given, then the directory server is chosen + randomly with probability proportional to that weight (default 1.0). If a flag "v3ident=**fp**" is given, the dirserver is a v3 directory authority whose v3 long-term signing key has the fingerprint **fp**. Lastly, if an "ipv6=**[**__ipv6address__**]**:__orport__" flag is present, then - the directory - authority is listening for IPv6 connections on the indicated IPv6 address - and OR Port. + + the directory authority is listening for IPv6 connections on the + indicated IPv6 address and OR Port. + + Tor will contact the authority at __ipv4address__ to - download directory documents. The provided __port__ value is a dirport; - clients ignore this in favor of the specified "orport=" value. If an - IPv6 ORPort is supplied, Tor will - also download directory documents at the IPv6 ORPort. + + download directory documents. Clients always use the ORPort. Relays + usually use the DirPort, but will use the ORPort in some circumstances. + If an IPv6 ORPort is supplied, clients will also download directory + documents at the IPv6 ORPort, if they are configured to use IPv6. + + If no **DirAuthority** line is given, Tor will use the default directory authorities. NOTE: this option is intended for setting up a private Tor @@ -2722,12 +2727,6 @@ on the public Tor network. multiple times: the values from multiple lines are spliced together. When this is set then **VersioningAuthoritativeDirectory** should be set too. -[[RecommendedPackages]] **RecommendedPackages** __PACKAGENAME__ __VERSION__ __URL__ __DIGESTTYPE__**=**__DIGEST__ :: - Adds "package" line to the directory authority's vote. This information - is used to vote on the correct URL and digest for the released versions - of different Tor-related packages, so that the consensus can certify - them. This line may appear any number of times. - [[RecommendedClientVersions]] **RecommendedClientVersions** __STRING__:: STRING is a comma-separated list of Tor versions currently believed to be safe for clients to use. This information is included in version 2 @@ -2974,7 +2973,7 @@ The following options are used to configure a hidden service. [[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** __protocol__:: The onion service will use the given protocol to expose the global circuit - identifier of each inbound client circuit via the selected protocol. The only + identifier of each inbound client circuit. The only protocol supported right now \'haproxy'. This option is only for v3 services. (Default: none) + + @@ -2991,12 +2990,12 @@ The following options are used to configure a hidden service. + global_circuit_id = (0xAA << 24) + (0xBB << 16) + (0xCC << 8) + 0xDD; + + - In the case above, where the last 32-bit is 0xffffffff, the global circuit + In the case above, where the last 32-bits are 0xffffffff, the global circuit identifier would be 4294967295. You can use this value together with Tor's - control port where it is possible to terminate a circuit given the global - circuit identifier. For more information about this see controls-spec.txt. + + control port to terminate particular circuits using their global + circuit identifiers. For more information about this see control-spec.txt. + + - The HAProxy version 1 proxy protocol is described in detail at + The HAProxy version 1 protocol is described in detail at https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: diff --git a/scripts/git/git-merge-forward.sh b/scripts/git/git-merge-forward.sh index 67af7e98bf..ba29983284 100755 --- a/scripts/git/git-merge-forward.sh +++ b/scripts/git/git-merge-forward.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ############################## # Configuration (change me!) # @@ -14,12 +14,12 @@ # ... which means that the tor worktrees are in /home/<user>/git/tor-wkt # Where are all those git repositories? -GIT_PATH="FULL_PATH_TO_GIT_REPOSITORY_DIRECTORY" +GIT_PATH=${TOR_FULL_GIT_PATH:-"FULL_PATH_TO_GIT_REPOSITORY_DIRECTORY"} # The tor master git repository directory from which all the worktree have # been created. -TOR_MASTER_NAME="tor" +TOR_MASTER_NAME=${TOR_MASTER_NAME:-"tor"} # The worktrees location (directory). -TOR_WKT_NAME="tor-wkt" +TOR_WKT_NAME=${TOR_WKT_NAME:-"tor-wkt"} ######################### # End of configuration. # @@ -38,15 +38,15 @@ TOR_WKT_NAME="tor-wkt" # # First set of arrays are the maint-* branch and then the release-* branch. # New arrays need to be in the WORKTREE= array else they aren't considered. -MAINT_034=( "maint-0.3.4" "maint-0.2.9" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.4" ) -MAINT_035=( "maint-0.3.5" "maint-0.3.4" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" ) +MAINT_035=( "maint-0.3.5" "maint-0.2.9" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" ) MAINT_040=( "maint-0.4.0" "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.0" ) -MAINT_MASTER=( "master" "maint-0.4.0" "$GIT_PATH/$TOR_MASTER_NAME" ) +MAINT_041=( "maint-0.4.1" "maint-0.4.0" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.1" ) +MAINT_MASTER=( "master" "maint-0.4.1" "$GIT_PATH/$TOR_MASTER_NAME" ) RELEASE_029=( "release-0.2.9" "maint-0.2.9" "$GIT_PATH/$TOR_WKT_NAME/release-0.2.9" ) -RELEASE_034=( "release-0.3.4" "maint-0.3.4" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.4" ) RELEASE_035=( "release-0.3.5" "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.5" ) RELEASE_040=( "release-0.4.0" "maint-0.4.0" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.0" ) +RELEASE_041=( "release-0.4.1" "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.1" ) # The master branch path has to be the main repository thus contains the # origin that will be used to fetch the updates. All the worktrees are created @@ -55,14 +55,14 @@ ORIGIN_PATH="$GIT_PATH/$TOR_MASTER_NAME" # SC2034 -- shellcheck thinks that these are unused. We know better. ACTUALLY_THESE_ARE_USED=<<EOF -${MAINT_034[0]} ${MAINT_035[0]} ${MAINT_040[0]} +${MAINT_041[0]} ${MAINT_MASTER[0]} ${RELEASE_029[0]} -${RELEASE_034[0]} ${RELEASE_035[0]} ${RELEASE_040[0]} +${RELEASE_041[0]} EOF ########################## @@ -74,15 +74,15 @@ EOF WORKTREE=( RELEASE_029[@] - MAINT_034[@] - RELEASE_034[@] - MAINT_035[@] RELEASE_035[@] MAINT_040[@] RELEASE_040[@] + MAINT_041[@] + RELEASE_041[@] + MAINT_MASTER[@] ) COUNT=${#WORKTREE[@]} diff --git a/scripts/git/git-pull-all.sh b/scripts/git/git-pull-all.sh index 5d1d58e4bf..8eb42c7c18 100755 --- a/scripts/git/git-pull-all.sh +++ b/scripts/git/git-pull-all.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ################################## # User configuration (change me) # @@ -14,12 +14,12 @@ # ... which means that the tor worktrees are in /home/<user>/git/tor-wkt # Where are all those git repositories? -GIT_PATH="FULL_PATH_TO_GIT_REPOSITORY_DIRECTORY" +GIT_PATH=${TOR_FULL_GIT_PATH:-"FULL_PATH_TO_GIT_REPOSITORY_DIRECTORY"} # The tor master git repository directory from which all the worktree have # been created. -TOR_MASTER_NAME="tor" +TOR_MASTER_NAME=${TOR_MASTER_NAME:-"tor"} # The worktrees location (directory). -TOR_WKT_NAME="tor-wkt" +TOR_WKT_NAME=${TOR_WKT_NAME:-"tor-wkt"} ######################### # End of configuration. # @@ -37,15 +37,15 @@ TOR_WKT_NAME="tor-wkt" # First set of arrays are the maint-* branch and then the release-* branch. # New arrays need to be in the WORKTREE= array else they aren't considered. MAINT_029=( "maint-0.2.9" "$GIT_PATH/$TOR_WKT_NAME/maint-0.2.9" ) -MAINT_034=( "maint-0.3.4" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.4" ) MAINT_035=( "maint-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/maint-0.3.5" ) MAINT_040=( "maint-0.4.0" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.0" ) +MAINT_041=( "maint-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/maint-0.4.1" ) MAINT_MASTER=( "master" "$GIT_PATH/$TOR_MASTER_NAME" ) RELEASE_029=( "release-0.2.9" "$GIT_PATH/$TOR_WKT_NAME/release-0.2.9" ) -RELEASE_034=( "release-0.3.4" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.4" ) RELEASE_035=( "release-0.3.5" "$GIT_PATH/$TOR_WKT_NAME/release-0.3.5" ) RELEASE_040=( "release-0.4.0" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.0" ) +RELEASE_041=( "release-0.4.1" "$GIT_PATH/$TOR_WKT_NAME/release-0.4.1" ) # The master branch path has to be the main repository thus contains the # origin that will be used to fetch the updates. All the worktrees are created @@ -55,14 +55,14 @@ ORIGIN_PATH="$GIT_PATH/$TOR_MASTER_NAME" # SC2034 -- shellcheck thinks that these are unused. We know better. ACTUALLY_THESE_ARE_USED=<<EOF ${MAINT_029[0]} -${MAINT_034[0]} ${MAINT_035[0]} ${MAINT_040[0]} +${MAINT_041[0]} ${MAINT_MASTER[0]} ${RELEASE_029[0]} -${RELEASE_034[0]} ${RELEASE_035[0]} ${RELEASE_040[0]} +${RELEASE_041[0]} EOF ########################## @@ -75,15 +75,15 @@ WORKTREE=( MAINT_029[@] RELEASE_029[@] - MAINT_034[@] - RELEASE_034[@] - MAINT_035[@] RELEASE_035[@] MAINT_040[@] RELEASE_040[@] + MAINT_041[@] + RELEASE_041[@] + MAINT_MASTER[@] ) COUNT=${#WORKTREE[@]} diff --git a/scripts/git/git-push-all.sh b/scripts/git/git-push-all.sh index 0ce951d4bd..2030a600ff 100755 --- a/scripts/git/git-push-all.sh +++ b/scripts/git/git-push-all.sh @@ -1,11 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash # The remote upstream branch on which git.torproject.org/tor.git points to. -UPSTREAM_BRANCH="upstream" +UPSTREAM_BRANCH=${TOR_UPSTREAM_REMOTE_NAME:-"upstream"} -git push $UPSTREAM_BRANCH \ +git push "$UPSTREAM_BRANCH" \ master \ + {release,maint}-0.4.1 \ {release,maint}-0.4.0 \ {release,maint}-0.3.5 \ - {release,maint}-0.3.4 \ {release,maint}-0.2.9 diff --git a/scripts/git/post-merge.git-hook b/scripts/git/post-merge.git-hook index 176b7c9bbd..eae4f999e7 100755 --- a/scripts/git/post-merge.git-hook +++ b/scripts/git/post-merge.git-hook @@ -35,6 +35,12 @@ check_for_script_update() { fi } +cur_branch=$(git rev-parse --abbrev-ref HEAD) +if [ "$cur_branch" != "master" ]; then + echo "post-merge: Not a master branch. Skipping." + exit 0 +fi + check_for_diffs "pre-push" check_for_diffs "pre-commit" check_for_diffs "post-merge" diff --git a/scripts/git/pre-commit.git-hook b/scripts/git/pre-commit.git-hook index b285776c04..2a29837198 100755 --- a/scripts/git/pre-commit.git-hook +++ b/scripts/git/pre-commit.git-hook @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # To install this script, copy it to .git/hooks/pre-commit in local copy of # tor git repo and make sure it has permission to execute. diff --git a/scripts/git/pre-push.git-hook b/scripts/git/pre-push.git-hook index c9e72a4d43..40a3bffa79 100755 --- a/scripts/git/pre-push.git-hook +++ b/scripts/git/pre-push.git-hook @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # git pre-push hook script to: # 1) prevent "fixup!" and "squash!" commit from ending up in master, release-* @@ -28,14 +28,17 @@ if [ -x "$workdir/.git/hooks/pre-commit" ]; then fi fi -if [ -e scripts/maint/practracker/practracker.py ]; then - if ! python3 ./scripts/maint/practracker/practracker.py "$workdir"; then - exit 1 - fi +PT_DIR=scripts/maint/practracker + +if [ -e "${PT_DIR}/practracker.py" ]; then + if [ -e "${PT_DIR}/.enable_practracker_in_hooks" ]; then + if ! python3 "${PT_DIR}/practracker.py" "$workdir"; then + exit 1 + fi + fi fi remote="$1" -remote_loc="$2" remote_name=$(git remote --verbose | grep "$2" | awk '{print $1}' | head -n 1) @@ -105,4 +108,3 @@ do done exit 0 - diff --git a/scripts/maint/add_c_file.py b/scripts/maint/add_c_file.py index 499415974f..adf7ce79bb 100755 --- a/scripts/maint/add_c_file.py +++ b/scripts/maint/add_c_file.py @@ -125,8 +125,8 @@ class AutomakeChunk: Y \ Z """ - self.prespace = "\t" - self.postspace = "\t\t" + prespace = "\t" + postspace = "\t\t" for lineno, line in enumerate(self.lines): m = re.match(r'(\s+)(\S+)(\s+)\\', line) if not m: @@ -135,7 +135,7 @@ class AutomakeChunk: if fname > member: self.insert_before(lineno, member, prespace, postspace) return - self.insert_at_end(member) + self.insert_at_end(member, prespace, postspace) def insert_before(self, lineno, member, prespace, postspace): self.lines.insert(lineno, diff --git a/scripts/maint/checkSpace.pl b/scripts/maint/checkSpace.pl index 433ae62807..9c9b68ff9d 100755 --- a/scripts/maint/checkSpace.pl +++ b/scripts/maint/checkSpace.pl @@ -177,7 +177,7 @@ for my $fn (@ARGV) { $1 ne "elsif" and $1 ne "WINAPI" and $2 ne "WINAPI" and $1 ne "void" and $1 ne "__attribute__" and $1 ne "op" and $1 ne "size_t" and $1 ne "double" and $1 ne "uint64_t" and - $1 ne "workqueue_reply_t") { + $1 ne "workqueue_reply_t" and $1 ne "bool") { msg " fn ():$fn:$.\n"; } } diff --git a/scripts/maint/practracker/.enable_practracker_in_hooks b/scripts/maint/practracker/.enable_practracker_in_hooks new file mode 100644 index 0000000000..a9e707f5da --- /dev/null +++ b/scripts/maint/practracker/.enable_practracker_in_hooks @@ -0,0 +1 @@ +This file is present to tell our git hooks to run practracker on this branch. diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt index 726dc9c3ef..4f3943f21c 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -29,47 +29,46 @@ # # Remember: It is better to fix the problem than to add a new exception! -problem file-size /src/app/config/config.c 8518 +problem file-size /src/app/config/config.c 8510 problem include-count /src/app/config/config.c 87 problem function-size /src/app/config/config.c:options_act_reversible() 296 problem function-size /src/app/config/config.c:options_act() 589 -problem function-size /src/app/config/config.c:resolve_my_address() 192 -problem function-size /src/app/config/config.c:options_validate() 1217 +problem function-size /src/app/config/config.c:resolve_my_address() 190 +problem function-size /src/app/config/config.c:options_validate() 1209 problem function-size /src/app/config/config.c:options_init_from_torrc() 207 -problem function-size /src/app/config/config.c:options_init_from_string() 173 -problem function-size /src/app/config/config.c:options_init_logs() 146 +problem function-size /src/app/config/config.c:options_init_from_string() 171 +problem function-size /src/app/config/config.c:options_init_logs() 145 problem function-size /src/app/config/config.c:parse_bridge_line() 104 -problem function-size /src/app/config/config.c:parse_transport_line() 191 -problem function-size /src/app/config/config.c:parse_dir_authority_line() 151 -problem function-size /src/app/config/config.c:parse_dir_fallback_line() 102 -problem function-size /src/app/config/config.c:parse_port_config() 452 -problem function-size /src/app/config/config.c:parse_ports() 170 -problem function-size /src/app/config/config.c:getinfo_helper_config() 116 -problem function-size /src/app/config/confparse.c:config_assign_value() 205 -problem function-size /src/app/config/confparse.c:config_get_assigned_option() 129 -problem include-count /src/app/main/main.c 67 +problem function-size /src/app/config/config.c:parse_transport_line() 189 +problem function-size /src/app/config/config.c:parse_dir_authority_line() 150 +problem function-size /src/app/config/config.c:parse_dir_fallback_line() 101 +problem function-size /src/app/config/config.c:parse_port_config() 446 +problem function-size /src/app/config/config.c:parse_ports() 168 +problem function-size /src/app/config/config.c:getinfo_helper_config() 113 +problem file-size /src/app/config/or_options_st.h 1112 +problem include-count /src/app/main/main.c 68 problem function-size /src/app/main/main.c:dumpstats() 102 problem function-size /src/app/main/main.c:tor_init() 137 problem function-size /src/app/main/main.c:sandbox_init_filter() 291 problem function-size /src/app/main/main.c:run_tor_main_loop() 105 -problem function-size /src/app/main/ntmain.c:nt_service_install() 125 +problem function-size /src/app/main/ntmain.c:nt_service_install() 126 problem file-size /src/core/mainloop/connection.c 5569 problem include-count /src/core/mainloop/connection.c 62 problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 185 -problem function-size /src/core/mainloop/connection.c:connection_listener_new() 328 +problem function-size /src/core/mainloop/connection.c:connection_listener_new() 324 problem function-size /src/core/mainloop/connection.c:connection_handle_listener_read() 161 -problem function-size /src/core/mainloop/connection.c:connection_connect_sockaddr() 103 problem function-size /src/core/mainloop/connection.c:connection_proxy_connect() 148 problem function-size /src/core/mainloop/connection.c:connection_read_proxy_handshake() 153 -problem function-size /src/core/mainloop/connection.c:retry_listener_ports() 116 +problem function-size /src/core/mainloop/connection.c:retry_listener_ports() 112 problem function-size /src/core/mainloop/connection.c:connection_handle_read_impl() 111 -problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 181 +problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 180 problem function-size /src/core/mainloop/connection.c:connection_handle_write_impl() 241 problem function-size /src/core/mainloop/connection.c:assert_connection_ok() 143 problem include-count /src/core/mainloop/mainloop.c 63 problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 108 problem function-size /src/core/mainloop/mainloop.c:run_connection_housekeeping() 123 problem file-size /src/core/or/channel.c 3487 +problem file-size /src/core/or/channel.h 780 problem function-size /src/core/or/channeltls.c:channel_tls_handle_var_cell() 160 problem function-size /src/core/or/channeltls.c:channel_tls_process_versions_cell() 170 problem function-size /src/core/or/channeltls.c:channel_tls_process_netinfo_cell() 214 @@ -80,210 +79,208 @@ problem function-size /src/core/or/circuitbuild.c:get_unique_circ_id_by_chan() 1 problem function-size /src/core/or/circuitbuild.c:circuit_extend() 147 problem function-size /src/core/or/circuitbuild.c:choose_good_exit_server_general() 206 problem include-count /src/core/or/circuitlist.c 55 -problem function-size /src/core/or/circuitlist.c:HT_PROTOTYPE() 128 +problem function-size /src/core/or/circuitlist.c:HT_PROTOTYPE() 109 problem function-size /src/core/or/circuitlist.c:circuit_free_() 143 -problem function-size /src/core/or/circuitlist.c:circuit_find_to_cannibalize() 102 +problem function-size /src/core/or/circuitlist.c:circuit_find_to_cannibalize() 101 problem function-size /src/core/or/circuitlist.c:circuit_about_to_free() 120 problem function-size /src/core/or/circuitlist.c:circuits_handle_oom() 117 -problem function-size /src/core/or/circuitmux.c:circuitmux_set_policy() 110 -problem function-size /src/core/or/circuitmux.c:circuitmux_attach_circuit() 114 -problem file-size /src/core/or/circuitpadding.c 3040 +problem function-size /src/core/or/circuitmux.c:circuitmux_set_policy() 109 +problem function-size /src/core/or/circuitmux.c:circuitmux_attach_circuit() 113 +problem file-size /src/core/or/circuitpadding.c 3043 problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 107 -problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 113 -problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_relay_hide_intro_circuits() 104 +problem file-size /src/core/or/circuitpadding.h 809 +problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_relay_hide_intro_circuits() 103 problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_client_hide_rend_circuits() 112 -problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_state() 124 +problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_state() 123 problem file-size /src/core/or/circuituse.c 3162 -problem function-size /src/core/or/circuituse.c:circuit_is_acceptable() 132 +problem function-size /src/core/or/circuituse.c:circuit_is_acceptable() 128 problem function-size /src/core/or/circuituse.c:circuit_expire_building() 394 problem function-size /src/core/or/circuituse.c:circuit_log_ancient_one_hop_circuits() 126 problem function-size /src/core/or/circuituse.c:circuit_build_failed() 149 -problem function-size /src/core/or/circuituse.c:circuit_launch_by_extend_info() 110 -problem function-size /src/core/or/circuituse.c:circuit_get_open_circ_or_launch() 354 +problem function-size /src/core/or/circuituse.c:circuit_launch_by_extend_info() 108 +problem function-size /src/core/or/circuituse.c:circuit_get_open_circ_or_launch() 352 problem function-size /src/core/or/circuituse.c:connection_ap_handshake_attach_circuit() 244 problem function-size /src/core/or/command.c:command_process_create_cell() 156 problem function-size /src/core/or/command.c:command_process_relay_cell() 132 -problem file-size /src/core/or/connection_edge.c 4595 +problem file-size /src/core/or/connection_edge.c 4596 problem include-count /src/core/or/connection_edge.c 65 problem function-size /src/core/or/connection_edge.c:connection_ap_expire_beginning() 117 -problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite() 192 -problem function-size /src/core/or/connection_edge.c:connection_ap_handle_onion() 188 -problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite_and_attach() 423 +problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite() 191 +problem function-size /src/core/or/connection_edge.c:connection_ap_handle_onion() 185 +problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite_and_attach() 421 problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_send_begin() 111 -problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_socks_resolved() 106 -problem function-size /src/core/or/connection_edge.c:connection_exit_begin_conn() 184 +problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_socks_resolved() 101 +problem function-size /src/core/or/connection_edge.c:connection_exit_begin_conn() 185 problem function-size /src/core/or/connection_edge.c:connection_exit_connect() 102 -problem file-size /src/core/or/connection_or.c 3124 +problem file-size /src/core/or/connection_or.c 3122 problem include-count /src/core/or/connection_or.c 51 problem function-size /src/core/or/connection_or.c:connection_or_group_set_badness_() 105 -problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 144 -problem function-size /src/core/or/connection_or.c:connection_or_compute_authenticate_cell_body() 235 +problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 142 +problem function-size /src/core/or/connection_or.c:connection_or_compute_authenticate_cell_body() 231 +problem file-size /src/core/or/or.h 1103 +problem include-count /src/core/or/or.h 49 problem file-size /src/core/or/policies.c 3249 problem function-size /src/core/or/policies.c:policy_summarize() 107 problem function-size /src/core/or/protover.c:protover_all_supported() 117 -problem file-size /src/core/or/relay.c 3244 +problem file-size /src/core/or/relay.c 3264 problem function-size /src/core/or/relay.c:circuit_receive_relay_cell() 127 -problem function-size /src/core/or/relay.c:relay_send_command_from_edge_() 112 -problem function-size /src/core/or/relay.c:connection_ap_process_end_not_open() 194 -problem function-size /src/core/or/relay.c:connection_edge_process_relay_cell_not_open() 139 -problem function-size /src/core/or/relay.c:connection_edge_process_relay_cell() 430 -problem function-size /src/core/or/relay.c:connection_edge_package_raw_inbuf() 129 -problem function-size /src/core/or/relay.c:circuit_resume_edge_reading_helper() 148 +problem function-size /src/core/or/relay.c:relay_send_command_from_edge_() 109 +problem function-size /src/core/or/relay.c:connection_ap_process_end_not_open() 192 +problem function-size /src/core/or/relay.c:connection_edge_process_relay_cell_not_open() 137 +problem function-size /src/core/or/relay.c:handle_relay_cell_command() 369 +problem function-size /src/core/or/relay.c:connection_edge_package_raw_inbuf() 128 +problem function-size /src/core/or/relay.c:circuit_resume_edge_reading_helper() 146 problem function-size /src/core/or/scheduler_kist.c:kist_scheduler_run() 171 problem function-size /src/core/or/scheduler_vanilla.c:vanilla_scheduler_run() 109 problem function-size /src/core/or/versions.c:tor_version_parse() 104 -problem function-size /src/core/proto/proto_socks.c:parse_socks_client() 112 -problem function-size /src/feature/client/addressmap.c:addressmap_rewrite() 112 +problem function-size /src/core/proto/proto_socks.c:parse_socks_client() 110 +problem function-size /src/feature/client/addressmap.c:addressmap_rewrite() 109 problem function-size /src/feature/client/bridges.c:rewrite_node_address_for_bridge() 126 problem function-size /src/feature/client/circpathbias.c:pathbias_measure_close_rate() 108 problem function-size /src/feature/client/dnsserv.c:evdns_server_callback() 153 problem file-size /src/feature/client/entrynodes.c 3824 -problem function-size /src/feature/client/entrynodes.c:entry_guards_upgrade_waiting_circuits() 157 +problem function-size /src/feature/client/entrynodes.c:entry_guards_upgrade_waiting_circuits() 155 problem function-size /src/feature/client/entrynodes.c:entry_guard_parse_from_state() 246 +problem file-size /src/feature/client/entrynodes.h 639 problem function-size /src/feature/client/transports.c:handle_proxy_line() 108 -problem function-size /src/feature/client/transports.c:parse_method_line_helper() 112 +problem function-size /src/feature/client/transports.c:parse_method_line_helper() 110 problem function-size /src/feature/client/transports.c:create_managed_proxy_environment() 109 problem function-size /src/feature/control/control.c:connection_control_process_inbuf() 136 -problem function-size /src/feature/control/control_auth.c:handle_control_authchallenge() 103 -problem function-size /src/feature/control/control_auth.c:handle_control_authenticate() 187 -problem function-size /src/feature/control/control_cmd.c:handle_control_extendcircuit() 151 -problem function-size /src/feature/control/control_cmd.c:handle_control_add_onion() 269 -problem function-size /src/feature/control/control_cmd.c:add_onion_helper_keyarg() 125 -problem function-size /src/feature/control/control_events.c:control_event_stream_status() 119 +problem function-size /src/feature/control/control_auth.c:handle_control_authenticate() 186 +problem function-size /src/feature/control/control_cmd.c:handle_control_extendcircuit() 150 +problem function-size /src/feature/control/control_cmd.c:handle_control_add_onion() 256 +problem function-size /src/feature/control/control_cmd.c:add_onion_helper_keyarg() 116 +problem function-size /src/feature/control/control_events.c:control_event_stream_status() 118 problem include-count /src/feature/control/control_getinfo.c 54 -problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 109 -problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 304 -problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 236 -problem function-size /src/feature/dirauth/bwauth.c:dirserv_read_measured_bandwidths() 124 -problem file-size /src/feature/dirauth/dirvote.c 4726 +problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 108 +problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 302 +problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 234 +problem function-size /src/feature/dirauth/bwauth.c:dirserv_read_measured_bandwidths() 121 +problem file-size /src/feature/dirauth/dirvote.c 4700 problem include-count /src/feature/dirauth/dirvote.c 53 -problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 249 -problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_bw_weights_v10() 235 -problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_consensus() 962 -problem function-size /src/feature/dirauth/dirvote.c:networkstatus_add_detached_signatures() 123 +problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 231 +problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_bw_weights_v10() 233 +problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_consensus() 956 +problem function-size /src/feature/dirauth/dirvote.c:networkstatus_add_detached_signatures() 119 problem function-size /src/feature/dirauth/dirvote.c:dirvote_add_vote() 162 problem function-size /src/feature/dirauth/dirvote.c:dirvote_compute_consensuses() 164 -problem function-size /src/feature/dirauth/dirvote.c:dirserv_generate_networkstatus_vote_obj() 293 +problem function-size /src/feature/dirauth/dirvote.c:dirserv_generate_networkstatus_vote_obj() 283 problem function-size /src/feature/dirauth/dsigs_parse.c:networkstatus_parse_detached_signatures() 196 -problem function-size /src/feature/dirauth/guardfraction.c:dirserv_read_guardfraction_file_from_str() 110 +problem function-size /src/feature/dirauth/guardfraction.c:dirserv_read_guardfraction_file_from_str() 109 problem function-size /src/feature/dirauth/process_descs.c:dirserv_add_descriptor() 125 -problem function-size /src/feature/dirauth/shared_random.c:should_keep_commit() 110 +problem function-size /src/feature/dirauth/shared_random.c:should_keep_commit() 109 problem function-size /src/feature/dirauth/voteflags.c:dirserv_compute_performance_thresholds() 172 problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_cleanup() 115 problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_rescan_flavor_() 111 problem function-size /src/feature/dircache/consdiffmgr.c:consensus_diff_worker_threadfn() 132 -problem function-size /src/feature/dircache/dircache.c:handle_get_current_consensus() 166 -problem function-size /src/feature/dircache/dircache.c:directory_handle_command_post() 120 +problem function-size /src/feature/dircache/dircache.c:handle_get_current_consensus() 165 +problem function-size /src/feature/dircache/dircache.c:directory_handle_command_post() 119 problem file-size /src/feature/dirclient/dirclient.c 3215 problem include-count /src/feature/dirclient/dirclient.c 51 -problem function-size /src/feature/dirclient/dirclient.c:directory_get_from_dirserver() 131 +problem function-size /src/feature/dirclient/dirclient.c:directory_get_from_dirserver() 126 problem function-size /src/feature/dirclient/dirclient.c:directory_initiate_request() 201 -problem function-size /src/feature/dirclient/dirclient.c:directory_send_command() 241 -problem function-size /src/feature/dirclient/dirclient.c:dir_client_decompress_response_body() 114 +problem function-size /src/feature/dirclient/dirclient.c:directory_send_command() 239 +problem function-size /src/feature/dirclient/dirclient.c:dir_client_decompress_response_body() 111 problem function-size /src/feature/dirclient/dirclient.c:connection_dir_client_reached_eof() 189 -problem function-size /src/feature/dirclient/dirclient.c:handle_response_fetch_consensus() 105 -problem function-size /src/feature/dircommon/consdiff.c:gen_ed_diff() 204 -problem function-size /src/feature/dircommon/consdiff.c:apply_ed_diff() 159 -problem function-size /src/feature/dirparse/authcert_parse.c:authority_cert_parse_from_string() 182 -problem function-size /src/feature/dirparse/microdesc_parse.c:microdescs_parse_from_string() 169 -problem function-size /src/feature/dirparse/ns_parse.c:routerstatus_parse_entry_from_string() 286 +problem function-size /src/feature/dirclient/dirclient.c:handle_response_fetch_consensus() 104 +problem function-size /src/feature/dircommon/consdiff.c:gen_ed_diff() 203 +problem function-size /src/feature/dircommon/consdiff.c:apply_ed_diff() 158 +problem function-size /src/feature/dirparse/authcert_parse.c:authority_cert_parse_from_string() 181 +problem function-size /src/feature/dirparse/microdesc_parse.c:microdescs_parse_from_string() 166 +problem function-size /src/feature/dirparse/ns_parse.c:routerstatus_parse_entry_from_string() 280 problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_verify_bw_weights() 389 -problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_parse_vote_from_string() 638 -problem function-size /src/feature/dirparse/parsecommon.c:tokenize_string() 103 -problem function-size /src/feature/dirparse/parsecommon.c:get_next_token() 159 -problem function-size /src/feature/dirparse/routerparse.c:router_parse_entry_from_string() 557 -problem function-size /src/feature/dirparse/routerparse.c:extrainfo_parse_entry_from_string() 210 +problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_parse_vote_from_string() 635 +problem function-size /src/feature/dirparse/parsecommon.c:tokenize_string() 101 +problem function-size /src/feature/dirparse/parsecommon.c:get_next_token() 158 +problem function-size /src/feature/dirparse/routerparse.c:router_parse_entry_from_string() 554 +problem function-size /src/feature/dirparse/routerparse.c:extrainfo_parse_entry_from_string() 208 problem function-size /src/feature/hibernate/hibernate.c:accounting_parse_options() 109 -problem function-size /src/feature/hs/hs_cell.c:hs_cell_build_establish_intro() 115 -problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 154 -problem function-size /src/feature/hs/hs_client.c:send_introduce1() 104 -problem function-size /src/feature/hs/hs_client.c:hs_config_client_authorization() 108 -problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 104 -problem function-size /src/feature/hs/hs_config.c:config_generic_service() 140 -problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 104 -problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 110 +problem function-size /src/feature/hs/hs_cell.c:hs_cell_build_establish_intro() 113 +problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 152 +problem function-size /src/feature/hs/hs_client.c:send_introduce1() 103 +problem function-size /src/feature/hs/hs_client.c:hs_config_client_authorization() 107 +problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 102 +problem function-size /src/feature/hs/hs_config.c:config_generic_service() 138 +problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 101 +problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 105 problem function-size /src/feature/hs/hs_descriptor.c:decode_introduction_point() 122 -problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_superencrypted_v3() 109 -problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_encrypted_v3() 109 +problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_superencrypted_v3() 107 +problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_encrypted_v3() 107 problem file-size /src/feature/hs/hs_service.c 4109 -problem function-size /src/feature/keymgt/loadkey.c:ed_key_init_from_file() 333 -problem function-size /src/feature/nodelist/authcert.c:trusted_dirs_load_certs_from_string() 124 -problem function-size /src/feature/nodelist/authcert.c:authority_certs_fetch_missing() 296 -problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 166 +problem function-size /src/feature/keymgt/loadkey.c:ed_key_init_from_file() 326 +problem function-size /src/feature/nodelist/authcert.c:trusted_dirs_load_certs_from_string() 123 +problem function-size /src/feature/nodelist/authcert.c:authority_certs_fetch_missing() 295 +problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 162 problem function-size /src/feature/nodelist/microdesc.c:microdesc_cache_rebuild() 134 -problem include-count /src/feature/nodelist/networkstatus.c 62 -problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 176 -problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 293 -problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 123 -problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 206 -problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 114 -problem function-size /src/feature/nodelist/nodelist.c:compute_frac_paths_available() 193 -problem file-size /src/feature/nodelist/routerlist.c 3238 +problem include-count /src/feature/nodelist/networkstatus.c 63 +problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 175 +problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 289 +problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 122 +problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 203 +problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 112 +problem function-size /src/feature/nodelist/nodelist.c:compute_frac_paths_available() 190 +problem file-size /src/feature/nodelist/routerlist.c 3239 problem function-size /src/feature/nodelist/routerlist.c:router_rebuild_store() 148 -problem function-size /src/feature/nodelist/routerlist.c:router_add_to_routerlist() 169 +problem function-size /src/feature/nodelist/routerlist.c:router_add_to_routerlist() 168 problem function-size /src/feature/nodelist/routerlist.c:routerlist_remove_old_routers() 121 -problem function-size /src/feature/nodelist/routerlist.c:update_consensus_router_descriptor_downloads() 136 +problem function-size /src/feature/nodelist/routerlist.c:update_consensus_router_descriptor_downloads() 135 problem function-size /src/feature/nodelist/routerlist.c:update_extrainfo_downloads() 103 -problem function-size /src/feature/relay/dns.c:dns_resolve_impl() 134 +problem function-size /src/feature/relay/dns.c:dns_resolve_impl() 131 problem function-size /src/feature/relay/dns.c:configure_nameservers() 161 -problem function-size /src/feature/relay/dns.c:evdns_callback() 109 -problem file-size /src/feature/relay/router.c 3407 +problem function-size /src/feature/relay/dns.c:evdns_callback() 108 +problem file-size /src/feature/relay/router.c 3522 problem include-count /src/feature/relay/router.c 56 problem function-size /src/feature/relay/router.c:init_keys() 252 problem function-size /src/feature/relay/router.c:get_my_declared_family() 114 problem function-size /src/feature/relay/router.c:router_build_fresh_unsigned_routerinfo() 136 -problem function-size /src/feature/relay/router.c:router_dump_router_to_string() 371 -problem function-size /src/feature/relay/router.c:extrainfo_dump_to_string() 206 +problem function-size /src/feature/relay/router.c:router_dump_router_to_string() 367 problem function-size /src/feature/relay/routerkeys.c:load_ed_keys() 294 -problem function-size /src/feature/rend/rendcache.c:rend_cache_store_v2_desc_as_client() 193 -problem function-size /src/feature/rend/rendclient.c:rend_client_send_introduction() 220 -problem function-size /src/feature/rend/rendcommon.c:rend_encode_v2_descriptors() 225 -problem function-size /src/feature/rend/rendmid.c:rend_mid_establish_intro_legacy() 104 -problem function-size /src/feature/rend/rendparse.c:rend_parse_v2_service_descriptor() 187 -problem function-size /src/feature/rend/rendparse.c:rend_decrypt_introduction_points() 104 -problem function-size /src/feature/rend/rendparse.c:rend_parse_introduction_points() 131 +problem function-size /src/feature/rend/rendcache.c:rend_cache_store_v2_desc_as_client() 190 +problem function-size /src/feature/rend/rendclient.c:rend_client_send_introduction() 219 +problem function-size /src/feature/rend/rendcommon.c:rend_encode_v2_descriptors() 221 +problem function-size /src/feature/rend/rendmid.c:rend_mid_establish_intro_legacy() 103 +problem function-size /src/feature/rend/rendparse.c:rend_parse_v2_service_descriptor() 181 +problem function-size /src/feature/rend/rendparse.c:rend_parse_introduction_points() 129 problem file-size /src/feature/rend/rendservice.c 4511 problem function-size /src/feature/rend/rendservice.c:rend_service_prune_list_impl_() 107 -problem function-size /src/feature/rend/rendservice.c:rend_config_service() 164 +problem function-size /src/feature/rend/rendservice.c:rend_config_service() 162 problem function-size /src/feature/rend/rendservice.c:rend_service_load_auth_keys() 178 -problem function-size /src/feature/rend/rendservice.c:rend_service_receive_introduction() 332 -problem function-size /src/feature/rend/rendservice.c:rend_service_parse_intro_for_v3() 115 -problem function-size /src/feature/rend/rendservice.c:rend_service_decrypt_intro() 115 +problem function-size /src/feature/rend/rendservice.c:rend_service_receive_introduction() 330 +problem function-size /src/feature/rend/rendservice.c:rend_service_parse_intro_for_v3() 111 +problem function-size /src/feature/rend/rendservice.c:rend_service_decrypt_intro() 112 problem function-size /src/feature/rend/rendservice.c:rend_service_intro_has_opened() 126 problem function-size /src/feature/rend/rendservice.c:rend_service_rendezvous_has_opened() 117 -problem function-size /src/feature/rend/rendservice.c:directory_post_to_hs_dir() 108 +problem function-size /src/feature/rend/rendservice.c:directory_post_to_hs_dir() 106 problem function-size /src/feature/rend/rendservice.c:upload_service_descriptor() 111 problem function-size /src/feature/rend/rendservice.c:rend_consider_services_intro_points() 170 problem function-size /src/feature/stats/rephist.c:rep_hist_load_mtbf_data() 185 problem function-size /src/feature/stats/rephist.c:rep_hist_format_exit_stats() 148 -problem function-size /src/lib/compress/compress.c:tor_compress_impl() 133 -problem function-size /src/lib/compress/compress_zstd.c:tor_zstd_compress_process() 126 -problem function-size /src/lib/container/smartlist.c:smartlist_bsearch_idx() 109 +problem function-size /src/lib/compress/compress.c:tor_compress_impl() 127 +problem function-size /src/lib/compress/compress_zstd.c:tor_zstd_compress_process() 123 +problem function-size /src/lib/container/smartlist.c:smartlist_bsearch_idx() 107 problem function-size /src/lib/crypt_ops/crypto_rand.c:crypto_strongest_rand_syscall() 102 -problem function-size /src/lib/encoding/binascii.c:base64_encode() 107 -problem function-size /src/lib/encoding/confline.c:parse_config_line_from_str_verbose() 119 +problem function-size /src/lib/encoding/binascii.c:base64_encode() 106 +problem function-size /src/lib/encoding/confline.c:parse_config_line_from_str_verbose() 117 problem function-size /src/lib/encoding/cstring.c:unescape_string() 108 -problem function-size /src/lib/fs/dir.c:check_private_dir() 231 -problem function-size /src/lib/log/log.c:parse_log_severity_config() 101 +problem function-size /src/lib/fs/dir.c:check_private_dir() 230 problem function-size /src/lib/math/prob_distr.c:sample_uniform_interval() 145 -problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 198 -problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 111 +problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 194 +problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 110 problem function-size /src/lib/net/inaddr.c:tor_inet_pton() 107 -problem function-size /src/lib/net/resolve.c:tor_addr_lookup() 110 problem function-size /src/lib/net/socketpair.c:tor_ersatz_socketpair() 102 problem function-size /src/lib/osinfo/uname.c:get_uname() 116 problem function-size /src/lib/process/process_unix.c:process_unix_exec() 220 problem function-size /src/lib/process/process_win32.c:process_win32_exec() 133 -problem function-size /src/lib/process/process_win32.c:process_win32_create_pipe() 112 +problem function-size /src/lib/process/process_win32.c:process_win32_create_pipe() 109 problem function-size /src/lib/process/restrict.c:set_max_file_descriptors() 102 problem function-size /src/lib/process/setuid.c:switch_id() 156 problem function-size /src/lib/sandbox/sandbox.c:prot_strings() 104 problem function-size /src/lib/string/scanf.c:tor_vsscanf() 112 -problem function-size /src/lib/tls/tortls_nss.c:tor_tls_context_new() 153 -problem function-size /src/lib/tls/tortls_openssl.c:tor_tls_context_new() 171 -problem function-size /src/lib/tls/x509_nss.c:tor_tls_create_certificate_internal() 126 +problem function-size /src/lib/tls/tortls_nss.c:tor_tls_context_new() 152 +problem function-size /src/lib/tls/tortls_openssl.c:tor_tls_context_new() 170 +problem function-size /src/lib/tls/x509_nss.c:tor_tls_create_certificate_internal() 121 problem function-size /src/tools/tor-gencert.c:parse_commandline() 111 -problem function-size /src/tools/tor-resolve.c:build_socks5_resolve_request() 104 -problem function-size /src/tools/tor-resolve.c:do_resolve() 174 +problem function-size /src/tools/tor-resolve.c:build_socks5_resolve_request() 102 +problem function-size /src/tools/tor-resolve.c:do_resolve() 171 problem function-size /src/tools/tor-resolve.c:main() 112 diff --git a/scripts/maint/practracker/metrics.py b/scripts/maint/practracker/metrics.py index 5fa305a868..9f69b2ac1f 100644 --- a/scripts/maint/practracker/metrics.py +++ b/scripts/maint/practracker/metrics.py @@ -16,7 +16,7 @@ def get_include_count(f): """Get number of #include statements in the file""" include_count = 0 for line in f: - if re.match(r' *# *include', line): + if re.match(r'\s*#\s*include', line): include_count += 1 return include_count @@ -27,10 +27,13 @@ def get_function_lines(f): # Skip lines that look like they are defining functions with these # names: they aren't real function definitions. - REGEXP_CONFUSE_TERMS = {"MOCK_IMPL", "ENABLE_GCC_WARNINGS", "ENABLE_GCC_WARNING", "DUMMY_TYPECHECK_INSTANCE", + REGEXP_CONFUSE_TERMS = {"MOCK_IMPL", "MOCK_DECL", "HANDLE_DECL", + "ENABLE_GCC_WARNINGS", "ENABLE_GCC_WARNING", + "DUMMY_TYPECHECK_INSTANCE", "DISABLE_GCC_WARNING", "DISABLE_GCC_WARNINGS"} in_function = False + found_openbrace = False for lineno, line in enumerate(f): if not in_function: # find the start of a function @@ -41,10 +44,13 @@ def get_function_lines(f): continue func_start = lineno in_function = True - + elif not found_openbrace and line.startswith("{"): + found_openbrace = True + func_start = lineno else: # Find the end of a function if line.startswith("}"): - n_lines = lineno - func_start + n_lines = lineno - func_start + 1 in_function = False + found_openbrace = False yield (func_name, n_lines) diff --git a/scripts/maint/practracker/practracker.py b/scripts/maint/practracker/practracker.py index febb14639d..0fdfd4a40a 100755 --- a/scripts/maint/practracker/practracker.py +++ b/scripts/maint/practracker/practracker.py @@ -35,12 +35,20 @@ MAX_FILE_SIZE = 3000 # lines MAX_FUNCTION_SIZE = 100 # lines # Recommended number of #includes MAX_INCLUDE_COUNT = 50 +# Recommended file size for headers +MAX_H_FILE_SIZE = 500 +# Recommended include count for headers +MAX_H_INCLUDE_COUNT = 15 + +# Map from problem type to functions that adjust for tolerance +TOLERANCE_FNS = { + 'include-count': lambda n: int(n*1.1), + 'function-size': lambda n: int(n*1.1), + 'file-size': lambda n: int(n*1.02) +} ####################################################### -# ProblemVault singleton -ProblemVault = None - # The Tor source code topdir TOR_TOPDIR = None @@ -54,71 +62,59 @@ else: return open(fname, 'r', encoding='utf-8') def consider_file_size(fname, f): - """Consider file size issues for 'f' and return True if a new issue was found""" + """Consider the size of 'f' and yield an FileSizeItem for it. + """ file_size = metrics.get_file_len(f) - if file_size > MAX_FILE_SIZE: - p = problem.FileSizeProblem(fname, file_size) - return ProblemVault.register_problem(p) - return False + yield problem.FileSizeItem(fname, file_size) def consider_includes(fname, f): - """Consider #include issues for 'f' and return True if a new issue was found""" + """Consider the #include count in for 'f' and yield an IncludeCountItem + for it. + """ include_count = metrics.get_include_count(f) - if include_count > MAX_INCLUDE_COUNT: - p = problem.IncludeCountProblem(fname, include_count) - return ProblemVault.register_problem(p) - return False + yield problem.IncludeCountItem(fname, include_count) def consider_function_size(fname, f): - """Consider the function sizes for 'f' and return True if a new issue was found""" - found_new_issues = False + """yield a FunctionSizeItem for every function in f. + """ for name, lines in metrics.get_function_lines(f): - # Don't worry about functions within our limits - if lines <= MAX_FUNCTION_SIZE: - continue - - # That's a big function! Issue a problem! canonical_function_name = "%s:%s()" % (fname, name) - p = problem.FunctionSizeProblem(canonical_function_name, lines) - found_new_issues |= ProblemVault.register_problem(p) - - return found_new_issues + yield problem.FunctionSizeItem(canonical_function_name, lines) ####################################################### def consider_all_metrics(files_list): - """Consider metrics for all files, and return True if new issues were found""" - found_new_issues = False + """Consider metrics for all files, and yield a sequence of problem.Item + object for those issues.""" for fname in files_list: with open_file(fname) as f: - found_new_issues |= consider_metrics_for_file(fname, f) - return found_new_issues + for item in consider_metrics_for_file(fname, f): + yield item def consider_metrics_for_file(fname, f): """ - Consider the various metrics for file with filename 'fname' and file descriptor 'f'. - Return True if we found new issues. + Yield a sequence of problem.Item objects for all of the metrics in + 'f'. """ # Strip the useless part of the path if fname.startswith(TOR_TOPDIR): fname = fname[len(TOR_TOPDIR):] - found_new_issues = False - # Get file length - found_new_issues |= consider_file_size(fname, f) + for item in consider_file_size(fname, f): + yield item # Consider number of #includes f.seek(0) - found_new_issues |= consider_includes(fname, f) + for item in consider_includes(fname, f): + yield item # Get function length f.seek(0) - found_new_issues |= consider_function_size(fname, f) - - return found_new_issues + for item in consider_function_size(fname, f): + yield item HEADER="""\ # Welcome to the exceptions file for Tor's best-practices tracker! @@ -161,8 +157,24 @@ def main(argv): parser = argparse.ArgumentParser(prog=progname) parser.add_argument("--regen", action="store_true", help="Regenerate the exceptions file") + parser.add_argument("--list-overbroad", action="store_true", + help="List over-strict exceptions") parser.add_argument("--exceptions", help="Override the location for the exceptions file") + parser.add_argument("--strict", action="store_true", + help="Make all warnings into errors") + parser.add_argument("--terse", action="store_true", + help="Do not emit helpful instructions.") + parser.add_argument("--max-h-file-size", default=MAX_H_FILE_SIZE, + help="Maximum lines per .H file") + parser.add_argument("--max-h-include-count", default=MAX_H_INCLUDE_COUNT, + help="Maximum includes per .H file") + parser.add_argument("--max-file-size", default=MAX_FILE_SIZE, + help="Maximum lines per C file") + parser.add_argument("--max-include-count", default=MAX_INCLUDE_COUNT, + help="Maximum includes per C file") + parser.add_argument("--max-function-size", default=MAX_FUNCTION_SIZE, + help="Maximum lines per function") parser.add_argument("topdir", default=".", nargs="?", help="Top-level directory for the tor source") args = parser.parse_args(argv[1:]) @@ -174,24 +186,44 @@ def main(argv): else: exceptions_file = os.path.join(TOR_TOPDIR, "scripts/maint/practracker", EXCEPTIONS_FNAME) + # 0) Configure our thresholds of "what is a problem actually" + filt = problem.ProblemFilter() + filt.addThreshold(problem.FileSizeItem("*.c", int(args.max_file_size))) + filt.addThreshold(problem.IncludeCountItem("*.c", int(args.max_include_count))) + filt.addThreshold(problem.FileSizeItem("*.h", int(args.max_h_file_size))) + filt.addThreshold(problem.IncludeCountItem("*.h", int(args.max_h_include_count))) + filt.addThreshold(problem.FunctionSizeItem("*.c", int(args.max_function_size))) + # 1) Get all the .c files we care about files_list = util.get_tor_c_files(TOR_TOPDIR) # 2) Initialize problem vault and load an optional exceptions file so that # we don't warn about the past - global ProblemVault - if args.regen: tmpname = exceptions_file + ".tmp" tmpfile = open(tmpname, "w") - sys.stdout = tmpfile - sys.stdout.write(HEADER) + problem_file = tmpfile + problem_file.write(HEADER) ProblemVault = problem.ProblemVault() else: ProblemVault = problem.ProblemVault(exceptions_file) + problem_file = sys.stdout + + # 2.1) Adjust the exceptions so that we warn only about small problems, + # and produce errors on big ones. + if not (args.regen or args.list_overbroad or args.strict): + ProblemVault.set_tolerances(TOLERANCE_FNS) # 3) Go through all the files and report problems if they are not exceptions - found_new_issues = consider_all_metrics(files_list) + found_new_issues = 0 + for item in filt.filter(consider_all_metrics(files_list)): + status = ProblemVault.register_problem(item) + if status == problem.STATUS_ERR: + print(item, file=problem_file) + found_new_issues += 1 + elif status == problem.STATUS_WARN: + # warnings always go to stdout. + print("(warning) {}".format(item)) if args.regen: tmpfile.close() @@ -199,18 +231,32 @@ def main(argv): sys.exit(0) # If new issues were found, try to give out some advice to the developer on how to resolve it. - if found_new_issues and not args.regen: + if found_new_issues and not args.regen and not args.terse: new_issues_str = """\ -FAILURE: practracker found new problems in the code: see warnings above. +FAILURE: practracker found {} new problem(s) in the code: see warnings above. Please fix the problems if you can, and update the exceptions file ({}) if you can't. See doc/HACKING/HelpfulTools.md for more information on using practracker.\ -""".format(exceptions_file) + +You can disable this message by setting the TOR_DISABLE_PRACTRACKER environment +variable. +""".format(found_new_issues, exceptions_file) print(new_issues_str) + if args.list_overbroad: + def k_fn(tup): + return tup[0].key() + for (ex,p) in sorted(ProblemVault.list_overbroad_exceptions(), key=k_fn): + if p is None: + print(ex, "->", 0) + else: + print(ex, "->", p.metric_value) + sys.exit(found_new_issues) if __name__ == '__main__': + if os.environ.get("TOR_DISABLE_PRACTRACKER"): + sys.exit(0) main(sys.argv) diff --git a/scripts/maint/practracker/practracker_tests.py b/scripts/maint/practracker/practracker_tests.py index cdbab2908e..45719d6cb7 100755 --- a/scripts/maint/practracker/practracker_tests.py +++ b/scripts/maint/practracker/practracker_tests.py @@ -1,8 +1,15 @@ +#!/usr/bin/python + """Some simple tests for practracker metrics""" import unittest -import StringIO +try: + # python 2 names the module this way... + from StringIO import StringIO +except ImportError: + # python 3 names the module this way. + from io import StringIO import metrics @@ -36,15 +43,25 @@ fun,( class TestFunctionLength(unittest.TestCase): def test_function_length(self): - funcs = StringIO.StringIO(function_file) + funcs = StringIO(function_file) # All functions should have length 2 - for name, lines in metrics.function_lines(funcs): + for name, lines in metrics.get_function_lines(funcs): self.assertEqual(name, "fun") funcs.seek(0) - for name, lines in metrics.function_lines(funcs): - self.assertEqual(lines, 2) + for name, lines in metrics.get_function_lines(funcs): + self.assertEqual(lines, 4) + +class TestIncludeCount(unittest.TestCase): + def test_include_count(self): + f = StringIO(""" + # include <abc.h> + # include "def.h" +#include "ghi.h" +\t#\t include "jkl.h" +""") + self.assertEqual(metrics.get_include_count(f),4) if __name__ == '__main__': unittest.main() diff --git a/scripts/maint/practracker/problem.py b/scripts/maint/practracker/problem.py index c82c5db572..13c8e55143 100644 --- a/scripts/maint/practracker/problem.py +++ b/scripts/maint/practracker/problem.py @@ -13,6 +13,10 @@ import os.path import re import sys +STATUS_ERR = 2 +STATUS_WARN = 1 +STATUS_OK = 0 + class ProblemVault(object): """ Singleton where we store the various new problems we @@ -22,6 +26,9 @@ class ProblemVault(object): def __init__(self, exception_fname=None): # Exception dictionary: { problem.key() : Problem object } self.exceptions = {} + # Exception dictionary: maps key to the problem it was used to + # suppress. + self.used_exception_for = {} if exception_fname == None: return @@ -57,42 +64,91 @@ class ProblemVault(object): def register_problem(self, problem): """ - Register this problem to the problem value. Return True if it was a new - problem or it worsens an already existing problem. + Register this problem to the problem value. Return true if it was a new + problem or it worsens an already existing problem. A true + value may be STATUS_ERR to indicate a hard violation, or STATUS_WARN + to indicate a warning. """ # This is a new problem, print it if problem.key() not in self.exceptions: - print(problem) - return True + return STATUS_ERR # If it's an old problem, we don't warn if the situation got better # (e.g. we went from 4k LoC to 3k LoC), but we do warn if the # situation worsened (e.g. we went from 60 includes to 80). - if problem.is_worse_than(self.exceptions[problem.key()]): - print(problem) - return True + status = problem.is_worse_than(self.exceptions[problem.key()]) + if status == STATUS_OK: + self.used_exception_for[problem.key()] = problem - return False + return status -class Problem(object): + def list_overbroad_exceptions(self): + """Return an iterator of tuples containing (ex,prob) where ex is an + exceptions in this vault that are stricter than it needs to be, and + prob is the worst problem (if any) that it covered. + """ + for k in self.exceptions: + e = self.exceptions[k] + p = self.used_exception_for.get(k) + if p is None or e.is_worse_than(p): + yield (e, p) + + def set_tolerances(self, fns): + """Adjust the tolerances for the exceptions in this vault. Takes + a map of problem type to a function that adjusts the permitted + function to its new maximum value.""" + for k in self.exceptions: + ex = self.exceptions[k] + fn = fns.get(ex.problem_type) + if fn is not None: + ex.metric_value = fn(ex.metric_value) + +class ProblemFilter(object): + def __init__(self): + self.thresholds = dict() + + def addThreshold(self, item): + self.thresholds[(item.get_type(),item.get_file_type())] = item + + def matches(self, item): + key = (item.get_type(), item.get_file_type()) + filt = self.thresholds.get(key, None) + if filt is None: + return False + return item.is_worse_than(filt) + + def filter(self, sequence): + for item in iter(sequence): + if self.matches(item): + yield item + +class Item(object): """ - A generic problem in our source code. See the subclasses below for the - specific problems we are trying to tackle. + A generic measurement about some aspect of our source code. See + the subclasses below for the specific problems we are trying to tackle. """ def __init__(self, problem_type, problem_location, metric_value): self.problem_location = problem_location self.metric_value = int(metric_value) + self.warning_threshold = self.metric_value self.problem_type = problem_type def is_worse_than(self, other_problem): - """Return True if this is a worse problem than other_problem""" + """Return STATUS_ERR if this is a worse problem than other_problem. + Return STATUS_WARN if it is a little worse, but falls within the + warning threshold. Return STATUS_OK if this problem is not + at all worse than other_problem. + """ if self.metric_value > other_problem.metric_value: - return True - return False + return STATUS_ERR + elif self.metric_value > other_problem.warning_threshold: + return STATUS_WARN + else: + return STATUS_OK def key(self): """Generate a unique key that describes this problem that can be used as a dictionary key""" - # Problem location is a filesystem path, so we need to normalize this + # Item location is a filesystem path, so we need to normalize this # across platforms otherwise same paths are not gonna match. canonical_location = os.path.normcase(self.problem_location) return "%s:%s" % (canonical_location, self.problem_type) @@ -100,7 +156,16 @@ class Problem(object): def __str__(self): return "problem %s %s %s" % (self.problem_type, self.problem_location, self.metric_value) -class FileSizeProblem(Problem): + def get_type(self): + return self.problem_type + + def get_file_type(self): + if self.problem_location.endswith(".h"): + return "*.h" + else: + return "*.c" + +class FileSizeItem(Item): """ Denotes a problem with the size of a .c file. @@ -108,9 +173,9 @@ class FileSizeProblem(Problem): 'metric_value' is the number of lines in the .c file. """ def __init__(self, problem_location, metric_value): - super(FileSizeProblem, self).__init__("file-size", problem_location, metric_value) + super(FileSizeItem, self).__init__("file-size", problem_location, metric_value) -class IncludeCountProblem(Problem): +class IncludeCountItem(Item): """ Denotes a problem with the number of #includes in a .c file. @@ -118,9 +183,9 @@ class IncludeCountProblem(Problem): 'metric_value' is the number of #includes in the .c file. """ def __init__(self, problem_location, metric_value): - super(IncludeCountProblem, self).__init__("include-count", problem_location, metric_value) + super(IncludeCountItem, self).__init__("include-count", problem_location, metric_value) -class FunctionSizeProblem(Problem): +class FunctionSizeItem(Item): """ Denotes a problem with a size of a function in a .c file. @@ -131,7 +196,7 @@ class FunctionSizeProblem(Problem): The 'metric_value' is the size of the offending function in lines. """ def __init__(self, problem_location, metric_value): - super(FunctionSizeProblem, self).__init__("function-size", problem_location, metric_value) + super(FunctionSizeItem, self).__init__("function-size", problem_location, metric_value) comment_re = re.compile(r'#.*$') @@ -149,10 +214,10 @@ def get_old_problem_from_exception_str(exception_str): raise ValueError("Misformatted line {!r}".format(orig_str)) if problem_type == "file-size": - return FileSizeProblem(problem_location, metric_value) + return FileSizeItem(problem_location, metric_value) elif problem_type == "include-count": - return IncludeCountProblem(problem_location, metric_value) + return IncludeCountItem(problem_location, metric_value) elif problem_type == "function-size": - return FunctionSizeProblem(problem_location, metric_value) + return FunctionSizeItem(problem_location, metric_value) else: raise ValueError("Unknown exception type {!r}".format(orig_str)) diff --git a/scripts/maint/practracker/test_practracker.sh b/scripts/maint/practracker/test_practracker.sh new file mode 100755 index 0000000000..c878ca5580 --- /dev/null +++ b/scripts/maint/practracker/test_practracker.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +umask 077 + +TMPDIR="" +clean () { + if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then + rm -rf "$TMPDIR" + fi +} +trap clean EXIT HUP INT TERM + +if test "${PRACTRACKER_DIR}" = "" || + test ! -e "${PRACTRACKER_DIR}/practracker.py" ; then + PRACTRACKER_DIR=$(dirname "$0") +fi + +TMPDIR="$(mktemp -d -t pracktracker.test.XXXXXX)" +if test -z "${TMPDIR}" || test ! -d "${TMPDIR}" ; then + echo >&2 "mktemp failed." + exit 1; +fi + +DATA="${PRACTRACKER_DIR}/testdata" + +run_practracker() { + "${PYTHON:-python}" "${PRACTRACKER_DIR}/practracker.py" \ + --max-include-count=0 --max-file-size=0 --max-function-size=0 --terse \ + "${DATA}/" "$@"; +} +compare() { + # we can't use cmp because we need to use -b for windows + diff -b -u "$@" > "${TMPDIR}/test-diff" + if test -z "$(cat "${TMPDIR}"/test-diff)"; then + echo "OK" + else + cat "${TMPDIR}/test-diff" + echo "FAILED" + exit 1 + fi +} + +echo "unit tests:" + +"${PYTHON:-python}" "${PRACTRACKER_DIR}/practracker_tests.py" || exit 1 + +echo "ex0:" + +run_practracker --exceptions "${DATA}/ex0.txt" > "${TMPDIR}/ex0-received.txt" + +compare "${TMPDIR}/ex0-received.txt" "${DATA}/ex0-expected.txt" + +echo "ex1:" + +run_practracker --exceptions "${DATA}/ex1.txt" > "${TMPDIR}/ex1-received.txt" + +compare "${TMPDIR}/ex1-received.txt" "${DATA}/ex1-expected.txt" diff --git a/scripts/maint/practracker/testdata/a.c b/scripts/maint/practracker/testdata/a.c new file mode 100644 index 0000000000..b52a14f56a --- /dev/null +++ b/scripts/maint/practracker/testdata/a.c @@ -0,0 +1,38 @@ + +#include "one.h" +#include "two.h" +#incldue "three.h" + +# include "four.h" + +int +i_am_a_function(void) +{ + call(); + call(); + /* comment + + another */ + + return 3; +} + +# include "five.h" + +long +another_function(long x, + long y) +{ + int abcd; + + abcd = x+y; + abcd *= abcd; + + /* comment here */ + + return abcd + + abcd + + abcd; +} + +/* And a comment to grow! */ diff --git a/scripts/maint/practracker/testdata/b.c b/scripts/maint/practracker/testdata/b.c new file mode 100644 index 0000000000..bef277aaae --- /dev/null +++ b/scripts/maint/practracker/testdata/b.c @@ -0,0 +1,15 @@ + +MOCK_IMPL(int, +foo,(void)) +{ + // blah1 + return 0; +} + +MOCK_IMPL(int, +bar,( long z)) +{ + // blah2 + + return (int)(z+2); +} diff --git a/scripts/maint/practracker/testdata/ex.txt b/scripts/maint/practracker/testdata/ex.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/maint/practracker/testdata/ex.txt diff --git a/scripts/maint/practracker/testdata/ex0-expected.txt b/scripts/maint/practracker/testdata/ex0-expected.txt new file mode 100644 index 0000000000..c021e6f710 --- /dev/null +++ b/scripts/maint/practracker/testdata/ex0-expected.txt @@ -0,0 +1,7 @@ +problem file-size a.c 38 +problem include-count a.c 4 +problem function-size a.c:i_am_a_function() 9 +problem function-size a.c:another_function() 12 +problem file-size b.c 15 +problem function-size b.c:foo() 4 +problem function-size b.c:bar() 5 diff --git a/scripts/maint/practracker/testdata/ex0.txt b/scripts/maint/practracker/testdata/ex0.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/maint/practracker/testdata/ex0.txt diff --git a/scripts/maint/practracker/testdata/ex1-expected.txt b/scripts/maint/practracker/testdata/ex1-expected.txt new file mode 100644 index 0000000000..58140a4d9a --- /dev/null +++ b/scripts/maint/practracker/testdata/ex1-expected.txt @@ -0,0 +1,3 @@ +problem function-size a.c:i_am_a_function() 9 +(warning) problem function-size a.c:another_function() 12 +problem function-size b.c:foo() 4 diff --git a/scripts/maint/practracker/testdata/ex1.txt b/scripts/maint/practracker/testdata/ex1.txt new file mode 100644 index 0000000000..db42ae8450 --- /dev/null +++ b/scripts/maint/practracker/testdata/ex1.txt @@ -0,0 +1,11 @@ + +problem file-size a.c 38 +problem include-count a.c 4 +# this problem will produce an error +problem function-size a.c:i_am_a_function() 8 +# this problem will produce a warning +problem function-size a.c:another_function() 11 +problem file-size b.c 15 +# This is removed, and so will produce an error. +# problem function-size b.c:foo() 4 +problem function-size b.c:bar() 5 diff --git a/scripts/maint/practracker/testdata/not_c_file b/scripts/maint/practracker/testdata/not_c_file new file mode 100644 index 0000000000..e150962c02 --- /dev/null +++ b/scripts/maint/practracker/testdata/not_c_file @@ -0,0 +1,2 @@ + +This isn't a C file, so practracker shouldn't care about it. diff --git a/scripts/maint/practracker/util.py b/scripts/maint/practracker/util.py index b0ca73b997..695668f561 100644 --- a/scripts/maint/practracker/util.py +++ b/scripts/maint/practracker/util.py @@ -2,26 +2,36 @@ import os # We don't want to run metrics for unittests, automatically-generated C files, # external libraries or git leftovers. -EXCLUDE_SOURCE_DIRS = {"/src/test/", "/src/trunnel/", "/src/ext/", "/.git/"} +EXCLUDE_SOURCE_DIRS = {"src/test/", "src/trunnel/", "src/rust/", + "src/ext/", ".git/"} + +EXCLUDE_FILES = {"orconfig.h"} + +def _norm(p): + return os.path.normcase(os.path.normpath(p)) def get_tor_c_files(tor_topdir): """ - Return a list with the .c filenames we want to get metrics of. + Return a list with the .c and .h filenames we want to get metrics of. """ files_list = [] + exclude_dirs = { _norm(os.path.join(tor_topdir, p)) for p in EXCLUDE_SOURCE_DIRS } + for root, directories, filenames in os.walk(tor_topdir): + # Remove all the directories that are excluded. + directories[:] = [ d for d in directories + if _norm(os.path.join(root,d)) not in exclude_dirs ] directories.sort() filenames.sort() for filename in filenames: - # We only care about .c files - if not filename.endswith(".c"): + # We only care about .c and .h files + if not (filename.endswith(".c") or filename.endswith(".h")): + continue + if filename in EXCLUDE_FILES: continue - # Exclude the excluded paths full_path = os.path.join(root,filename) - if any(os.path.normcase(exclude_dir) in full_path for exclude_dir in EXCLUDE_SOURCE_DIRS): - continue files_list.append(full_path) diff --git a/src/app/config/config.c b/src/app/config/config.c index a061871748..7908007051 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -317,7 +317,7 @@ static config_var_t option_vars_[] = { OBSOLETE("AuthDirRejectUnlisted"), OBSOLETE("AuthDirListBadDirs"), V(AuthDirListBadExits, BOOL, "0"), - V(AuthDirMaxServersPerAddr, UINT, "2"), + V(AuthDirMaxServersPerAddr, POSINT, "2"), OBSOLETE("AuthDirMaxServersPerAuthAddr"), V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), @@ -352,7 +352,7 @@ static config_var_t option_vars_[] = { V(ClientUseIPv6, BOOL, "0"), V(ClientUseIPv4, BOOL, "1"), V(ConsensusParams, STRING, NULL), - V(ConnLimit, UINT, "1000"), + V(ConnLimit, POSINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), @@ -402,14 +402,14 @@ static config_var_t option_vars_[] = { V(DormantCanceledByStartup, BOOL, "0"), /* DoS circuit creation options. */ V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"), - V(DoSCircuitCreationMinConnections, UINT, "0"), - V(DoSCircuitCreationRate, UINT, "0"), - V(DoSCircuitCreationBurst, UINT, "0"), + V(DoSCircuitCreationMinConnections, POSINT, "0"), + V(DoSCircuitCreationRate, POSINT, "0"), + V(DoSCircuitCreationBurst, POSINT, "0"), V(DoSCircuitCreationDefenseType, INT, "0"), V(DoSCircuitCreationDefenseTimePeriod, INTERVAL, "0"), /* DoS connection options. */ V(DoSConnectionEnabled, AUTOBOOL, "auto"), - V(DoSConnectionMaxConcurrentCount, UINT, "0"), + V(DoSConnectionMaxConcurrentCount, POSINT, "0"), V(DoSConnectionDefenseType, INT, "0"), /* DoS single hop client options. */ V(DoSRefuseSingleHopClientRendezvous, AUTOBOOL, "auto"), @@ -522,7 +522,7 @@ static config_var_t option_vars_[] = { VAR("MapAddress", LINELIST, AddressMap, NULL), V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"), V(MaxCircuitDirtiness, INTERVAL, "10 minutes"), - V(MaxClientCircuitsPending, UINT, "32"), + V(MaxClientCircuitsPending, POSINT, "32"), V(MaxConsensusAgeForDiffs, INTERVAL, "0 seconds"), VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"), OBSOLETE("MaxOnionsPending"), @@ -539,10 +539,10 @@ static config_var_t option_vars_[] = { OBSOLETE("WarnUnsafeSocks"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), V(NoExec, BOOL, "0"), - V(NumCPUs, UINT, "0"), - V(NumDirectoryGuards, UINT, "0"), - V(NumEntryGuards, UINT, "0"), - V(NumPrimaryGuards, UINT, "0"), + V(NumCPUs, POSINT, "0"), + V(NumDirectoryGuards, POSINT, "0"), + V(NumEntryGuards, POSINT, "0"), + V(NumPrimaryGuards, POSINT, "0"), V(OfflineMasterKey, BOOL, "0"), OBSOLETE("ORListenAddress"), VPORT(ORPort), @@ -593,7 +593,7 @@ static config_var_t option_vars_[] = { V(RecommendedVersions, LINELIST, NULL), V(RecommendedClientVersions, LINELIST, NULL), V(RecommendedServerVersions, LINELIST, NULL), - V(RecommendedPackages, LINELIST, NULL), + OBSOLETE("RecommendedPackages"), V(ReducedConnectionPadding, BOOL, "0"), V(ConnectionPadding, AUTOBOOL, "auto"), V(RefuseUnknownExits, AUTOBOOL, "auto"), @@ -666,7 +666,7 @@ static config_var_t option_vars_[] = { V(V3AuthVotingInterval, INTERVAL, "1 hour"), V(V3AuthVoteDelay, INTERVAL, "5 minutes"), V(V3AuthDistDelay, INTERVAL, "5 minutes"), - V(V3AuthNIntervalsValid, UINT, "3"), + V(V3AuthNIntervalsValid, POSINT, "3"), V(V3AuthUseLegacyKey, BOOL, "0"), V(V3BandwidthsFile, FILENAME, NULL), V(GuardfractionFile, FILENAME, NULL), @@ -715,7 +715,7 @@ static config_var_t option_vars_[] = { * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ - V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), + V(ClientBootstrapConsensusMaxInProgressTries, POSINT, "3"), /* When a client has any running bridges, check each bridge occasionally, * whether or not that bridge is actually up. */ V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"), @@ -749,7 +749,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(DirAllowPrivateAddresses, BOOL, "1"), V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), - V(AuthDirMaxServersPerAddr, UINT, "0"), + V(AuthDirMaxServersPerAddr, POSINT, "0"), V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"), V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, @@ -3522,13 +3522,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "features to be broken in unpredictable ways."); } - for (cl = options->RecommendedPackages; cl; cl = cl->next) { - if (! validate_recommended_package_line(cl->value)) { - log_warn(LD_CONFIG, "Invalid RecommendedPackage line %s will be ignored", - escaped(cl->value)); - } - } - if (options->AuthoritativeDir) { if (!options->ContactInfo && !options->TestingTorNetwork) REJECT("Authoritative directory servers must set ContactInfo"); @@ -8184,10 +8177,9 @@ getinfo_helper_config(control_connection_t *conn, switch (var->type) { case CONFIG_TYPE_STRING: type = "String"; break; case CONFIG_TYPE_FILENAME: type = "Filename"; break; - case CONFIG_TYPE_UINT: type = "Integer"; break; + case CONFIG_TYPE_POSINT: type = "Integer"; break; case CONFIG_TYPE_UINT64: type = "Integer"; break; case CONFIG_TYPE_INT: type = "SignedInteger"; break; - case CONFIG_TYPE_PORT: type = "Port"; break; case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break; case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c index 729e7a4478..bc2ab24e4f 100644 --- a/src/app/config/confparse.c +++ b/src/app/config/confparse.c @@ -21,16 +21,17 @@ * specified, and a linked list of key-value pairs. */ +#define CONFPARSE_PRIVATE #include "core/or/or.h" #include "app/config/confparse.h" #include "feature/nodelist/routerset.h" +#include "lib/confmgt/unitparse.h" #include "lib/container/bitarray.h" #include "lib/encoding/confline.h" -static uint64_t config_parse_memunit(const char *s, int *ok); -static int config_parse_msec_interval(const char *s, int *ok); -static int config_parse_interval(const char *s, int *ok); +#include "lib/confmgt/typedvar.h" + static void config_reset(const config_format_t *fmt, void *options, const config_var_t *var, int use_defaults); @@ -87,7 +88,7 @@ const char * config_find_deprecation(const config_format_t *fmt, const char *key) { if (BUG(fmt == NULL) || BUG(key == NULL)) - return NULL; + return NULL; // LCOV_EXCL_LINE if (fmt->deprecations == NULL) return NULL; @@ -161,7 +162,6 @@ static int config_assign_value(const config_format_t *fmt, void *options, config_line_t *c, char **msg) { - int i, ok; const config_var_t *var; void *lvalue; @@ -169,150 +169,14 @@ config_assign_value(const config_format_t *fmt, void *options, var = config_find_option(fmt, c->key); tor_assert(var); + tor_assert(!strcmp(c->key, var->name)); lvalue = STRUCT_VAR_P(options, var->var_offset); - switch (var->type) { - - case CONFIG_TYPE_PORT: - if (!strcasecmp(c->value, "auto")) { - *(int *)lvalue = CFG_AUTO_PORT; - break; - } - /* fall through */ - case CONFIG_TYPE_INT: - case CONFIG_TYPE_UINT: - i = (int)tor_parse_long(c->value, 10, - var->type==CONFIG_TYPE_INT ? INT_MIN : 0, - var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, - &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Int keyword '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_UINT64: { - uint64_t u64 = tor_parse_uint64(c->value, 10, - 0, UINT64_MAX, &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "uint64 keyword '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(uint64_t *)lvalue = u64; - break; - } - - case CONFIG_TYPE_CSV_INTERVAL: { - /* We used to have entire smartlists here. But now that all of our - * download schedules use exponential backoff, only the first part - * matters. */ - const char *comma = strchr(c->value, ','); - const char *val = c->value; - char *tmp = NULL; - if (comma) { - tmp = tor_strndup(c->value, comma - c->value); - val = tmp; - } + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. - i = config_parse_interval(val, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - tor_free(tmp); - return -1; - } - *(int *)lvalue = i; - tor_free(tmp); - break; - } - - case CONFIG_TYPE_INTERVAL: { - i = config_parse_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MSEC_INTERVAL: { - i = config_parse_msec_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Msec interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MEMUNIT: { - uint64_t u64 = config_parse_memunit(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Value '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(uint64_t *)lvalue = u64; - break; - } - - case CONFIG_TYPE_BOOL: - i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Boolean '%s %s' expects 0 or 1.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_AUTOBOOL: - if (!strcasecmp(c->value, "auto")) - *(int *)lvalue = -1; - else if (!strcmp(c->value, "0")) - *(int *)lvalue = 0; - else if (!strcmp(c->value, "1")) - *(int *)lvalue = 1; - else { - tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.", - c->key, c->value); - return -1; - } - break; - - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char **)lvalue); - *(char **)lvalue = tor_strdup(c->value); - break; - - case CONFIG_TYPE_DOUBLE: - *(double *)lvalue = atof(c->value); - break; - - case CONFIG_TYPE_ISOTIME: - if (parse_iso_time(c->value, (time_t *)lvalue)) { - tor_asprintf(msg, - "Invalid time '%s' for keyword '%s'", c->value, c->key); - return -1; - } - break; - - case CONFIG_TYPE_ROUTERSET: if (*(routerset_t**)lvalue) { routerset_free(*(routerset_t**)lvalue); } @@ -322,48 +186,10 @@ config_assign_value(const config_format_t *fmt, void *options, c->value, c->key); return -1; } - break; - - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); - smartlist_clear(*(smartlist_t**)lvalue); - } else { - *(smartlist_t**)lvalue = smartlist_new(); - } - - smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - break; - - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - { - config_line_t *lastval = *(config_line_t**)lvalue; - if (lastval && lastval->fragile) { - if (c->command != CONFIG_LINE_APPEND) { - config_free_lines(lastval); - *(config_line_t**)lvalue = NULL; - } else { - lastval->fragile = 0; - } - } - - config_line_append((config_line_t**)lvalue, c->key, c->value); - } - break; - case CONFIG_TYPE_OBSOLETE: - log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); - break; - case CONFIG_TYPE_LINELIST_V: - tor_asprintf(msg, - "You may not provide a value for virtual option '%s'", c->key); - return -1; - default: - tor_assert(0); - break; + return 0; } - return 0; + + return typed_var_kvassign(lvalue, c, msg, var->type); } /** Mark every linelist in <b>options</b> "fragile", so that fresh assignments @@ -462,7 +288,9 @@ config_assign_line(const config_format_t *fmt, void *options, } return 0; } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { - config_reset(fmt, options, var, use_defaults); + // XXXX This is unreachable, since a CLEAR line always has an + // XXXX empty value. + config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -484,7 +312,7 @@ config_assign_line(const config_format_t *fmt, void *options, /** Restore the option named <b>key</b> in options to its default value. * Called from config_assign(). */ -static void +STATIC void config_reset_line(const config_format_t *fmt, void *options, const char *key, int use_defaults) { @@ -547,105 +375,15 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, } value = STRUCT_VAR_P(options, var->var_offset); - result = tor_malloc_zero(sizeof(config_line_t)); - result->key = tor_strdup(var->name); - switch (var->type) - { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - if (*(char**)value) { - result->value = tor_strdup(*(char**)value); - } else { - tor_free(result->key); - tor_free(result); - return NULL; - } - break; - case CONFIG_TYPE_ISOTIME: - if (*(time_t*)value) { - result->value = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(result->value, *(time_t*)value); - } else { - tor_free(result->key); - tor_free(result); - } - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_PORT: - if (*(int*)value == CFG_AUTO_PORT) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_CSV_INTERVAL: - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - /* This means every or_options_t uint or bool element - * needs to be an int. Not, say, a uint16_t or char. */ - tor_asprintf(&result->value, "%d", *(int*)value); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_UINT64: /* Fall through */ - case CONFIG_TYPE_MEMUNIT: - tor_asprintf(&result->value, "%"PRIu64, - (*(uint64_t*)value)); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_DOUBLE: - tor_asprintf(&result->value, "%f", *(double*)value); - escape_val = 0; /* Can't need escape. */ - break; - - case CONFIG_TYPE_AUTOBOOL: - if (*(int*)value == -1) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_BOOL: - result->value = tor_strdup(*(int*)value ? "1" : "0"); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_ROUTERSET: - result->value = routerset_to_string(*(routerset_t**)value); - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)value) - result->value = - smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); - else - result->value = tor_strdup(""); - break; - case CONFIG_TYPE_OBSOLETE: - log_fn(LOG_INFO, LD_CONFIG, - "You asked me for the value of an obsolete config option '%s'.", - key); - tor_free(result->key); - tor_free(result); - return NULL; - case CONFIG_TYPE_LINELIST_S: - tor_free(result->key); - tor_free(result); - result = config_lines_dup_and_filter(*(const config_line_t **)value, - key); - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_V: - tor_free(result->key); - tor_free(result); - result = config_lines_dup(*(const config_line_t**)value); - break; - default: - tor_free(result->key); - tor_free(result); - log_warn(LD_BUG,"Unknown type %d for known key '%s'", - var->type, key); - return NULL; - } + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. + result = tor_malloc_zero(sizeof(config_line_t)); + result->key = tor_strdup(var->name); + result->value = routerset_to_string(*(routerset_t**)value); + } else { + result = typed_var_kvencode(var->name, value, var->type); + } if (escape_val) { config_line_t *line; @@ -773,57 +511,17 @@ config_clear(const config_format_t *fmt, void *options, { void *lvalue = STRUCT_VAR_P(options, var->var_offset); (void)fmt; /* unused */ - switch (var->type) { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char**)lvalue); - break; - case CONFIG_TYPE_DOUBLE: - *(double*)lvalue = 0.0; - break; - case CONFIG_TYPE_ISOTIME: - *(time_t*)lvalue = 0; - break; - case CONFIG_TYPE_CSV_INTERVAL: - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - case CONFIG_TYPE_PORT: - case CONFIG_TYPE_BOOL: - *(int*)lvalue = 0; - break; - case CONFIG_TYPE_AUTOBOOL: - *(int*)lvalue = -1; - break; - case CONFIG_TYPE_UINT64: - case CONFIG_TYPE_MEMUNIT: - *(uint64_t*)lvalue = 0; - break; - case CONFIG_TYPE_ROUTERSET: - if (*(routerset_t**)lvalue) { - routerset_free(*(routerset_t**)lvalue); - *(routerset_t**)lvalue = NULL; - } - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; - } - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - config_free_lines(*(config_line_t **)lvalue); - *(config_line_t **)lvalue = NULL; - break; - case CONFIG_TYPE_LINELIST_V: - /* handled by linelist_s. */ - break; - case CONFIG_TYPE_OBSOLETE: - break; + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. + if (*(routerset_t**)lvalue) { + routerset_free(*(routerset_t**)lvalue); + *(routerset_t**)lvalue = NULL; + } + return; } + + typed_var_free(lvalue, var->type); } /** Clear the option indexed by <b>var</b> in <b>options</b>. Then if @@ -844,8 +542,10 @@ config_reset(const config_format_t *fmt, void *options, c->key = tor_strdup(var->name); c->value = tor_strdup(var->initvalue); if (config_assign_value(fmt, options, c, &msg) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Failed to assign default: %s", msg); tor_free(msg); /* if this happens it's a bug */ + // LCOV_EXCL_STOP } config_free_lines(c); } @@ -911,10 +611,12 @@ config_dup(const config_format_t *fmt, const void *old) if (line) { char *msg = NULL; if (config_assign(fmt, newopts, line, 0, &msg) < 0) { + // LCOV_EXCL_START log_err(LD_BUG, "config_get_assigned_option() generated " "something we couldn't config_assign(): %s", msg); tor_free(msg); tor_assert(0); + // LCOV_EXCL_STOP } } config_free_lines(line); @@ -963,9 +665,11 @@ config_dump(const config_format_t *fmt, const void *default_options, /* XXX use a 1 here so we don't add a new log line while dumping */ if (default_options == NULL) { if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) { + // LCOV_EXCL_START log_err(LD_BUG, "Failed to validate default config: %s", msg); tor_free(msg); tor_assert(0); + // LCOV_EXCL_STOP } } @@ -1015,193 +719,3 @@ config_dump(const config_format_t *fmt, const void *default_options, } return result; } - -/** Mapping from a unit name to a multiplier for converting that unit into a - * base unit. Used by config_parse_unit. */ -struct unit_table_t { - const char *unit; /**< The name of the unit */ - uint64_t multiplier; /**< How many of the base unit appear in this unit */ -}; - -/** Table to map the names of memory units to the number of bytes they - * contain. */ -static struct unit_table_t memory_units[] = { - { "", 1 }, - { "b", 1<< 0 }, - { "byte", 1<< 0 }, - { "bytes", 1<< 0 }, - { "kb", 1<<10 }, - { "kbyte", 1<<10 }, - { "kbytes", 1<<10 }, - { "kilobyte", 1<<10 }, - { "kilobytes", 1<<10 }, - { "kilobits", 1<<7 }, - { "kilobit", 1<<7 }, - { "kbits", 1<<7 }, - { "kbit", 1<<7 }, - { "m", 1<<20 }, - { "mb", 1<<20 }, - { "mbyte", 1<<20 }, - { "mbytes", 1<<20 }, - { "megabyte", 1<<20 }, - { "megabytes", 1<<20 }, - { "megabits", 1<<17 }, - { "megabit", 1<<17 }, - { "mbits", 1<<17 }, - { "mbit", 1<<17 }, - { "gb", 1<<30 }, - { "gbyte", 1<<30 }, - { "gbytes", 1<<30 }, - { "gigabyte", 1<<30 }, - { "gigabytes", 1<<30 }, - { "gigabits", 1<<27 }, - { "gigabit", 1<<27 }, - { "gbits", 1<<27 }, - { "gbit", 1<<27 }, - { "tb", UINT64_C(1)<<40 }, - { "tbyte", UINT64_C(1)<<40 }, - { "tbytes", UINT64_C(1)<<40 }, - { "terabyte", UINT64_C(1)<<40 }, - { "terabytes", UINT64_C(1)<<40 }, - { "terabits", UINT64_C(1)<<37 }, - { "terabit", UINT64_C(1)<<37 }, - { "tbits", UINT64_C(1)<<37 }, - { "tbit", UINT64_C(1)<<37 }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of seconds they - * contain. */ -static struct unit_table_t time_units[] = { - { "", 1 }, - { "second", 1 }, - { "seconds", 1 }, - { "minute", 60 }, - { "minutes", 60 }, - { "hour", 60*60 }, - { "hours", 60*60 }, - { "day", 24*60*60 }, - { "days", 24*60*60 }, - { "week", 7*24*60*60 }, - { "weeks", 7*24*60*60 }, - { "month", 2629728, }, /* about 30.437 days */ - { "months", 2629728, }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of milliseconds - * they contain. */ -static struct unit_table_t time_msec_units[] = { - { "", 1 }, - { "msec", 1 }, - { "millisecond", 1 }, - { "milliseconds", 1 }, - { "second", 1000 }, - { "seconds", 1000 }, - { "minute", 60*1000 }, - { "minutes", 60*1000 }, - { "hour", 60*60*1000 }, - { "hours", 60*60*1000 }, - { "day", 24*60*60*1000 }, - { "days", 24*60*60*1000 }, - { "week", 7*24*60*60*1000 }, - { "weeks", 7*24*60*60*1000 }, - { NULL, 0 }, -}; - -/** Parse a string <b>val</b> containing a number, zero or more - * spaces, and an optional unit string. If the unit appears in the - * table <b>u</b>, then multiply the number by the unit multiplier. - * On success, set *<b>ok</b> to 1 and return this product. - * Otherwise, set *<b>ok</b> to 0. - */ -static uint64_t -config_parse_units(const char *val, struct unit_table_t *u, int *ok) -{ - uint64_t v = 0; - double d = 0; - int use_float = 0; - char *cp; - - tor_assert(ok); - - v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); - if (!*ok || (cp && *cp == '.')) { - d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); - if (!*ok) - goto done; - use_float = 1; - } - - if (!cp) { - *ok = 1; - v = use_float ? ((uint64_t)d) : v; - goto done; - } - - cp = (char*) eat_whitespace(cp); - - for ( ;u->unit;++u) { - if (!strcasecmp(u->unit, cp)) { - if (use_float) - v = (uint64_t)(u->multiplier * d); - else - v *= u->multiplier; - *ok = 1; - goto done; - } - } - log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); - *ok = 0; - done: - - if (*ok) - return v; - else - return 0; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * information (byte, KB, M, etc). On success, set *<b>ok</b> to true - * and return the number of bytes specified. Otherwise, set - * *<b>ok</b> to false and return 0. */ -static uint64_t -config_parse_memunit(const char *s, int *ok) -{ - uint64_t u = config_parse_units(s, memory_units, ok); - return u; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * time in milliseconds. On success, set *<b>ok</b> to true and return - * the number of milliseconds in the provided interval. Otherwise, set - * *<b>ok</b> to 0 and return -1. */ -static int -config_parse_msec_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_msec_units, ok); - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} - -/** Parse a string in the format "number unit", where unit is a unit of time. - * On success, set *<b>ok</b> to true and return the number of seconds in - * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. - */ -static int -config_parse_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_units, ok); - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} diff --git a/src/app/config/confparse.h b/src/app/config/confparse.h index 57f1ec1762..bd06a4a0d0 100644 --- a/src/app/config/confparse.h +++ b/src/app/config/confparse.h @@ -13,72 +13,7 @@ #ifndef TOR_CONFPARSE_H #define TOR_CONFPARSE_H -/** Enumeration of types which option values can take */ -typedef enum config_type_t { - CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ - CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ - CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ - CONFIG_TYPE_INT, /**< Any integer. */ - CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ - CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or - * "auto". */ - CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ - CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional - * units */ - CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ - CONFIG_TYPE_DOUBLE, /**< A floating-point value */ - CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ - CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, - * 1 for true, and -1 for auto */ - CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ - CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and - * optional whitespace. */ - CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and - * optional whitespace, representing intervals in - * seconds, with optional units. We allow - * multiple values here for legacy reasons, but - * ignore every value after the first. */ - CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ - CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, - * mixed with other keywords. */ - CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize - * context-sensitive config lines when fetching. - */ - CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, - * parsed into a routerset_t. */ - CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ -} config_type_t; - -#ifdef TOR_UNIT_TESTS -/** - * Union used when building in test mode typechecking the members of a type - * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how - * it is used. */ -typedef union { - char **STRING; - char **FILENAME; - int *UINT; /* yes, really: Even though the confparse type is called - * "UINT", it still uses the C int type -- it just enforces that - * the values are in range [0,INT_MAX]. - */ - uint64_t *UINT64; - int *INT; - int *PORT; - int *INTERVAL; - int *MSEC_INTERVAL; - uint64_t *MEMUNIT; - double *DOUBLE; - int *BOOL; - int *AUTOBOOL; - time_t *ISOTIME; - smartlist_t **CSV; - int *CSV_INTERVAL; - struct config_line_t **LINELIST; - struct config_line_t **LINELIST_S; - struct config_line_t **LINELIST_V; - routerset_t **ROUTERSET; -} confparse_dummy_values_t; -#endif /* defined(TOR_UNIT_TESTS) */ +#include "lib/conf/conftypes.h" /** An abbreviation for a configuration option allowed on the command line. */ typedef struct config_abbrev_t { @@ -230,4 +165,9 @@ void warn_deprecated_option(const char *what, const char *why); #define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt) #define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt) +#ifdef CONFPARSE_PRIVATE +STATIC void config_reset_line(const config_format_t *fmt, void *options, + const char *key, int use_defaults); +#endif + #endif /* !defined(TOR_CONFPARSE_H) */ diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 2ee2d15674..8156d2ca11 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -121,7 +121,6 @@ struct or_options_t { struct config_line_t *RecommendedVersions; struct config_line_t *RecommendedClientVersions; struct config_line_t *RecommendedServerVersions; - struct config_line_t *RecommendedPackages; /** Whether dirservers allow router descriptors with private IPs. */ int DirAllowPrivateAddresses; /** Whether routers accept EXTEND cells to routers with private IPs. */ diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index fdfd68b244..c6c5ec14f5 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -105,19 +105,19 @@ static config_var_t state_vars_[] = { V(HidServRevCounter, LINELIST, NULL), V(BWHistoryReadEnds, ISOTIME, NULL), - V(BWHistoryReadInterval, UINT, "900"), + V(BWHistoryReadInterval, POSINT, "900"), V(BWHistoryReadValues, CSV, ""), V(BWHistoryReadMaxima, CSV, ""), V(BWHistoryWriteEnds, ISOTIME, NULL), - V(BWHistoryWriteInterval, UINT, "900"), + V(BWHistoryWriteInterval, POSINT, "900"), V(BWHistoryWriteValues, CSV, ""), V(BWHistoryWriteMaxima, CSV, ""), V(BWHistoryDirReadEnds, ISOTIME, NULL), - V(BWHistoryDirReadInterval, UINT, "900"), + V(BWHistoryDirReadInterval, POSINT, "900"), V(BWHistoryDirReadValues, CSV, ""), V(BWHistoryDirReadMaxima, CSV, ""), V(BWHistoryDirWriteEnds, ISOTIME, NULL), - V(BWHistoryDirWriteInterval, UINT, "900"), + V(BWHistoryDirWriteInterval, POSINT, "900"), V(BWHistoryDirWriteValues, CSV, ""), V(BWHistoryDirWriteMaxima, CSV, ""), @@ -128,12 +128,12 @@ static config_var_t state_vars_[] = { V(LastRotatedOnionKey, ISOTIME, NULL), V(LastWritten, ISOTIME, NULL), - V(TotalBuildTimes, UINT, NULL), - V(CircuitBuildAbandonedCount, UINT, "0"), + V(TotalBuildTimes, POSINT, NULL), + V(CircuitBuildAbandonedCount, POSINT, "0"), VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), - V(MinutesSinceUserActivity, UINT, NULL), + V(MinutesSinceUserActivity, POSINT, NULL), V(Dormant, AUTOBOOL, "auto"), END_OF_CONFIG_VARS diff --git a/src/app/main/main.c b/src/app/main/main.c index 6e325f0b10..3bdf8f146b 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -41,6 +41,7 @@ #include "feature/dircache/consdiffmgr.h" #include "feature/dirparse/routerparse.h" #include "feature/hibernate/hibernate.h" +#include "feature/hs/hs_dos.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/routerlist.h" @@ -637,6 +638,10 @@ tor_init(int argc, char *argv[]) /* Initialize circuit padding to defaults+torrc until we get a consensus */ circpad_machines_init(); + /* Initialize hidden service DoS subsystem. We need to do this once the + * configuration object has been set because it can be accessed. */ + hs_dos_init(); + /* Initialize predicted ports list after loading options */ predicted_ports_init(); @@ -653,10 +658,6 @@ tor_init(int argc, char *argv[]) return -1; } - if (tor_init_libevent_rng() < 0) { - log_warn(LD_NET, "Problem initializing libevent RNG."); - } - /* Scan/clean unparseable descriptors; after reading config */ routerparse_init(); @@ -1256,6 +1257,8 @@ pubsub_connect(void) /* XXXX For each pubsub channel, its delivery strategy should be set at * this XXXX point, using tor_mainloop_set_delivery_strategy(). */ + tor_mainloop_set_delivery_strategy("orconn", DELIV_IMMEDIATE); + tor_mainloop_set_delivery_strategy("ocirc", DELIV_IMMEDIATE); } } diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c index f00b712702..a2de5bb87e 100644 --- a/src/app/main/ntmain.c +++ b/src/app/main/ntmain.c @@ -608,6 +608,7 @@ nt_service_install(int argc, char **argv) &sidUse) == 0) { /* XXXX For some reason, the above test segfaults. Fix that. */ printf("User \"%s\" doesn't seem to exist.\n", user_acct); + tor_free(command); return -1; } else { printf("Will try to install service as user \"%s\".\n", user_acct); diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c index cc0091a9ab..93d6351d1b 100644 --- a/src/app/main/shutdown.c +++ b/src/app/main/shutdown.c @@ -160,8 +160,6 @@ tor_free_all(int postfork) subsystems_shutdown(); - tor_libevent_free_all(); - /* Stuff in util.c and address.c*/ if (!postfork) { esc_router_info(NULL); diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c index f595796232..95d96f78d2 100644 --- a/src/app/main/subsystem_list.c +++ b/src/app/main/subsystem_list.c @@ -25,6 +25,7 @@ #include "lib/time/time_sys.h" #include "lib/tls/tortls_sys.h" #include "lib/wallclock/wallclock_sys.h" +#include "lib/evloop/evloop_sys.h" #include "feature/dirauth/dirauth_sys.h" @@ -50,6 +51,8 @@ const subsys_fns_t *tor_subsystems[] = { &sys_ocirc_event, /* -32 */ &sys_btrack, /* -30 */ + &sys_evloop, /* -20 */ + &sys_mainloop, /* 5 */ &sys_or, /* 20 */ diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging index cb3adca35c..90bad7f7cc 100644 --- a/src/config/torrc.minimal.in-staging +++ b/src/config/torrc.minimal.in-staging @@ -88,6 +88,9 @@ ## yourself to make this work. #ORPort 443 NoListen #ORPort 127.0.0.1:9090 NoAdvertise +## If you want to listen on IPv6 your numeric address must be explictly +## between square brackets as follows. You must also listen on IPv4. +#ORPort [2001:DB8::1]:9050 ## The IP address or full DNS name for incoming connections to your ## relay. Leave commented out and Tor will guess. diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in index 9d514e6bda..51e1c3af4b 100644 --- a/src/config/torrc.sample.in +++ b/src/config/torrc.sample.in @@ -88,6 +88,9 @@ ## yourself to make this work. #ORPort 443 NoListen #ORPort 127.0.0.1:9090 NoAdvertise +## If you want to listen on IPv6 your numeric address must be explictly +## between square brackets as follows. You must also listen on IPv4. +#ORPort [2001:DB8::1]:9050 ## The IP address or full DNS name for incoming connections to your ## relay. Leave commented out and Tor will guess. diff --git a/src/core/include.am b/src/core/include.am index 1a4b9fb8ab..ee275f172c 100644 --- a/src/core/include.am +++ b/src/core/include.am @@ -117,6 +117,7 @@ LIBTOR_APP_A_SOURCES = \ src/feature/hs/hs_config.c \ src/feature/hs/hs_control.c \ src/feature/hs/hs_descriptor.c \ + src/feature/hs/hs_dos.c \ src/feature/hs/hs_ident.c \ src/feature/hs/hs_intropoint.c \ src/feature/hs/hs_service.c \ @@ -374,6 +375,7 @@ noinst_HEADERS += \ src/feature/hs/hs_config.h \ src/feature/hs/hs_control.h \ src/feature/hs/hs_descriptor.h \ + src/feature/hs/hs_dos.h \ src/feature/hs/hs_ident.h \ src/feature/hs/hs_intropoint.h \ src/feature/hs/hs_service.h \ diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 3a4e729429..ff809c01cf 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -522,14 +522,13 @@ origin_circuit_get_guard_state(origin_circuit_t *circ) static void circuit_chan_publish(const origin_circuit_t *circ, const channel_t *chan) { - ocirc_event_msg_t msg; + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = OCIRC_MSGTYPE_CHAN; - msg.u.chan.gid = circ->global_identifier; - msg.u.chan.chan = chan->global_identifier; - msg.u.chan.onehop = circ->build_state->onehop_tunnel; + msg->gid = circ->global_identifier; + msg->chan = chan->global_identifier; + msg->onehop = circ->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_chan_publish(msg); } /** Start establishing the first hop of our circuit. Figure out what diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index ebbe7f0824..9ee9f93c99 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -496,17 +496,16 @@ int circuit_event_status(origin_circuit_t *circ, circuit_status_event_t tp, int reason_code) { - ocirc_event_msg_t msg; + ocirc_cevent_msg_t *msg = tor_malloc(sizeof(*msg)); tor_assert(circ); - msg.type = OCIRC_MSGTYPE_CEVENT; - msg.u.cevent.gid = circ->global_identifier; - msg.u.cevent.evtype = tp; - msg.u.cevent.reason = reason_code; - msg.u.cevent.onehop = circ->build_state->onehop_tunnel; + msg->gid = circ->global_identifier; + msg->evtype = tp; + msg->reason = reason_code; + msg->onehop = circ->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_cevent_publish(msg); return control_event_circuit_status(circ, tp, reason_code); } @@ -514,26 +513,25 @@ circuit_event_status(origin_circuit_t *circ, circuit_status_event_t tp, * Helper function to publish a state change message * * circuit_set_state() calls this to notify subscribers about a change - * of the state of an origin circuit. + * of the state of an origin circuit. @a circ must be an origin + * circuit. **/ static void circuit_state_publish(const circuit_t *circ) { - ocirc_event_msg_t msg; + ocirc_state_msg_t *msg = tor_malloc(sizeof(*msg)); const origin_circuit_t *ocirc; - if (!CIRCUIT_IS_ORIGIN(circ)) - return; + tor_assert(CIRCUIT_IS_ORIGIN(circ)); ocirc = CONST_TO_ORIGIN_CIRCUIT(circ); /* Only inbound OR circuits can be in this state, not origin circuits. */ tor_assert(circ->state != CIRCUIT_STATE_ONIONSKIN_PENDING); - msg.type = OCIRC_MSGTYPE_STATE; - msg.u.state.gid = ocirc->global_identifier; - msg.u.state.state = circ->state; - msg.u.state.onehop = ocirc->build_state->onehop_tunnel; + msg->gid = ocirc->global_identifier; + msg->state = circ->state; + msg->onehop = ocirc->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_state_publish(msg); } /** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing @@ -565,7 +563,8 @@ circuit_set_state(circuit_t *circ, uint8_t state) if (state == CIRCUIT_STATE_GUARD_WAIT || state == CIRCUIT_STATE_OPEN) tor_assert(!circ->n_chan_create_cell); circ->state = state; - circuit_state_publish(circ); + if (CIRCUIT_IS_ORIGIN(circ)) + circuit_state_publish(circ); } /** Append to <b>out</b> all circuits in state CHAN_WAIT waiting for diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c index a62cdcf9e6..99c68d5f6b 100644 --- a/src/core/or/circuitpadding.c +++ b/src/core/or/circuitpadding.c @@ -17,7 +17,7 @@ * Each padding type is described by a state machine (circpad_machine_spec_t), * which is also referred as a "padding machine" in this file. Currently, * these state machines are hardcoded in the source code (e.g. see - * circpad_circ_client_machine_init()), but in the future we will be able to + * circpad_machines_init()), but in the future we will be able to * serialize them in the torrc or the consensus. * * As specified by prop#254, clients can negotiate padding with relays by using @@ -564,11 +564,12 @@ circpad_distribution_sample_iat_delay(const circpad_state_t *state, } /** - * Sample an expected time-until-next-packet delay from the histogram. + * Sample an expected time-until-next-packet delay from the histogram or + * probability distribution. * - * The bin is chosen with probability proportional to the number - * of tokens in each bin, and then a time value is chosen uniformly from - * that bin's [start,end) time range. + * A bin of the histogram is chosen with probability proportional to the number + * of tokens in each bin, and then a time value is chosen uniformly from that + * bin's [start,end) time range. */ STATIC circpad_delay_t circpad_machine_sample_delay(circpad_machine_runtime_t *mi) @@ -667,12 +668,7 @@ circpad_machine_sample_delay(circpad_machine_runtime_t *mi) /** * Sample a value from the specified probability distribution. * - * This performs inverse transform sampling - * (https://en.wikipedia.org/wiki/Inverse_transform_sampling). - * - * XXX: These formulas were taken verbatim. Need a floating wizard - * to check them for catastropic cancellation and other issues (teor?). - * Also: is 32bits of double from [0.0,1.0) enough? + * Uses functions from src/lib/math/prob_distr.c . */ static double circpad_distribution_sample(circpad_distribution_t dist) @@ -756,6 +752,8 @@ circpad_distribution_sample(circpad_distribution_t dist) /** * Find the index of the first bin whose upper bound is * greater than the target, and that has tokens remaining. + * + * Used for histograms with token removal. */ static circpad_hist_index_t circpad_machine_first_higher_index(const circpad_machine_runtime_t *mi, @@ -778,6 +776,8 @@ circpad_machine_first_higher_index(const circpad_machine_runtime_t *mi, /** * Find the index of the first bin whose lower bound is lower or equal to * <b>target_bin_usec</b>, and that still has tokens remaining. + * + * Used for histograms with token removal. */ static circpad_hist_index_t circpad_machine_first_lower_index(const circpad_machine_runtime_t *mi, @@ -799,6 +799,8 @@ circpad_machine_first_lower_index(const circpad_machine_runtime_t *mi, /** * Remove a token from the first non-empty bin whose upper bound is * greater than the target. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi, @@ -820,6 +822,8 @@ circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi, /** * Remove a token from the first non-empty bin whose upper bound is * lower than the target. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi, @@ -849,6 +853,8 @@ circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi, * midpoint. * * If it is false, use bin index distance only. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi, @@ -931,6 +937,8 @@ circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi, * Remove a token from the exact bin corresponding to the target. * * If it is empty, do nothing. + * + * Used for histograms with token removal. */ static void circpad_machine_remove_exact(circpad_machine_runtime_t *mi, @@ -1365,7 +1373,7 @@ circpad_machine_reached_padding_limit(circpad_machine_runtime_t *mi) /* If circpad_max_global_padding_pct is non-zero, and we've * sent more than the global padding cell limit, then check our - * gloabl tor process percentage limit on padding. */ + * global tor process percentage limit on padding. */ if (circpad_global_max_padding_percent && circpad_global_padding_sent >= circpad_global_allowed_cells) { uint64_t total_cells = circpad_global_padding_sent + @@ -1511,7 +1519,7 @@ circpad_machine_schedule_padding,(circpad_machine_runtime_t *mi)) /** * If the machine transitioned to the END state, we need * to check to see if it wants us to shut it down immediately. - * If it does, then we need to send the appropate negotation commands + * If it does, then we need to send the appropiate negotiation commands * depending on which side it is. * * After this function is called, mi may point to freed memory. Do @@ -1534,7 +1542,7 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_runtime_t *mi) * we can handle the case where this machine started while it was * the only machine that matched conditions, but *since* then more * "higher ranking" machines now match the conditions, and would - * be given a chance to take precidence over this one in + * be given a chance to take precedence over this one in * circpad_add_matching_machines(). * * Returning to START or waiting forever in END would not give those @@ -1662,7 +1670,7 @@ circpad_estimate_circ_rtt_on_received(circuit_t *circ, if (CIRCUIT_IS_ORIGIN(circ) || mi->stop_rtt_update) return; - /* If we already have a last receieved packet time, that means we + /* If we already have a last received packet time, that means we * did not get a response before this packet. The RTT estimate * only makes sense if we do not have multiple packets on the * wire, so stop estimating if this is the second packet @@ -1807,6 +1815,61 @@ circpad_cell_event_nonpadding_sent(circuit_t *on_circ) } FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END; } +/** Check if this cell or circuit are related to circuit padding and handle + * them if so. Return 0 if the cell was handled in this subsystem and does + * not need any other consideration, otherwise return 1. + */ +int +circpad_check_received_cell(cell_t *cell, circuit_t *circ, + crypt_path_t *layer_hint, + const relay_header_t *rh) +{ + /* First handle the padding commands, since we want to ignore any other + * commands if this circuit is padding-specific. */ + switch (rh->command) { + case RELAY_COMMAND_DROP: + /* Already examined in circpad_deliver_recognized_relay_cell_events */ + return 0; + case RELAY_COMMAND_PADDING_NEGOTIATE: + circpad_handle_padding_negotiate(circ, cell); + return 0; + case RELAY_COMMAND_PADDING_NEGOTIATED: + if (circpad_handle_padding_negotiated(circ, cell, layer_hint) == 0) + circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length); + return 0; + } + + /* If this is a padding circuit we don't need to parse any other commands + * than the padding ones. Just drop them to the floor. + * + * Note: we deliberately do not call circuit_read_valid_data() here. The + * vanguards addon (specifically the 'bandguards' component's dropped cell + * detection) will thus close this circuit, as it would for any other + * unexpected cell. However, default tor will *not* close the circuit. + * + * This is intentional. We are not yet certain that is it optimal to keep + * padding circuits open in cases like these, rather than closing them. + * We suspect that continuing to pad is optimal against a passive classifier, + * but as soon as the adversary is active (even as a client adversary) this + * might change. + * + * So as a way forward, we log the cell command and circuit number, to + * help us enumerate the most common instances of this in testing with + * vanguards, to see which are common enough to verify and handle + * properly. + * - Mike + */ + if (circ->purpose == CIRCUIT_PURPOSE_C_CIRCUIT_PADDING) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Ignored cell (%d) that arrived in padding circuit " + " %u.", rh->command, CIRCUIT_IS_ORIGIN(circ) ? + TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0); + return 0; + } + + return 1; +} + /** * A "non-padding" cell has been received by this endpoint. React * according to any padding state machines on the circuit. @@ -2332,7 +2395,7 @@ circpad_deliver_sent_relay_cell_events(circuit_t *circ, /* Optimization: The event for RELAY_COMMAND_DROP is sent directly * from circpad_send_padding_cell_for_callback(). This is to avoid * putting a cell_t and a relay_header_t on the stack repeatedly - * if we decide to send a long train of padidng cells back-to-back + * if we decide to send a long train of padding cells back-to-back * with 0 delay. So we do nothing here. */ return; } else { diff --git a/src/core/or/circuitpadding.h b/src/core/or/circuitpadding.h index 3cf40e11db..e9eb32c618 100644 --- a/src/core/or/circuitpadding.h +++ b/src/core/or/circuitpadding.h @@ -51,7 +51,7 @@ typedef enum { CIRCPAD_EVENT_INFINITY = 4, /* All histogram bins are empty (we are out of tokens) */ CIRCPAD_EVENT_BINS_EMPTY = 5, - /* just a counter of the events above */ + /* This state has used up its cell count */ CIRCPAD_EVENT_LENGTH_COUNT = 6 } circpad_event_t; #define CIRCPAD_NUM_EVENTS ((int)CIRCPAD_EVENT_LENGTH_COUNT+1) @@ -79,7 +79,7 @@ typedef uint32_t circpad_delay_t; * An infinite padding cell delay means don't schedule any padding -- * simply wait until a different event triggers a transition. * - * This means that the maximum delay we can scedule is UINT32_MAX-1 + * This means that the maximum delay we can schedule is UINT32_MAX-1 * microseconds, or about 4300 seconds (1.25 hours). * XXX: Is this enough if we want to simulate light, intermittent * activity on an onion service? @@ -106,8 +106,8 @@ typedef uint32_t circpad_delay_t; * * If any of these elements is set, then the circuit will be tested against * that specific condition. If an element is unset, then we don't test it. - * (E.g. If neither NO_STREAMS or STREAMS are set, then we will not care - * whether a circuit has streams attached when we apply a state machine) + * (E.g., if neither NO_STREAMS or STREAMS are set, then we will not care + * whether a circuit has streams attached when we apply a state machine.) * * The helper function circpad_circuit_state() converts circuit state * flags into this more compact representation. @@ -255,8 +255,9 @@ typedef struct circpad_distribution_t { typedef uint16_t circpad_statenum_t; #define CIRCPAD_STATENUM_MAX (UINT16_MAX) -/** A histogram is used to sample padding delays given a machine state. This - * constant defines the maximum histogram width (i.e. the max number of bins). +/** A histogram can be used to sample padding delays given a machine state. + * This constant defines the maximum histogram width (i.e. the max number of + * bins). * * The current limit is arbitrary and could be raised if there is a need, * however too many bins will be hard to serialize in the future. @@ -275,10 +276,10 @@ typedef uint16_t circpad_statenum_t; * happen. The mutable information that gets updated in runtime are carried in * a circpad_machine_runtime_t. * - * This struct describes the histograms and parameters of a single - * state in the adaptive padding machine. Instances of this struct - * exist in global circpad machine definitions that come from torrc - * or the consensus. + * This struct describes the histograms and/or probability distributions, as + * well as parameters of a single state in the adaptive padding machine. + * Instances of this struct exist in global circpad machine definitions that + * come from torrc or the consensus. */ typedef struct circpad_state_t { /** @@ -733,6 +734,10 @@ bool circpad_padding_negotiated(struct circuit_t *circ, circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t circ_purpose); +int circpad_check_received_cell(cell_t *cell, circuit_t *circ, + crypt_path_t *layer_hint, + const relay_header_t *rh); + MOCK_DECL(circpad_decision_t, circpad_machine_schedule_padding,(circpad_machine_runtime_t *)); diff --git a/src/core/or/circuitpadding_machines.c b/src/core/or/circuitpadding_machines.c index 75d2614aca..7220d657fc 100644 --- a/src/core/or/circuitpadding_machines.c +++ b/src/core/or/circuitpadding_machines.c @@ -155,7 +155,6 @@ circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl) relay_machine->name = "relay_ip_circ"; relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; - relay_machine->target_hopnum = 2; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -387,7 +386,6 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* Only pad after the circuit has been built and pad to the middle */ relay_machine->conditions.min_hops = 2; relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; - relay_machine->target_hopnum = 2; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -408,7 +406,7 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* OBFUSCATE_CIRC_SETUP -> END transition when we send our first * padding packet and/or hit the state length (the state length is 1). */ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]. - next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_END; + next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_END; relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]. next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END; diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index c08d2a9ff5..091d9c9b09 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -3833,6 +3833,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (! bcell.is_begindir) { /* Steal reference */ + tor_assert(bcell.address); address = bcell.address; port = bcell.port; diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 830e09fd54..4c93351e31 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -414,13 +414,12 @@ void connection_or_event_status(or_connection_t *conn, or_conn_status_event_t tp, int reason) { - orconn_event_msg_t msg; + orconn_status_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = ORCONN_MSGTYPE_STATUS; - msg.u.status.gid = conn->base_.global_identifier; - msg.u.status.status = tp; - msg.u.status.reason = reason; - orconn_event_publish(&msg); + msg->gid = conn->base_.global_identifier; + msg->status = tp; + msg->reason = reason; + orconn_status_publish(msg); control_event_or_conn_status(conn, tp, reason); } @@ -433,26 +432,25 @@ connection_or_event_status(or_connection_t *conn, or_conn_status_event_t tp, static void connection_or_state_publish(const or_connection_t *conn, uint8_t state) { - orconn_event_msg_t msg; + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = ORCONN_MSGTYPE_STATE; - msg.u.state.gid = conn->base_.global_identifier; + msg->gid = conn->base_.global_identifier; if (conn->is_pt) { /* Do extra decoding because conn->proxy_type indicates the proxy * protocol that tor uses to talk with the transport plugin, * instead of PROXY_PLUGGABLE. */ tor_assert_nonfatal(conn->proxy_type != PROXY_NONE); - msg.u.state.proxy_type = PROXY_PLUGGABLE; + msg->proxy_type = PROXY_PLUGGABLE; } else { - msg.u.state.proxy_type = conn->proxy_type; + msg->proxy_type = conn->proxy_type; } - msg.u.state.state = state; + msg->state = state; if (conn->chan) { - msg.u.state.chan = TLS_CHAN_TO_BASE(conn->chan)->global_identifier; + msg->chan = TLS_CHAN_TO_BASE(conn->chan)->global_identifier; } else { - msg.u.state.chan = 0; + msg->chan = 0; } - orconn_event_publish(&msg); + orconn_state_publish(msg); } /** Call this to change or_connection_t states, so the owning channel_tls_t can diff --git a/src/core/or/ocirc_event.c b/src/core/or/ocirc_event.c index 4a6fc748c9..3cb9147134 100644 --- a/src/core/or/ocirc_event.c +++ b/src/core/or/ocirc_event.c @@ -26,59 +26,103 @@ #include "core/or/origin_circuit_st.h" #include "lib/subsys/subsys.h" -/** List of subscribers */ -static smartlist_t *ocirc_event_rcvrs; +DECLARE_PUBLISH(ocirc_state); +DECLARE_PUBLISH(ocirc_chan); +DECLARE_PUBLISH(ocirc_cevent); + +static void +ocirc_event_free(msg_aux_data_t u) +{ + tor_free_(u.ptr); +} + +static char * +ocirc_state_fmt(msg_aux_data_t u) +{ + ocirc_state_msg_t *msg = (ocirc_state_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" state=%d onehop=%d>", + msg->gid, msg->state, msg->onehop); + return s; +} + +static char * +ocirc_chan_fmt(msg_aux_data_t u) +{ + ocirc_chan_msg_t *msg = (ocirc_chan_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" chan=%"PRIu64" onehop=%d>", + msg->gid, msg->chan, msg->onehop); + return s; +} + +static char * +ocirc_cevent_fmt(msg_aux_data_t u) +{ + ocirc_cevent_msg_t *msg = (ocirc_cevent_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" evtype=%d reason=%d onehop=%d>", + msg->gid, msg->evtype, msg->reason, msg->onehop); + return s; +} + +static dispatch_typefns_t ocirc_state_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_state_fmt, +}; + +static dispatch_typefns_t ocirc_chan_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_chan_fmt, +}; + +static dispatch_typefns_t ocirc_cevent_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_cevent_fmt, +}; -/** Initialize subscriber list */ static int -ocirc_event_init(void) +ocirc_add_pubsub(struct pubsub_connector_t *connector) { - ocirc_event_rcvrs = smartlist_new(); + if (DISPATCH_REGISTER_TYPE(connector, ocirc_state, ô_state_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, ocirc_chan, ô_chan_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, ocirc_cevent, ô_cevent_fns)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_state)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_chan)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_cevent)) + return -1; return 0; } -/** Free subscriber list */ -static void -ocirc_event_fini(void) +void +ocirc_state_publish(ocirc_state_msg_t *msg) { - smartlist_free(ocirc_event_rcvrs); + PUBLISH(ocirc_state, msg); } -/** - * Subscribe to messages about origin circuit events - * - * Register a callback function to receive messages about origin - * circuits. The publisher calls this function synchronously. - **/ void -ocirc_event_subscribe(ocirc_event_rcvr_t fn) +ocirc_chan_publish(ocirc_chan_msg_t *msg) { - tor_assert(fn); - /* Don't duplicate subscriptions. */ - if (smartlist_contains(ocirc_event_rcvrs, fn)) - return; - - smartlist_add(ocirc_event_rcvrs, fn); + PUBLISH(ocirc_chan, msg); } -/** - * Publish a message about OR connection events - * - * This calls the subscriber receiver function synchronously. - **/ void -ocirc_event_publish(const ocirc_event_msg_t *msg) +ocirc_cevent_publish(ocirc_cevent_msg_t *msg) { - SMARTLIST_FOREACH_BEGIN(ocirc_event_rcvrs, ocirc_event_rcvr_t, fn) { - tor_assert(fn); - (*fn)(msg); - } SMARTLIST_FOREACH_END(fn); + PUBLISH(ocirc_cevent, msg); } const subsys_fns_t sys_ocirc_event = { .name = "ocirc_event", .supported = true, .level = -32, - .initialize = ocirc_event_init, - .shutdown = ocirc_event_fini, + .add_pubsub = ocirc_add_pubsub, }; diff --git a/src/core/or/ocirc_event.h b/src/core/or/ocirc_event.h index 59ec9e27cb..8e9494874f 100644 --- a/src/core/or/ocirc_event.h +++ b/src/core/or/ocirc_event.h @@ -12,6 +12,7 @@ #include <stdbool.h> #include "lib/cc/torint.h" +#include "lib/pubsub/pubsub.h" /** Used to indicate the type of a circuit event passed to the controller. * The various types are defined in control-spec.txt */ @@ -30,6 +31,8 @@ typedef struct ocirc_state_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_state_msg_t; +DECLARE_MESSAGE(ocirc_state, ocirc_state, ocirc_state_msg_t *); + /** * Message when a channel gets associated to a circuit. * @@ -44,6 +47,8 @@ typedef struct ocirc_chan_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_chan_msg_t; +DECLARE_MESSAGE(ocirc_chan, ocirc_chan, ocirc_chan_msg_t *); + /** * Message for origin circuit status event * @@ -56,34 +61,12 @@ typedef struct ocirc_cevent_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_cevent_msg_t; -/** Discriminant values for origin circuit event message */ -typedef enum ocirc_msgtype_t { - OCIRC_MSGTYPE_STATE, - OCIRC_MSGTYPE_CHAN, - OCIRC_MSGTYPE_CEVENT, -} ocirc_msgtype_t; - -/** Discriminated union for the actual message */ -typedef struct ocirc_event_msg_t { - int type; - union { - ocirc_state_msg_t state; - ocirc_chan_msg_t chan; - ocirc_cevent_msg_t cevent; - } u; -} ocirc_event_msg_t; - -/** - * Receiver function pointer for origin circuit subscribers - * - * This function gets called synchronously by the publisher. - **/ -typedef void (*ocirc_event_rcvr_t)(const ocirc_event_msg_t *); - -void ocirc_event_subscribe(ocirc_event_rcvr_t fn); +DECLARE_MESSAGE(ocirc_cevent, ocirc_cevent, ocirc_cevent_msg_t *); #ifdef OCIRC_EVENT_PRIVATE -void ocirc_event_publish(const ocirc_event_msg_t *msg); +void ocirc_state_publish(ocirc_state_msg_t *msg); +void ocirc_chan_publish(ocirc_chan_msg_t *msg); +void ocirc_cevent_publish(ocirc_cevent_msg_t *msg); #endif #endif /* !defined(TOR_OCIRC_EVENT_H) */ diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h index 6789668224..8f319585a6 100644 --- a/src/core/or/or_circuit_st.h +++ b/src/core/or/or_circuit_st.h @@ -12,6 +12,8 @@ #include "core/or/circuit_st.h" #include "core/or/crypt_path_st.h" +#include "lib/evloop/token_bucket.h" + struct onion_queue_t; /** An or_circuit_t holds information needed to implement a circuit at an @@ -69,6 +71,11 @@ struct or_circuit_t { * exit-ward queues of this circuit; reset every time when writing * buffer stats to disk. */ uint64_t total_cell_waiting_time; + + /** INTRODUCE2 cell bucket controlling how much can go on this circuit. Only + * used if this is a service introduction circuit at the intro point + * (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */ + token_bucket_ctr_t introduce2_bucket; }; #endif /* !defined(OR_CIRCUIT_ST_H) */ diff --git a/src/core/or/orconn_event.c b/src/core/or/orconn_event.c index 9fb34bd1ff..86f112fc09 100644 --- a/src/core/or/orconn_event.c +++ b/src/core/or/orconn_event.c @@ -17,65 +17,83 @@ **/ #include "core/or/or.h" +#include "lib/pubsub/pubsub.h" #include "lib/subsys/subsys.h" #define ORCONN_EVENT_PRIVATE #include "core/or/orconn_event.h" #include "core/or/orconn_event_sys.h" -/** List of subscribers */ -static smartlist_t *orconn_event_rcvrs; +DECLARE_PUBLISH(orconn_state); +DECLARE_PUBLISH(orconn_status); -/** Initialize subscriber list */ -static int -orconn_event_init(void) +static void +orconn_event_free(msg_aux_data_t u) { - orconn_event_rcvrs = smartlist_new(); - return 0; + tor_free_(u.ptr); } -/** Free subscriber list */ -static void -orconn_event_fini(void) +static char * +orconn_state_fmt(msg_aux_data_t u) { - smartlist_free(orconn_event_rcvrs); + orconn_state_msg_t *msg = (orconn_state_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu64" chan=%"PRIu64" proxy_type=%d state=%d>", + msg->gid, msg->chan, msg->proxy_type, msg->state); + return s; } -/** - * Subscribe to messages about OR connection events - * - * Register a callback function to receive messages about ORCONNs. - * The publisher calls this function synchronously. - **/ -void -orconn_event_subscribe(orconn_event_rcvr_t fn) +static char * +orconn_status_fmt(msg_aux_data_t u) { - tor_assert(fn); - /* Don't duplicate subscriptions. */ - if (smartlist_contains(orconn_event_rcvrs, fn)) - return; + orconn_status_msg_t *msg = (orconn_status_msg_t *)u.ptr; + char *s = NULL; - smartlist_add(orconn_event_rcvrs, fn); + tor_asprintf(&s, "<gid=%"PRIu64" status=%d reason=%d>", + msg->gid, msg->status, msg->reason); + return s; +} + +static dispatch_typefns_t orconn_state_fns = { + .free_fn = orconn_event_free, + .fmt_fn = orconn_state_fmt, +}; + +static dispatch_typefns_t orconn_status_fns = { + .free_fn = orconn_event_free, + .fmt_fn = orconn_status_fmt, +}; + +static int +orconn_add_pubsub(struct pubsub_connector_t *connector) +{ + if (DISPATCH_REGISTER_TYPE(connector, orconn_state, &orconn_state_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, orconn_status, &orconn_status_fns)) + return -1; + if (DISPATCH_ADD_PUB(connector, orconn, orconn_state) != 0) + return -1; + if (DISPATCH_ADD_PUB(connector, orconn, orconn_status) != 0) + return -1; + return 0; +} + +void +orconn_state_publish(orconn_state_msg_t *msg) +{ + PUBLISH(orconn_state, msg); } -/** - * Publish a message about OR connection events - * - * This calls the subscriber receiver function synchronously. - **/ void -orconn_event_publish(const orconn_event_msg_t *msg) +orconn_status_publish(orconn_status_msg_t *msg) { - SMARTLIST_FOREACH_BEGIN(orconn_event_rcvrs, orconn_event_rcvr_t, fn) { - tor_assert(fn); - (*fn)(msg); - } SMARTLIST_FOREACH_END(fn); + PUBLISH(orconn_status, msg); } const subsys_fns_t sys_orconn_event = { .name = "orconn_event", .supported = true, .level = -33, - .initialize = orconn_event_init, - .shutdown = orconn_event_fini, + .add_pubsub = orconn_add_pubsub, }; diff --git a/src/core/or/orconn_event.h b/src/core/or/orconn_event.h index d6635793db..fb67a7d183 100644 --- a/src/core/or/orconn_event.h +++ b/src/core/or/orconn_event.h @@ -16,6 +16,8 @@ #ifndef TOR_ORCONN_EVENT_H #define TOR_ORCONN_EVENT_H +#include "lib/pubsub/pubsub.h" + /** * @name States of OR connections * @@ -62,12 +64,6 @@ typedef enum or_conn_status_event_t { OR_CONN_EVENT_NEW = 4, } or_conn_status_event_t; -/** Discriminant values for orconn event message */ -typedef enum orconn_msgtype_t { - ORCONN_MSGTYPE_STATE, - ORCONN_MSGTYPE_STATUS, -} orconn_msgtype_t; - /** * Message for orconn state update * @@ -83,6 +79,8 @@ typedef struct orconn_state_msg_t { uint8_t state; /**< new connection state */ } orconn_state_msg_t; +DECLARE_MESSAGE(orconn_state, orconn_state, orconn_state_msg_t *); + /** * Message for orconn status event * @@ -95,26 +93,11 @@ typedef struct orconn_status_msg_t { int reason; /**< reason */ } orconn_status_msg_t; -/** Discriminated union for the actual message */ -typedef struct orconn_event_msg_t { - int type; - union { - orconn_state_msg_t state; - orconn_status_msg_t status; - } u; -} orconn_event_msg_t; - -/** - * Receiver function pointer for OR subscribers - * - * This function gets called synchronously by the publisher. - **/ -typedef void (*orconn_event_rcvr_t)(const orconn_event_msg_t *); - -void orconn_event_subscribe(orconn_event_rcvr_t); +DECLARE_MESSAGE(orconn_status, orconn_status, orconn_status_msg_t *); #ifdef ORCONN_EVENT_PRIVATE -void orconn_event_publish(const orconn_event_msg_t *); +void orconn_state_publish(orconn_state_msg_t *); +void orconn_status_publish(orconn_status_msg_t *); #endif #endif /* !defined(TOR_ORCONN_EVENT_H) */ diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 9e691a02b5..a437b54792 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -265,8 +265,8 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, if (cell_direction == CELL_DIRECTION_OUT) { ++stats_n_relay_cells_delivered; log_debug(LD_OR,"Sending away from origin."); - if ((reason=connection_edge_process_relay_cell(cell, circ, conn, NULL)) - < 0) { + reason = connection_edge_process_relay_cell(cell, circ, conn, NULL); + if (reason < 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "connection_edge_process_relay_cell (away from origin) " "failed."); @@ -276,8 +276,9 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, if (cell_direction == CELL_DIRECTION_IN) { ++stats_n_relay_cells_delivered; log_debug(LD_OR,"Sending to origin."); - if ((reason = connection_edge_process_relay_cell(cell, circ, conn, - layer_hint)) < 0) { + reason = connection_edge_process_relay_cell(cell, circ, conn, + layer_hint); + if (reason < 0) { /* If a client is trying to connect to unknown hidden service port, * END_CIRC_AT_ORIGIN is sent back so we can then close the circuit. * Do not log warn as this is an expected behavior for a service. */ @@ -1576,104 +1577,33 @@ process_sendme_cell(const relay_header_t *rh, const cell_t *cell, return 0; } -/** An incoming relay cell has arrived on circuit <b>circ</b>. If - * <b>conn</b> is NULL this is a control cell, else <b>cell</b> is - * destined for <b>conn</b>. - * - * If <b>layer_hint</b> is defined, then we're the origin of the - * circuit, and it specifies the hop that packaged <b>cell</b>. +/** A helper for connection_edge_process_relay_cell(): Actually handles the + * cell that we received on the connection. * - * Return -reason if you want to warn and tear down the circuit, else 0. + * The arguments are the same as in the parent function + * connection_edge_process_relay_cell(), plus the relay header <b>rh</b> as + * unpacked by the parent function, and <b>optimistic_data</b> as set by the + * parent function. */ STATIC int -connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, - edge_connection_t *conn, - crypt_path_t *layer_hint) +handle_relay_cell_command(cell_t *cell, circuit_t *circ, + edge_connection_t *conn, crypt_path_t *layer_hint, + relay_header_t *rh, int optimistic_data) { - static int num_seen=0; - relay_header_t rh; unsigned domain = layer_hint?LD_APP:LD_EXIT; int reason; - int optimistic_data = 0; /* Set to 1 if we receive data on a stream - * that's in the EXIT_CONN_STATE_RESOLVING - * or EXIT_CONN_STATE_CONNECTING states. */ - - tor_assert(cell); - tor_assert(circ); - - relay_header_unpack(&rh, cell->payload); -// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id); - num_seen++; - log_debug(domain, "Now seen %d relay cells here (command %d, stream %d).", - num_seen, rh.command, rh.stream_id); - - if (rh.length > RELAY_PAYLOAD_SIZE) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Relay cell length field too long. Closing circuit."); - return - END_CIRC_REASON_TORPROTOCOL; - } - - if (rh.stream_id == 0) { - switch (rh.command) { - case RELAY_COMMAND_BEGIN: - case RELAY_COMMAND_CONNECTED: - case RELAY_COMMAND_END: - case RELAY_COMMAND_RESOLVE: - case RELAY_COMMAND_RESOLVED: - case RELAY_COMMAND_BEGIN_DIR: - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay command %d with zero " - "stream_id. Dropping.", (int)rh.command); - return 0; - default: - ; - } - } - - /* Tell circpad that we've received a recognized cell */ - circpad_deliver_recognized_relay_cell_events(circ, rh.command, layer_hint); - - /* either conn is NULL, in which case we've got a control cell, or else - * conn points to the recognized stream. */ - if (conn && !connection_state_is_open(TO_CONN(conn))) { - if (conn->base_.type == CONN_TYPE_EXIT && - (conn->base_.state == EXIT_CONN_STATE_CONNECTING || - conn->base_.state == EXIT_CONN_STATE_RESOLVING) && - rh.command == RELAY_COMMAND_DATA) { - /* Allow DATA cells to be delivered to an exit node in state - * EXIT_CONN_STATE_CONNECTING or EXIT_CONN_STATE_RESOLVING. - * This speeds up HTTP, for example. */ - optimistic_data = 1; - } else if (rh.stream_id == 0 && rh.command == RELAY_COMMAND_DATA) { - log_warn(LD_BUG, "Somehow I had a connection that matched a " - "data cell with stream ID 0."); - } else { - return connection_edge_process_relay_cell_not_open( - &rh, cell, circ, conn, layer_hint); - } - } - switch (rh.command) { - case RELAY_COMMAND_DROP: - /* Already examined in circpad_deliver_recognized_relay_cell_events */ - return 0; - case RELAY_COMMAND_PADDING_NEGOTIATE: - circpad_handle_padding_negotiate(circ, cell); - return 0; - case RELAY_COMMAND_PADDING_NEGOTIATED: - if (circpad_handle_padding_negotiated(circ, cell, layer_hint) == 0) - circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length); - return 0; - } + tor_assert(rh); - /* If this is a padding circuit we don't need to parse any other commands - * than the padding ones. Just drop them to the floor. */ - if (circ->purpose == CIRCUIT_PURPOSE_C_CIRCUIT_PADDING) { - log_info(domain, "Ignored cell (%d) that arrived in padding circuit.", - rh.command); + /* First pass the cell to the circuit padding subsystem, in case it's a + * padding cell or circuit that should be handled there. */ + if (circpad_check_received_cell(cell, circ, layer_hint, rh) == 0) { + log_debug(domain, "Cell handled as circuit padding"); return 0; } - switch (rh.command) { + /* Now handle all the other commands */ + switch (rh->command) { case RELAY_COMMAND_BEGIN: case RELAY_COMMAND_BEGIN_DIR: if (layer_hint && @@ -1694,7 +1624,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, "Begin cell for known stream. Dropping."); return 0; } - if (rh.command == RELAY_COMMAND_BEGIN_DIR && + if (rh->command == RELAY_COMMAND_BEGIN_DIR && circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { /* Assign this circuit and its app-ward OR connection a unique ID, * so that we can measure download times. The local edge and dir @@ -1721,7 +1651,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, /* Consider sending a circuit-level SENDME cell. */ sendme_circuit_consider_sending(circ, layer_hint); - if (rh.stream_id == 0) { + if (rh->stream_id == 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay data cell with zero " "stream_id. Dropping."); return 0; @@ -1729,16 +1659,16 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); if (connection_half_edge_is_valid_data(ocirc->half_streams, - rh.stream_id)) { - circuit_read_valid_data(ocirc, rh.length); + rh->stream_id)) { + circuit_read_valid_data(ocirc, rh->length); log_info(domain, "data cell on circ %u valid on half-closed " - "stream id %d", ocirc->global_identifier, rh.stream_id); + "stream id %d", ocirc->global_identifier, rh->stream_id); } } log_info(domain,"data cell dropped, unknown stream (streamid %d).", - rh.stream_id); + rh->stream_id); return 0; } @@ -1753,13 +1683,13 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, return -END_CIRC_REASON_TORPROTOCOL; } /* Total all valid application bytes delivered */ - if (CIRCUIT_IS_ORIGIN(circ) && rh.length > 0) { - circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length); + if (CIRCUIT_IS_ORIGIN(circ) && rh->length > 0) { + circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length); } - stats_n_data_bytes_received += rh.length; + stats_n_data_bytes_received += rh->length; connection_buf_add((char*)(cell->payload + RELAY_HEADER_SIZE), - rh.length, TO_CONN(conn)); + rh->length, TO_CONN(conn)); #ifdef MEASUREMENTS_21206 /* Count number of RELAY_DATA cells received on a linked directory @@ -1780,20 +1710,20 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, return 0; case RELAY_COMMAND_END: - reason = rh.length > 0 ? + reason = rh->length > 0 ? get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC; if (!conn) { if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); if (connection_half_edge_is_valid_end(ocirc->half_streams, - rh.stream_id)) { + rh->stream_id)) { - circuit_read_valid_data(ocirc, rh.length); + circuit_read_valid_data(ocirc, rh->length); log_info(domain, "end cell (%s) on circ %u valid on half-closed " "stream id %d", stream_end_reason_to_string(reason), - ocirc->global_identifier, rh.stream_id); + ocirc->global_identifier, rh->stream_id); return 0; } } @@ -1825,7 +1755,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, /* Total all valid application bytes delivered */ if (CIRCUIT_IS_ORIGIN(circ)) { - circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length); + circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length); } } return 0; @@ -1833,7 +1763,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, case RELAY_COMMAND_EXTEND2: { static uint64_t total_n_extend=0, total_nonearly=0; total_n_extend++; - if (rh.stream_id) { + if (rh->stream_id) { log_fn(LOG_PROTOCOL_WARN, domain, "'extend' cell received for non-zero stream. Dropping."); return 0; @@ -1874,9 +1804,9 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, log_debug(domain,"Got an extended cell! Yay."); { extended_cell_t extended_cell; - if (extended_cell_parse(&extended_cell, rh.command, + if (extended_cell_parse(&extended_cell, rh->command, (const uint8_t*)cell->payload+RELAY_HEADER_SIZE, - rh.length)<0) { + rh->length)<0) { log_warn(LD_PROTOCOL, "Can't parse EXTENDED cell; killing circuit."); return -END_CIRC_REASON_TORPROTOCOL; @@ -1894,7 +1824,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, } /* Total all valid bytes delivered. */ if (CIRCUIT_IS_ORIGIN(circ)) { - circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length); + circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length); } return 0; case RELAY_COMMAND_TRUNCATE: @@ -1938,7 +1868,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, * circuit is being torn down anyway, though. */ if (CIRCUIT_IS_ORIGIN(circ)) { circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), - rh.length); + rh->length); } circuit_truncated(TO_ORIGIN_CIRCUIT(circ), get_uint8(cell->payload + RELAY_HEADER_SIZE)); @@ -1953,11 +1883,11 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); if (connection_half_edge_is_valid_connected(ocirc->half_streams, - rh.stream_id)) { - circuit_read_valid_data(ocirc, rh.length); + rh->stream_id)) { + circuit_read_valid_data(ocirc, rh->length); log_info(domain, "connected cell on circ %u valid on half-closed " - "stream id %d", ocirc->global_identifier, rh.stream_id); + "stream id %d", ocirc->global_identifier, rh->stream_id); return 0; } } @@ -1965,10 +1895,10 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, log_info(domain, "'connected' received on circid %u for streamid %d, " "no conn attached anymore. Ignoring.", - (unsigned)circ->n_circ_id, rh.stream_id); + (unsigned)circ->n_circ_id, rh->stream_id); return 0; case RELAY_COMMAND_SENDME: - return process_sendme_cell(&rh, cell, circ, conn, layer_hint, domain); + return process_sendme_cell(rh, cell, circ, conn, layer_hint, domain); case RELAY_COMMAND_RESOLVE: if (layer_hint) { log_fn(LOG_PROTOCOL_WARN, LD_APP, @@ -1996,11 +1926,11 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); if (connection_half_edge_is_valid_resolved(ocirc->half_streams, - rh.stream_id)) { - circuit_read_valid_data(ocirc, rh.length); + rh->stream_id)) { + circuit_read_valid_data(ocirc, rh->length); log_info(domain, "resolved cell on circ %u valid on half-closed " - "stream id %d", ocirc->global_identifier, rh.stream_id); + "stream id %d", ocirc->global_identifier, rh->stream_id); return 0; } } @@ -2018,17 +1948,96 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, case RELAY_COMMAND_INTRO_ESTABLISHED: case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: rend_process_relay_cell(circ, layer_hint, - rh.command, rh.length, + rh->command, rh->length, cell->payload+RELAY_HEADER_SIZE); return 0; } log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received unknown relay command %d. Perhaps the other side is using " "a newer version of Tor? Dropping.", - rh.command); + rh->command); return 0; /* for forward compatibility, don't kill the circuit */ } +/** An incoming relay cell has arrived on circuit <b>circ</b>. If + * <b>conn</b> is NULL this is a control cell, else <b>cell</b> is + * destined for <b>conn</b>. + * + * If <b>layer_hint</b> is defined, then we're the origin of the + * circuit, and it specifies the hop that packaged <b>cell</b>. + * + * Return -reason if you want to warn and tear down the circuit, else 0. + */ +STATIC int +connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, + edge_connection_t *conn, + crypt_path_t *layer_hint) +{ + static int num_seen=0; + relay_header_t rh; + unsigned domain = layer_hint?LD_APP:LD_EXIT; + int optimistic_data = 0; /* Set to 1 if we receive data on a stream + * that's in the EXIT_CONN_STATE_RESOLVING + * or EXIT_CONN_STATE_CONNECTING states. */ + + tor_assert(cell); + tor_assert(circ); + + relay_header_unpack(&rh, cell->payload); +// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id); + num_seen++; + log_debug(domain, "Now seen %d relay cells here (command %d, stream %d).", + num_seen, rh.command, rh.stream_id); + + if (rh.length > RELAY_PAYLOAD_SIZE) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Relay cell length field too long. Closing circuit."); + return - END_CIRC_REASON_TORPROTOCOL; + } + + if (rh.stream_id == 0) { + switch (rh.command) { + case RELAY_COMMAND_BEGIN: + case RELAY_COMMAND_CONNECTED: + case RELAY_COMMAND_END: + case RELAY_COMMAND_RESOLVE: + case RELAY_COMMAND_RESOLVED: + case RELAY_COMMAND_BEGIN_DIR: + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay command %d with zero " + "stream_id. Dropping.", (int)rh.command); + return 0; + default: + ; + } + } + + /* Tell circpad that we've received a recognized cell */ + circpad_deliver_recognized_relay_cell_events(circ, rh.command, layer_hint); + + /* either conn is NULL, in which case we've got a control cell, or else + * conn points to the recognized stream. */ + if (conn && !connection_state_is_open(TO_CONN(conn))) { + if (conn->base_.type == CONN_TYPE_EXIT && + (conn->base_.state == EXIT_CONN_STATE_CONNECTING || + conn->base_.state == EXIT_CONN_STATE_RESOLVING) && + rh.command == RELAY_COMMAND_DATA) { + /* Allow DATA cells to be delivered to an exit node in state + * EXIT_CONN_STATE_CONNECTING or EXIT_CONN_STATE_RESOLVING. + * This speeds up HTTP, for example. */ + optimistic_data = 1; + } else if (rh.stream_id == 0 && rh.command == RELAY_COMMAND_DATA) { + log_warn(LD_BUG, "Somehow I had a connection that matched a " + "data cell with stream ID 0."); + } else { + return connection_edge_process_relay_cell_not_open( + &rh, cell, circ, conn, layer_hint); + } + } + + return handle_relay_cell_command(cell, circ, conn, layer_hint, + &rh, optimistic_data); +} + /** How many relay_data cells have we built, ever? */ uint64_t stats_n_data_cells_packaged = 0; /** How many bytes of data have we put in relay_data cells have we built, diff --git a/src/core/or/relay.h b/src/core/or/relay.h index 79036f97bd..99f7553013 100644 --- a/src/core/or/relay.h +++ b/src/core/or/relay.h @@ -99,6 +99,11 @@ circid_t packed_cell_get_circid(const packed_cell_t *cell, int wide_circ_ids); uint8_t packed_cell_get_command(const packed_cell_t *cell, int wide_circ_ids); #ifdef RELAY_PRIVATE +STATIC int +handle_relay_cell_command(cell_t *cell, circuit_t *circ, + edge_connection_t *conn, crypt_path_t *layer_hint, + relay_header_t *rh, int optimistic_data); + STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell, tor_addr_t *addr_out, int *ttl_out); /** An address-and-ttl tuple as yielded by resolved_cell_parse */ diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c index 47ac95f3cf..0757ce3d52 100644 --- a/src/core/or/sendme.c +++ b/src/core/or/sendme.c @@ -23,7 +23,7 @@ #include "core/or/sendme.h" #include "feature/nodelist/networkstatus.h" #include "lib/ctime/di_ops.h" -#include "trunnel/sendme.h" +#include "trunnel/sendme_cell.h" /* Return the minimum version given by the consensus (if any) that should be * used when emitting a SENDME cell. */ diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c index af8559a476..faa52ae4e1 100644 --- a/src/ext/csiphash.c +++ b/src/ext/csiphash.c @@ -87,6 +87,13 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k v0 ^= mi; } +#ifdef __COVERITY__ + { + uint64_t mi = 0; + memcpy(&mi, m+i, (src_sz-blocks)); + last7 = _le64toh(mi) | (uint64_t)(src_sz & 0xff) << 56; + } +#else switch (src_sz - blocks) { case 7: last7 |= (uint64_t)m[i + 6] << 48; /* Falls through. */ case 6: last7 |= (uint64_t)m[i + 5] << 40; /* Falls through. */ @@ -98,6 +105,7 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k case 0: default:; } +#endif v3 ^= last7; DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= last7; diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h index 15d1c8633e..52afa9ccd4 100644 --- a/src/ext/trunnel/trunnel-impl.h +++ b/src/ext/trunnel/trunnel-impl.h @@ -1,4 +1,4 @@ -/* trunnel-impl.h -- copied from Trunnel v1.5.2 +/* trunnel-impl.h -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c index 3ae3fe02c8..01a55c5bec 100644 --- a/src/ext/trunnel/trunnel.c +++ b/src/ext/trunnel/trunnel.c @@ -1,4 +1,4 @@ -/* trunnel.c -- copied from Trunnel v1.5.2 +/* trunnel.c -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h index 9b708437b8..87c75f4ec3 100644 --- a/src/ext/trunnel/trunnel.h +++ b/src/ext/trunnel/trunnel.h @@ -1,4 +1,4 @@ -/* trunnel.h -- copied from Trunnel v1.5.2 +/* trunnel.h -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/feature/control/btrack.c b/src/feature/control/btrack.c index d3d12cb2b7..3ce97dc855 100644 --- a/src/feature/control/btrack.c +++ b/src/feature/control/btrack.c @@ -24,6 +24,7 @@ #include "feature/control/btrack_circuit.h" #include "feature/control/btrack_orconn.h" #include "feature/control/btrack_sys.h" +#include "lib/pubsub/pubsub.h" #include "lib/subsys/subsys.h" static int @@ -31,8 +32,6 @@ btrack_init(void) { if (btrack_orconn_init()) return -1; - if (btrack_circ_init()) - return -1; return 0; } @@ -44,10 +43,22 @@ btrack_fini(void) btrack_circ_fini(); } +static int +btrack_add_pubsub(pubsub_connector_t *connector) +{ + if (btrack_orconn_add_pubsub(connector)) + return -1; + if (btrack_circ_add_pubsub(connector)) + return -1; + + return 0; +} + const subsys_fns_t sys_btrack = { .name = "btrack", .supported = true, .level = -30, .initialize = btrack_init, .shutdown = btrack_fini, + .add_pubsub = btrack_add_pubsub, }; diff --git a/src/feature/control/btrack_circuit.c b/src/feature/control/btrack_circuit.c index dcee9e460e..2980c77ddc 100644 --- a/src/feature/control/btrack_circuit.c +++ b/src/feature/control/btrack_circuit.c @@ -109,51 +109,53 @@ btc_update_evtype(const ocirc_cevent_msg_t *msg, btc_best_t *best, return false; } +DECLARE_SUBSCRIBE(ocirc_state, btc_state_rcvr); +DECLARE_SUBSCRIBE(ocirc_cevent, btc_cevent_rcvr); +DECLARE_SUBSCRIBE(ocirc_chan, btc_chan_rcvr); + static void -btc_state_rcvr(const ocirc_state_msg_t *msg) +btc_state_rcvr(const msg_t *msg, const ocirc_state_msg_t *arg) { + (void)msg; log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" state=%d onehop=%d", - msg->gid, msg->state, msg->onehop); + arg->gid, arg->state, arg->onehop); - btc_update_state(msg, &best_any_state, "ANY"); - if (msg->onehop) + btc_update_state(arg, &best_any_state, "ANY"); + if (arg->onehop) return; - btc_update_state(msg, &best_ap_state, "AP"); + btc_update_state(arg, &best_ap_state, "AP"); } static void -btc_cevent_rcvr(const ocirc_cevent_msg_t *msg) +btc_cevent_rcvr(const msg_t *msg, const ocirc_cevent_msg_t *arg) { + (void)msg; log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" evtype=%d reason=%d onehop=%d", - msg->gid, msg->evtype, msg->reason, msg->onehop); + arg->gid, arg->evtype, arg->reason, arg->onehop); - btc_update_evtype(msg, &best_any_evtype, "ANY"); - if (msg->onehop) + btc_update_evtype(arg, &best_any_evtype, "ANY"); + if (arg->onehop) return; - btc_update_evtype(msg, &best_ap_evtype, "AP"); + btc_update_evtype(arg, &best_ap_evtype, "AP"); } static void -btc_event_rcvr(const ocirc_event_msg_t *msg) +btc_chan_rcvr(const msg_t *msg, const ocirc_chan_msg_t *arg) { - switch (msg->type) { - case OCIRC_MSGTYPE_STATE: - return btc_state_rcvr(&msg->u.state); - case OCIRC_MSGTYPE_CHAN: - log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" chan=%"PRIu64" onehop=%d", - msg->u.chan.gid, msg->u.chan.chan, msg->u.chan.onehop); - break; - case OCIRC_MSGTYPE_CEVENT: - return btc_cevent_rcvr(&msg->u.cevent); - default: - break; - } + (void)msg; + log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" chan=%"PRIu64" onehop=%d", + arg->gid, arg->chan, arg->onehop); } int -btrack_circ_init(void) +btrack_circ_add_pubsub(pubsub_connector_t *connector) { - ocirc_event_subscribe(btc_event_rcvr); + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_chan)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_cevent)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_state)) + return -1; return 0; } diff --git a/src/feature/control/btrack_circuit.h b/src/feature/control/btrack_circuit.h index 9e06fefb07..b326c22ccf 100644 --- a/src/feature/control/btrack_circuit.h +++ b/src/feature/control/btrack_circuit.h @@ -9,7 +9,10 @@ #ifndef TOR_BTRACK_CIRCUIT_H #define TOR_BTRACK_CIRCUIT_H +#include "lib/pubsub/pubsub.h" + int btrack_circ_init(void); void btrack_circ_fini(void); +int btrack_circ_add_pubsub(pubsub_connector_t *); #endif /* !defined(TOR_BTRACK_CIRCUIT_H) */ diff --git a/src/feature/control/btrack_orconn.c b/src/feature/control/btrack_orconn.c index 93ebe8d9cc..922b542a0c 100644 --- a/src/feature/control/btrack_orconn.c +++ b/src/feature/control/btrack_orconn.c @@ -45,6 +45,11 @@ #include "feature/control/btrack_orconn_cevent.h" #include "feature/control/btrack_orconn_maps.h" #include "lib/log/log.h" +#include "lib/pubsub/pubsub.h" + +DECLARE_SUBSCRIBE(orconn_state, bto_state_rcvr); +DECLARE_SUBSCRIBE(orconn_status, bto_status_rcvr); +DECLARE_SUBSCRIBE(ocirc_chan, bto_chan_rcvr); /** Pair of a best ORCONN GID and with its state */ typedef struct bto_best_t { @@ -110,16 +115,17 @@ bto_reset_bests(void) * message comes from code in connection_or.c. **/ static void -bto_state_rcvr(const orconn_state_msg_t *msg) +bto_state_rcvr(const msg_t *msg, const orconn_state_msg_t *arg) { bt_orconn_t *bto; - bto = bto_find_or_new(msg->gid, msg->chan); + (void)msg; + bto = bto_find_or_new(arg->gid, arg->chan); log_debug(LD_BTRACK, "ORCONN gid=%"PRIu64" chan=%"PRIu64 " proxy_type=%d state=%d", - msg->gid, msg->chan, msg->proxy_type, msg->state); - bto->proxy_type = msg->proxy_type; - bto->state = msg->state; + arg->gid, arg->chan, arg->proxy_type, arg->state); + bto->proxy_type = arg->proxy_type; + bto->state = arg->state; if (bto->is_orig) bto_update_bests(bto); } @@ -130,54 +136,38 @@ bto_state_rcvr(const orconn_state_msg_t *msg) * control.c. **/ static void -bto_status_rcvr(const orconn_status_msg_t *msg) +bto_status_rcvr(const msg_t *msg, const orconn_status_msg_t *arg) { - switch (msg->status) { + (void)msg; + switch (arg->status) { case OR_CONN_EVENT_FAILED: case OR_CONN_EVENT_CLOSED: log_info(LD_BTRACK, "ORCONN DELETE gid=%"PRIu64" status=%d reason=%d", - msg->gid, msg->status, msg->reason); - return bto_delete(msg->gid); + arg->gid, arg->status, arg->reason); + return bto_delete(arg->gid); default: break; } } -/** Dispatch to individual ORCONN message handlers */ -static void -bto_event_rcvr(const orconn_event_msg_t *msg) -{ - switch (msg->type) { - case ORCONN_MSGTYPE_STATE: - return bto_state_rcvr(&msg->u.state); - case ORCONN_MSGTYPE_STATUS: - return bto_status_rcvr(&msg->u.status); - default: - tor_assert(false); - } -} - /** * Create or update a cached ORCONN state for a newly launched * connection, including whether it's launched by an origin circuit * and whether it's a one-hop circuit. **/ static void -bto_chan_rcvr(const ocirc_event_msg_t *msg) +bto_chan_rcvr(const msg_t *msg, const ocirc_chan_msg_t *arg) { bt_orconn_t *bto; - /* Ignore other kinds of origin circuit events; we don't need them */ - if (msg->type != OCIRC_MSGTYPE_CHAN) - return; - - bto = bto_find_or_new(0, msg->u.chan.chan); - if (!bto->is_orig || (bto->is_onehop && !msg->u.chan.onehop)) { + (void)msg; + bto = bto_find_or_new(0, arg->chan); + if (!bto->is_orig || (bto->is_onehop && !arg->onehop)) { log_debug(LD_BTRACK, "ORCONN LAUNCH chan=%"PRIu64" onehop=%d", - msg->u.chan.chan, msg->u.chan.onehop); + arg->chan, arg->onehop); } bto->is_orig = true; - if (!msg->u.chan.onehop) + if (!arg->onehop) bto->is_onehop = false; bto_update_bests(bto); } @@ -190,12 +180,22 @@ int btrack_orconn_init(void) { bto_init_maps(); - orconn_event_subscribe(bto_event_rcvr); - ocirc_event_subscribe(bto_chan_rcvr); return 0; } +int +btrack_orconn_add_pubsub(pubsub_connector_t *connector) +{ + if (DISPATCH_ADD_SUB(connector, orconn, orconn_state)) + return -1; + if (DISPATCH_ADD_SUB(connector, orconn, orconn_status)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_chan)) + return -1; + return 0; +} + /** Clear the hash maps and reset the "best" states */ void btrack_orconn_fini(void) diff --git a/src/feature/control/btrack_orconn.h b/src/feature/control/btrack_orconn.h index f8f5c1096c..07b1b755f3 100644 --- a/src/feature/control/btrack_orconn.h +++ b/src/feature/control/btrack_orconn.h @@ -9,6 +9,8 @@ #ifndef TOR_BTRACK_ORCONN_H #define TOR_BTRACK_ORCONN_H +#include "lib/pubsub/pubsub.h" + #ifdef BTRACK_ORCONN_PRIVATE #include "ht.h" @@ -33,6 +35,7 @@ typedef struct bt_orconn_t { #endif /* defined(BTRACK_ORCONN_PRIVATE) */ int btrack_orconn_init(void); +int btrack_orconn_add_pubsub(pubsub_connector_t *); void btrack_orconn_fini(void); #endif /* !defined(TOR_BTRACK_ORCONN_H) */ diff --git a/src/feature/control/control_auth.c b/src/feature/control/control_auth.c index 49d4d415c6..a574d07b33 100644 --- a/src/feature/control/control_auth.c +++ b/src/feature/control/control_auth.c @@ -151,12 +151,8 @@ handle_control_authchallenge(control_connection_t *conn, goto fail; } if (args->kwargs == NULL || args->kwargs->next != NULL) { - /* connection_write_str_to_buf("512 AUTHCHALLENGE requires exactly " - "2 arguments.\r\n", conn); - */ - control_printf_endreply(conn, 512, - "AUTHCHALLENGE dislikes argument list %s", - escaped(args->raw_body)); + control_write_endreply(conn, 512, + "Wrong number of arguments for AUTHCHALLENGE"); goto fail; } if (strcmp(args->kwargs->key, "")) { diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index abb579bd43..ad4a4ef0af 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -703,9 +703,8 @@ handle_control_mapaddress(control_connection_t *conn, connection_buf_add(r, sz, TO_CONN(conn)); tor_free(r); } else { - const char *response = - "512 syntax error: not enough arguments to mapaddress.\r\n"; - connection_buf_add(response, strlen(response), TO_CONN(conn)); + control_write_endreply(conn, 512, "syntax error: " + "not enough arguments to mapaddress."); } SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp)); @@ -845,7 +844,7 @@ handle_control_extendcircuit(control_connection_t *conn, "addresses that are allowed by the firewall configuration; " "circuit marked for closing."); circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED); - connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); + control_write_endreply(conn, 551, "Couldn't start circuit"); goto done; } circuit_append_new_exit(circ, info); @@ -1744,16 +1743,10 @@ handle_control_add_onion(control_connection_t *conn, goto out; } else if (!strcasecmp(arg->key, "ClientAuth")) { - char *err_msg = NULL; int created = 0; rend_authorized_client_t *client = - add_onion_helper_clientauth(arg->value, - &created, &err_msg); + add_onion_helper_clientauth(arg->value, &created, conn); if (!client) { - if (err_msg) { - connection_write_str_to_buf(err_msg, conn); - tor_free(err_msg); - } goto out; } @@ -1818,19 +1811,13 @@ handle_control_add_onion(control_connection_t *conn, add_onion_secret_key_t pk = { NULL }; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; const char *onionkey = smartlist_get(args->args, 0); if (add_onion_helper_keyarg(onionkey, 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); - } + conn) < 0) { goto out; } - tor_assert(!err_msg); /* Hidden service version 3 don't have client authentication support so if * ClientAuth was given, send back an error. */ @@ -1876,8 +1863,8 @@ handle_control_add_onion(control_connection_t *conn, char *encoded = rend_auth_encode_cookie(ac->descriptor_cookie, auth_type); tor_assert(encoded); - connection_printf_to_buf(conn, "250-ClientAuth=%s:%s\r\n", - ac->client_name, encoded); + control_printf_midreply(conn, 250, "ClientAuth=%s:%s", + ac->client_name, encoded); memwipe(encoded, 0, strlen(encoded)); tor_free(encoded); }); @@ -1930,27 +1917,30 @@ handle_control_add_onion(control_connection_t *conn, * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated * and the private key not discarded, the algorithm and serialized private key, * or NULL and an optional control protocol error message on failure. The - * caller is responsible for freeing the returned key_new_blob and err_msg. + * caller is responsible for freeing the returned key_new_blob. * * Note: The error messages returned are deliberately vague to avoid echoing * key material. + * + * Note: conn is only used for writing control replies. For testing + * purposes, it can be NULL if control_write_reply() is appropriately + * mocked. */ 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) + control_connection_t *conn) { smartlist_t *key_args = smartlist_new(); crypto_pk_t *pk = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; int ret = -1; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { - err_msg = tor_strdup("512 Invalid key type/blob\r\n"); + control_write_endreply(conn, 512, "Invalid key type/blob"); goto err; } @@ -1967,12 +1957,12 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */ pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob)); if (!pk) { - err_msg = tor_strdup("512 Failed to decode RSA key\r\n"); + control_write_endreply(conn, 512, "Failed to decode RSA key"); 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"); + control_write_endreply(conn, 512, "Invalid RSA key size"); goto err; } decoded_key->v2 = pk; @@ -1983,7 +1973,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, 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"); + control_write_endreply(conn, 512, "Failed to decode ED25519-V3 key"); goto err; } decoded_key->v3 = sk; @@ -1995,15 +1985,15 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */ pk = crypto_pk_new(); if (crypto_pk_generate_key(pk)) { - tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", - key_type_rsa1024); + control_printf_endreply(conn, 551, "Failed to generate %s key", + key_type_rsa1024); goto err; } if (!discard_pk) { if (crypto_pk_base64_encode_private(pk, &key_new_blob)) { crypto_pk_free(pk); - tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", - key_type_rsa1024); + control_printf_endreply(conn, 551, "Failed to encode %s key", + key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; @@ -2014,8 +2004,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, 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); + control_printf_endreply(conn, 551, "Failed to generate %s key", + key_type_ed25519_v3); goto err; } if (!discard_pk) { @@ -2025,8 +2015,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, 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); + control_printf_endreply(conn, 551, "Failed to encode %s key", + key_type_ed25519_v3); goto err; } key_new_alg = key_type_ed25519_v3; @@ -2034,11 +2024,11 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, decoded_key->v3 = sk; *hs_version = HS_VERSION_THREE; } else { - err_msg = tor_strdup("513 Invalid key type\r\n"); + control_write_endreply(conn, 513, "Invalid key type"); goto err; } } else { - err_msg = tor_strdup("513 Invalid key type\r\n"); + control_write_endreply(conn, 513, "Invalid key type"); goto err; } @@ -2052,11 +2042,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args); - if (err_msg_out) { - *err_msg_out = err_msg; - } else { - tor_free(err_msg); - } *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob; @@ -2066,27 +2051,30 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /** Helper function to handle parsing a ClientAuth argument to the * ADD_ONION command. Return a new rend_authorized_client_t, or NULL * and an optional control protocol error message on failure. The - * caller is responsible for freeing the returned auth_client and err_msg. + * caller is responsible for freeing the returned auth_client. * * If 'created' is specified, it will be set to 1 when a new cookie has * been generated. + * + * Note: conn is only used for writing control replies. For testing + * purposes, it can be NULL if control_write_reply() is appropriately + * mocked. */ STATIC rend_authorized_client_t * -add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) +add_onion_helper_clientauth(const char *arg, int *created, + control_connection_t *conn) { int ok = 0; tor_assert(arg); tor_assert(created); - tor_assert(err_msg); - *err_msg = NULL; smartlist_t *auth_args = smartlist_new(); rend_authorized_client_t *client = tor_malloc_zero(sizeof(rend_authorized_client_t)); smartlist_split_string(auth_args, arg, ":", 0, 0); if (smartlist_len(auth_args) < 1 || smartlist_len(auth_args) > 2) { - *err_msg = tor_strdup("512 Invalid ClientAuth syntax\r\n"); + control_write_endreply(conn, 512, "Invalid ClientAuth syntax"); goto err; } client->client_name = tor_strdup(smartlist_get(auth_args, 0)); @@ -2096,7 +2084,7 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) client->descriptor_cookie, NULL, &decode_err_msg) < 0) { tor_assert(decode_err_msg); - tor_asprintf(err_msg, "512 %s\r\n", decode_err_msg); + control_write_endreply(conn, 512, decode_err_msg); tor_free(decode_err_msg); goto err; } @@ -2107,7 +2095,7 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) } if (!rend_valid_client_name(client->client_name)) { - *err_msg = tor_strdup("512 Invalid name in ClientAuth\r\n"); + control_write_endreply(conn, 512, "Invalid name in ClientAuth"); goto err; } diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h index 5c3d1a1cec..4b6d54abe7 100644 --- a/src/feature/control/control_cmd.h +++ b/src/feature/control/control_cmd.h @@ -91,10 +91,11 @@ 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); + int *hs_version, + control_connection_t *conn); STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg, - int *created, char **err_msg_out); + int *created, control_connection_t *conn); STATIC control_cmd_args_t *control_cmd_parse_args( const char *command, diff --git a/src/feature/control/control_proto.c b/src/feature/control/control_proto.c index 1dd62da2be..d2541e7308 100644 --- a/src/feature/control/control_proto.c +++ b/src/feature/control/control_proto.c @@ -176,8 +176,9 @@ send_control_done(control_connection_t *conn) * @param c separator character, usually ' ', '-', or '+' * @param s string */ -void -control_write_reply(control_connection_t *conn, int code, int c, const char *s) +MOCK_IMPL(void, +control_write_reply, (control_connection_t *conn, int code, int c, + const char *s)) { connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s); } diff --git a/src/feature/control/control_proto.h b/src/feature/control/control_proto.h index 101b808d88..3182f3d415 100644 --- a/src/feature/control/control_proto.h +++ b/src/feature/control/control_proto.h @@ -21,8 +21,8 @@ size_t write_escaped_data(const char *data, size_t len, char **out); size_t read_escaped_data(const char *data, size_t len, char **out); void send_control_done(control_connection_t *conn); -void control_write_reply(control_connection_t *conn, int code, int c, - const char *s); +MOCK_DECL(void, control_write_reply, (control_connection_t *conn, int code, + int c, const char *s)); void control_vprintf_reply(control_connection_t *conn, int code, int c, const char *fmt, va_list ap) CHECK_PRINTF(4, 0); diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index cdbdf5a216..043bbfc227 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -220,7 +220,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, networkstatus_t *v3_ns) { smartlist_t *chunks = smartlist_new(); - char *packages = NULL; char fingerprint[FINGERPRINT_LEN+1]; char digest[DIGEST_LEN]; uint32_t addr; @@ -246,19 +245,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, v3_ns->server_versions); protocols_lines = format_protocols_lines_for_vote(v3_ns); - if (v3_ns->package_lines) { - smartlist_t *tmp = smartlist_new(); - SMARTLIST_FOREACH(v3_ns->package_lines, const char *, p, - if (validate_recommended_package_line(p)) - smartlist_add_asprintf(tmp, "package %s\n", p)); - smartlist_sort_strings(tmp); - packages = smartlist_join_strings(tmp, "", 0, NULL); - SMARTLIST_FOREACH(tmp, char *, cp, tor_free(cp)); - smartlist_free(tmp); - } else { - packages = tor_strdup(""); - } - /* Get shared random commitments/reveals line(s). */ shared_random_vote_str = sr_get_string_for_vote(); @@ -344,7 +330,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, "voting-delay %d %d\n" "%s%s" /* versions */ "%s" /* protocols */ - "%s" /* packages */ "known-flags %s\n" "flag-thresholds %s\n" "params %s\n" @@ -361,7 +346,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, client_versions_line, server_versions_line, protocols_lines, - packages, flags, flag_thresholds, params, @@ -460,7 +444,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(client_versions_line); tor_free(server_versions_line); tor_free(protocols_lines); - tor_free(packages); SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); @@ -4668,15 +4651,6 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, tor_assert_nonfatal(protover_all_supported( v3_out->recommended_client_protocols, NULL)); - v3_out->package_lines = smartlist_new(); - { - config_line_t *cl; - for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { - if (validate_recommended_package_line(cl->value)) - smartlist_add_strdup(v3_out->package_lines, cl->value); - } - } - v3_out->known_flags = smartlist_new(); smartlist_split_string(v3_out->known_flags, DIRVOTE_UNIVERSAL_FLAGS, diff --git a/src/feature/dirauth/keypin.c b/src/feature/dirauth/keypin.c index 667feb2c03..3ca2c3ef91 100644 --- a/src/feature/dirauth/keypin.c +++ b/src/feature/dirauth/keypin.c @@ -438,7 +438,7 @@ keypin_load_journal_impl(const char *data, size_t size) tor_log(severity, LD_DIRSERV, "Loaded %d entries from keypin journal. " "Found %d corrupt lines (ignored), %d duplicates (harmless), " - "and %d conflicts (resolved in favor or more recent entry).", + "and %d conflicts (resolved in favor of more recent entry).", n_entries, n_corrupt_lines, n_duplicates, n_conflicts); return 0; diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 656922233e..a68d155651 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -216,9 +216,14 @@ dirserv_load_fingerprint_file(void) #define DISABLE_DISABLING_ED25519 -/** Check whether <b>router</b> has a nickname/identity key combination that - * we recognize from the fingerprint list, or an IP we automatically act on - * according to our configuration. Return the appropriate router status. +/** Check whether <b>router</b> has: + * - a nickname/identity key combination that we recognize from the fingerprint + * list, + * - an IP we automatically act on according to our configuration, + * - an appropriate version, and + * - matching pinned keys. + * + * Return the appropriate router status. * * If the status is 'FP_REJECT' and <b>msg</b> is provided, set * *<b>msg</b> to an explanation of why. */ @@ -236,7 +241,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, return FP_REJECT; } - /* Check for the more usual versions to reject a router first. */ + /* Check for the more common reasons to reject a router first. */ const uint32_t r = dirserv_get_status_impl(d, router->nickname, router->addr, router->or_port, router->platform, msg, severity); @@ -535,7 +540,7 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, int general = purpose == ROUTER_PURPOSE_GENERAL; tor_assert(msg); - r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */ + r=ROUTER_ADDED_SUCCESSFULLY; /* Least severe return value. */ if (!string_is_utf8_no_bom(desc, desclen)) { *msg = "descriptor(s) or extrainfo(s) not valid UTF-8 or had BOM."; @@ -551,9 +556,7 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, !general ? router_purpose_to_string(purpose) : "", !general ? "\n" : "")<0) { *msg = "Couldn't format annotations"; - /* XXX Not cool: we return -1 below, but (was_router_added_t)-1 is - * ROUTER_BAD_EI, which isn't what's gone wrong here. :( */ - return -1; + return ROUTER_AUTHDIR_BUG_ANNOTATIONS; } s = desc; diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c index b669e3836e..b2c7acba1a 100644 --- a/src/feature/dirauth/shared_random_state.c +++ b/src/feature/dirauth/shared_random_state.c @@ -68,7 +68,7 @@ static void disk_state_free_cb(void *); /* Array of variables that are saved to disk as a persistent state. */ static config_var_t state_vars[] = { - V(Version, UINT, "0"), + V(Version, POSINT, "0"), V(TorVersion, STRING, NULL), V(ValidAfter, ISOTIME, NULL), V(ValidUntil, ISOTIME, NULL), diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index 1b36f716f4..7c6af3582b 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -1390,8 +1390,9 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn, const char *pubkey_str = NULL; const char *url = args->url; - /* Reject unencrypted dir connections */ - if (!connection_dir_is_encrypted(conn)) { + /* Reject non anonymous dir connections (which also tests if encrypted). We + * do not allow single hop clients to query an HSDir. */ + if (!connection_dir_is_anonymous(conn)) { write_short_http_response(conn, 404, "Not found"); goto done; } @@ -1632,10 +1633,10 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, goto done; } - /* Handle HS descriptor publish request. */ - /* XXX: This should be disabled with a consensus param until we want to - * the prop224 be deployed and thus use. */ - if (connection_dir_is_encrypted(conn) && !strcmpstart(url, "/tor/hs/")) { + /* Handle HS descriptor publish request. We force an anonymous connection + * (which also tests for encrypted). We do not allow single-hop client to + * post a descriptor onto an HSDir. */ + if (connection_dir_is_anonymous(conn) && !strcmpstart(url, "/tor/hs/")) { const char *msg = "HS descriptor stored successfully."; /* We most probably have a publish request for an HS descriptor. */ diff --git a/src/feature/dircommon/directory.c b/src/feature/dircommon/directory.c index 9e6f72e9ac..b3db0aa108 100644 --- a/src/feature/dircommon/directory.c +++ b/src/feature/dircommon/directory.c @@ -7,6 +7,10 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" +#include "core/or/circuitlist.h" +#include "core/or/connection_edge.h" +#include "core/or/connection_or.h" +#include "core/or/channeltls.h" #include "feature/dircache/dircache.h" #include "feature/dircache/dirserv.h" #include "feature/dirclient/dirclient.h" @@ -15,6 +19,10 @@ #include "feature/stats/geoip_stats.h" #include "lib/compress/compress.h" +#include "core/or/circuit_st.h" +#include "core/or/or_circuit_st.h" +#include "core/or/edge_connection_st.h" +#include "core/or/or_connection_st.h" #include "feature/dircommon/dir_connection_st.h" #include "feature/nodelist/routerinfo_st.h" @@ -167,6 +175,67 @@ connection_dir_is_encrypted(const dir_connection_t *conn) return TO_CONN(conn)->linked; } +/** Return true iff the given directory connection <b>dir_conn</b> is + * anonymous, that is, it is on a circuit via a public relay and not directly + * from a client or bridge. + * + * For client circuits via relays: true for 2-hop+ paths. + * For client circuits via bridges: true for 3-hop+ paths. + * + * This first test if the connection is encrypted since it is a strong + * requirement for anonymity. */ +bool +connection_dir_is_anonymous(const dir_connection_t *dir_conn) +{ + const connection_t *conn, *linked_conn; + const edge_connection_t *edge_conn; + const circuit_t *circ; + + tor_assert(dir_conn); + + if (!connection_dir_is_encrypted(dir_conn)) { + return false; + } + + /* + * Buckle up, we'll do a deep dive into the connection in order to get the + * final connection channel of that connection in order to figure out if + * this is a client or relay link. + * + * We go: dir_conn -> linked_conn -> edge_conn -> on_circuit -> p_chan. + */ + + conn = TO_CONN(dir_conn); + linked_conn = conn->linked_conn; + + /* The dir connection should be connected to an edge connection. It can not + * be closed or marked for close. */ + if (linked_conn == NULL || linked_conn->magic != EDGE_CONNECTION_MAGIC || + conn->linked_conn_is_closed || conn->linked_conn->marked_for_close) { + log_info(LD_DIR, "Rejected HSDir request: not linked to edge"); + return false; + } + + edge_conn = TO_EDGE_CONN((connection_t *) linked_conn); + circ = edge_conn->on_circuit; + + /* Can't be a circuit we initiated and without a circuit, no channel. */ + if (circ == NULL || CIRCUIT_IS_ORIGIN(circ)) { + log_info(LD_DIR, "Rejected HSDir request: not on OR circuit"); + return false; + } + + /* Get the previous channel to learn if it is a client or relay link. */ + if (BUG(CONST_TO_OR_CIRCUIT(circ)->p_chan == NULL)) { + log_info(LD_DIR, "Rejected HSDir request: no p_chan"); + return false; + } + + /* Will be true if the channel is an unauthenticated peer which is only true + * for clients and bridges. */ + return !channel_is_client(CONST_TO_OR_CIRCUIT(circ)->p_chan); +} + /** Parse an HTTP request line at the start of a headers string. On failure, * return -1. On success, set *<b>command_out</b> to a copy of the HTTP * command ("get", "post", etc), set *<b>url_out</b> to a copy of the URL, and diff --git a/src/feature/dircommon/directory.h b/src/feature/dircommon/directory.h index ba3f8c1b0e..4fc743ad3d 100644 --- a/src/feature/dircommon/directory.h +++ b/src/feature/dircommon/directory.h @@ -94,6 +94,7 @@ int parse_http_command(const char *headers, char *http_get_header(const char *headers, const char *which); int connection_dir_is_encrypted(const dir_connection_t *conn); +bool connection_dir_is_anonymous(const dir_connection_t *conn); int connection_dir_reached_eof(dir_connection_t *conn); int connection_dir_process_inbuf(dir_connection_t *conn); int connection_dir_finished_flushing(dir_connection_t *conn); diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 05f9940ae6..9817113b23 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -710,6 +710,11 @@ cache_clean_v3_as_client(time_t now) MAP_DEL_CURRENT(key); entry_size = cache_get_client_entry_size(entry); bytes_removed += entry_size; + /* We just removed an old descriptor. We need to close all intro circuits + * so we don't have leftovers that can be selected while lacking a + * descriptor. We leave the rendezvous circuits opened because they could + * be in use. */ + hs_client_close_intro_circuits_from_desc(entry->desc); /* Entry is not in the cache anymore, destroy it. */ cache_client_desc_free(entry); /* Update our OOM. We didn't use the remove() function because we are in diff --git a/src/feature/hs/hs_circuitmap.c b/src/feature/hs/hs_circuitmap.c index 5480d5eb84..e34f564fb4 100644 --- a/src/feature/hs/hs_circuitmap.c +++ b/src/feature/hs/hs_circuitmap.c @@ -272,6 +272,33 @@ hs_circuitmap_get_or_circuit(hs_token_type_t type, /**** Public relay-side getters: */ +/* Public function: Return v2 and v3 introduction circuit to this relay. + * Always return a newly allocated list for which it is the caller's + * responsability to free it. */ +smartlist_t * +hs_circuitmap_get_all_intro_circ_relay_side(void) +{ + circuit_t **iter; + smartlist_t *circuit_list = smartlist_new(); + + HT_FOREACH(iter, hs_circuitmap_ht, the_hs_circuitmap) { + circuit_t *circ = *iter; + + /* An origin circuit or purpose is wrong or the hs token is not set to be + * a v2 or v3 intro relay side type, we ignore the circuit. Else, we have + * a match so add it to our list. */ + if (CIRCUIT_IS_ORIGIN(circ) || + circ->purpose != CIRCUIT_PURPOSE_INTRO_POINT || + (circ->hs_token->type != HS_TOKEN_INTRO_V3_RELAY_SIDE && + circ->hs_token->type != HS_TOKEN_INTRO_V2_RELAY_SIDE)) { + continue; + } + smartlist_add(circuit_list, circ); + } + + return circuit_list; +} + /* Public function: Return a v3 introduction circuit to this relay with * <b>auth_key</b>. Return NULL if no such circuit is found in the * circuitmap. */ diff --git a/src/feature/hs/hs_circuitmap.h b/src/feature/hs/hs_circuitmap.h index c1bbb1ff1c..eac8230bbf 100644 --- a/src/feature/hs/hs_circuitmap.h +++ b/src/feature/hs/hs_circuitmap.h @@ -34,6 +34,8 @@ void hs_circuitmap_register_intro_circ_v2_relay_side(struct or_circuit_t *circ, void hs_circuitmap_register_intro_circ_v3_relay_side(struct or_circuit_t *circ, const ed25519_public_key_t *auth_key); +smartlist_t *hs_circuitmap_get_all_intro_circ_relay_side(void); + /** Public service-side API: */ struct origin_circuit_t * diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index a5747fe170..8661ce046a 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -21,6 +21,7 @@ #include "feature/hs/hs_circuitmap.h" #include "feature/hs/hs_client.h" #include "feature/hs/hs_common.h" +#include "feature/hs/hs_dos.h" #include "feature/hs/hs_ident.h" #include "feature/hs/hs_service.h" #include "feature/hs_common/shared_random_client.h" @@ -30,6 +31,7 @@ #include "feature/nodelist/routerset.h" #include "feature/rend/rendcommon.h" #include "feature/rend/rendservice.h" +#include "feature/relay/routermode.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/feature/hs/hs_dos.c b/src/feature/hs/hs_dos.c new file mode 100644 index 0000000000..a4586dd700 --- /dev/null +++ b/src/feature/hs/hs_dos.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_dos.c + * \brief Implement denial of service mitigation for the onion service + * subsystem. + * + * This module defenses: + * + * - Introduction Rate Limiting: If enabled by the consensus, an introduction + * point will rate limit client introduction towards the service (INTRODUCE2 + * cells). It uses a token bucket model with a rate and burst per second. + * + * Proposal 305 will expand this module by allowing an operator to define + * these values into the ESTABLISH_INTRO cell. Not yet implemented. + **/ + +#define HS_DOS_PRIVATE + +#include "core/or/or.h" +#include "app/config/config.h" + +#include "core/or/circuitlist.h" + +#include "feature/hs/hs_circuitmap.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/relay/routermode.h" + +#include "lib/evloop/token_bucket.h" + +#include "hs_dos.h" + +/* Default value of the allowed INTRODUCE2 cell rate per second. Above that + * value per second, the introduction is denied. */ +#define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25 + +/* Default value of the allowed INTRODUCE2 cell burst per second. This is the + * maximum value a token bucket has per second. We thus allow up to this value + * of INTRODUCE2 cell per second but the bucket is refilled by the rate value + * but never goes above that burst value. */ +#define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200 + +/* Default value of the consensus parameter enabling or disabling the + * introduction DoS defense. Disabled by default. */ +#define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0 + +/* Consensus parameters. */ +static uint32_t hs_dos_introduce_rate_per_sec = + HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC; +static uint32_t hs_dos_introduce_burst_per_sec = + HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC; +static uint32_t hs_dos_introduce_enabled = + HS_DOS_INTRODUCE_ENABLED_DEFAULT; + +static uint32_t +get_param_intro_dos_enabled(const networkstatus_t *ns) +{ + return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense", + HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1); +} + +/* Return the parameter for the introduction rate per sec. */ +static uint32_t +get_param_rate_per_sec(const networkstatus_t *ns) +{ + return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec", + HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC, + 0, INT32_MAX); +} + +/* Return the parameter for the introduction burst per sec. */ +static uint32_t +get_param_burst_per_sec(const networkstatus_t *ns) +{ + return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec", + HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC, + 0, INT32_MAX); +} + +/* Go over all introduction circuit relay side and adjust their rate/burst + * values using the global parameters. This is called right after the + * consensus parameters might have changed. */ +static void +update_intro_circuits(void) +{ + /* Returns all HS version intro circuits. */ + smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side(); + + SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) { + /* Adjust the rate/burst value that might have changed. */ + token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket, + hs_dos_get_intro2_rate(), + hs_dos_get_intro2_burst()); + } SMARTLIST_FOREACH_END(circ); + + smartlist_free(intro_circs); +} + +/* Set consensus parameters. */ +static void +set_consensus_parameters(const networkstatus_t *ns) +{ + hs_dos_introduce_rate_per_sec = get_param_rate_per_sec(ns); + hs_dos_introduce_burst_per_sec = get_param_burst_per_sec(ns); + hs_dos_introduce_enabled = get_param_intro_dos_enabled(ns); + + /* The above might have changed which means we need to go through all + * introduction circuits (relay side) and update the token buckets. */ + update_intro_circuits(); +} + +/* + * Public API. + */ + +/* Return the INTRODUCE2 cell rate per second. */ +uint32_t +hs_dos_get_intro2_rate(void) +{ + return hs_dos_introduce_rate_per_sec; +} + +/* Return the INTRODUCE2 cell burst per second. */ +uint32_t +hs_dos_get_intro2_burst(void) +{ + return hs_dos_introduce_burst_per_sec; +} + +/* Called when the consensus has changed. We might have new consensus + * parameters to look at. */ +void +hs_dos_consensus_has_changed(const networkstatus_t *ns) +{ + /* No point on updating these values if we are not a public relay that can + * be picked to be an introduction point. */ + if (!public_server_mode(get_options())) { + return; + } + + set_consensus_parameters(ns); +} + +/* Return true iff an INTRODUCE2 cell can be sent on the given service + * introduction circuit. */ +bool +hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) +{ + tor_assert(s_intro_circ); + + /* Always allowed if the defense is disabled. */ + if (!hs_dos_introduce_enabled) { + return true; + } + + /* Should not happen but if so, scream loudly. */ + if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) { + return false; + } + + /* This is called just after we got a valid and parsed INTRODUCE1 cell. The + * service has been found and we have its introduction circuit. + * + * First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented + * because we are about to send or not the cell we just got. Finally, + * evaluate if we can send it based on our token bucket state. */ + + /* Refill INTRODUCE2 bucket. */ + token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket, + (uint32_t) approx_time()); + + /* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't + * underflow else we end up with a too big of a bucket. */ + if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) { + token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1); + } + + /* Finally, we can send a new INTRODUCE2 if there are still tokens. */ + return token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0; +} + +/* Initialize the onion service Denial of Service subsystem. */ +void +hs_dos_init(void) +{ + set_consensus_parameters(NULL); +} diff --git a/src/feature/hs/hs_dos.h b/src/feature/hs/hs_dos.h new file mode 100644 index 0000000000..9fba00b52b --- /dev/null +++ b/src/feature/hs/hs_dos.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_dos.h + * \brief Header file containing denial of service defenses for the HS + * subsystem for all versions. + **/ + +#ifndef TOR_HS_DOS_H +#define TOR_HS_DOS_H + +#include "core/or/or_circuit_st.h" + +#include "feature/nodelist/networkstatus_st.h" + +/* Init */ +void hs_dos_init(void); + +/* Consensus. */ +void hs_dos_consensus_has_changed(const networkstatus_t *ns); + +bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ); + +/* Getters. */ +uint32_t hs_dos_get_intro2_rate(void); +uint32_t hs_dos_get_intro2_burst(void); + +#ifdef HS_DOS_PRIVATE + +#ifdef TOR_UNIT_TESTS + +#endif /* define(TOR_UNIT_TESTS) */ + +#endif /* defined(HS_DOS_PRIVATE) */ + +#endif /* !defined(TOR_HS_DOS_H) */ diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c index 9333060e7e..2c105f0b60 100644 --- a/src/feature/hs/hs_intropoint.c +++ b/src/feature/hs/hs_intropoint.c @@ -10,6 +10,7 @@ #include "core/or/or.h" #include "app/config/config.h" +#include "core/or/channel.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/relay.h" @@ -24,9 +25,10 @@ #include "trunnel/hs/cell_introduce1.h" #include "feature/hs/hs_circuitmap.h" +#include "feature/hs/hs_common.h" #include "feature/hs/hs_descriptor.h" +#include "feature/hs/hs_dos.h" #include "feature/hs/hs_intropoint.h" -#include "feature/hs/hs_common.h" #include "core/or/or_circuit_st.h" @@ -202,6 +204,9 @@ handle_verified_establish_intro_cell(or_circuit_t *circ, hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key); /* Repurpose this circuit into an intro circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); + /* Initialize the INTRODUCE2 token bucket for the rate limiting. */ + token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(), + hs_dos_get_intro2_burst(), (uint32_t) approx_time()); return 0; } @@ -480,6 +485,20 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request, } } + /* Before sending, lets make sure this cell can be sent on the service + * circuit asking the DoS defenses. */ + if (!hs_dos_can_send_intro2(service_circ)) { + char *msg; + static ratelim_t rlimit = RATELIM_INIT(5 * 60); + if ((msg = rate_limit_log(&rlimit, approx_time()))) { + log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v3 cell due to DoS " + "limitations. Sending NACK to client."); + tor_free(msg); + } + status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID; + goto send_ack; + } + /* Relay the cell to the service on its intro circuit with an INTRODUCE2 * cell which is the same exact payload. */ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ), @@ -546,6 +565,14 @@ circuit_is_suitable_for_introduce1(const or_circuit_t *circ) return 0; } + /* Disallow single hop client circuit. */ + if (circ->p_chan && channel_is_client(circ->p_chan)) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Single hop client was rejected while trying to introduce. " + "Closing circuit."); + return 0; + } + return 1; } diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index 2db293a8af..496bafb865 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -68,6 +68,7 @@ #include "feature/dircommon/voting_schedule.h" #include "feature/dirparse/ns_parse.h" #include "feature/hibernate/hibernate.h" +#include "feature/hs/hs_dos.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/fmt_routerstatus.h" @@ -1674,6 +1675,7 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, notify_control_networkstatus_changed(old_c, new_c); dos_consensus_has_changed(new_c); relay_consensus_has_changed(new_c); + hs_dos_consensus_has_changed(new_c); } /* Called after a new consensus has been put in the global state. It is safe diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index c56b714cb0..0cd7a76a9a 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -1459,12 +1459,13 @@ router_descriptor_is_older_than,(const routerinfo_t *router, int seconds)) } /** Add <b>router</b> to the routerlist, if we don't already have it. Replace - * older entries (if any) with the same key. Note: Callers should not hold - * their pointers to <b>router</b> if this function fails; <b>router</b> - * will either be inserted into the routerlist or freed. Similarly, even - * if this call succeeds, they should not hold their pointers to - * <b>router</b> after subsequent calls with other routerinfo's -- they - * might cause the original routerinfo to get freed. + * older entries (if any) with the same key. + * + * Note: Callers should not hold their pointers to <b>router</b> if this + * function fails; <b>router</b> will either be inserted into the routerlist or + * freed. Similarly, even if this call succeeds, they should not hold their + * pointers to <b>router</b> after subsequent calls with other routerinfo's -- + * they might cause the original routerinfo to get freed. * * Returns the status for the operation. Might set *<b>msg</b> if it wants * the poster of the router to know something. diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h index 5771ebb1ab..dc9203e015 100644 --- a/src/feature/nodelist/routerlist.h +++ b/src/feature/nodelist/routerlist.h @@ -37,9 +37,12 @@ typedef enum was_router_added_t { ROUTER_WAS_NOT_WANTED = -6, /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ - ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ - /* DOCDOC */ - ROUTER_CERTS_EXPIRED = -8 + ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'ROUTER_IS_ALREADY_KNOWN' */ + /* Some certs on this router are expired. */ + ROUTER_CERTS_EXPIRED = -8, + /* We couldn't format the annotations for this router. This is a directory + * authority bug. */ + ROUTER_AUTHDIR_BUG_ANNOTATIONS = -10 } was_router_added_t; /** How long do we avoid using a directory server after it's given us a 503? */ diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 25bb1835c2..51ced6289d 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -3113,33 +3113,22 @@ load_stats_file(const char *filename, const char *end_line, time_t now, return r; } -/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them - * with <b>ident_key</b>. - * - * If ExtraInfoStatistics is 1, also write aggregated statistics and related - * configuration data before signing. Most statistics also have an option that - * enables or disables that particular statistic. - * - * Return 0 on success, negative on failure. */ -int -extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, - crypto_pk_t *ident_key, - const ed25519_keypair_t *signing_keypair) +/** Add header strings to chunks, based on the extrainfo object extrainfo, + * and ed25519 keypair signing_keypair, if emit_ed_sigs is true. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_header_helper( + smartlist_t *chunks, + const extrainfo_t *extrainfo, + const ed25519_keypair_t *signing_keypair, + int emit_ed_sigs) { - const or_options_t *options = get_options(); char identity[HEX_DIGEST_LEN+1]; char published[ISO_TIME_LEN+1]; - char digest[DIGEST_LEN]; - int result; - static int write_stats_to_extrainfo = 1; - char sig[DIROBJ_MAX_SIG_LEN+1]; - char *s = NULL, *pre, *contents, *cp, *s_dup = NULL; - time_t now = time(NULL); - smartlist_t *chunks = smartlist_new(); - extrainfo_t *ei_tmp = NULL; - const int emit_ed_sigs = signing_keypair && - extrainfo->cache_info.signing_key_cert; char *ed_cert_line = NULL; + char *pre = NULL; + int rv = -1; base16_encode(identity, sizeof(identity), extrainfo->cache_info.identity_digest, DIGEST_LEN); @@ -3169,12 +3158,41 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, ed_cert_line = tor_strdup(""); } + /* This is the first chunk in the file. If the file is too big, other chunks + * are removed. So we must only add one chunk here. */ tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n", extrainfo->nickname, identity, ed_cert_line, published); smartlist_add(chunks, pre); + rv = 0; + goto done; + + err: + rv = -1; + + done: + tor_free(ed_cert_line); + return rv; +} + +/** Add pluggable transport and statistics strings to chunks, skipping + * statistics if write_stats_to_extrainfo is false. + * Helper for extrainfo_dump_to_string(). + * Can not fail. */ +static void +extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, + int write_stats_to_extrainfo) +{ + const or_options_t *options = get_options(); + char *contents = NULL; + time_t now = time(NULL); + + /* If the file is too big, these chunks are removed, starting with the last + * chunk. So each chunk must be a complete line, and the file must be valid + * after each chunk. */ + /* Add information about the pluggable transports we support, even if we * are not publishing statistics. This information is needed by BridgeDB * to distribute bridges. */ @@ -3241,34 +3259,132 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } } +} + +/** Add an ed25519 signature of chunks to chunks, using the ed25519 keypair + * signing_keypair. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_ed_sig_helper( + smartlist_t *chunks, + const ed25519_keypair_t *signing_keypair) +{ + char sha256_digest[DIGEST256_LEN]; + ed25519_signature_t ed_sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + int rv = -1; + + /* These are two of the three final chunks in the file. If the file is too + * big, other chunks are removed. So we must only add two chunks here. */ + smartlist_add_strdup(chunks, "router-sig-ed25519 "); + crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + ed25519_signature_to_base64(buf, &ed_sig); + + smartlist_add_asprintf(chunks, "%s\n", buf); + + rv = 0; + goto done; + + err: + rv = -1; + + done: + return rv; +} + +/** Add an RSA signature of extrainfo_string to chunks, using the RSA key + * ident_key. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_rsa_sig_helper(smartlist_t *chunks, + crypto_pk_t *ident_key, + const char *extrainfo_string) +{ + char sig[DIROBJ_MAX_SIG_LEN+1]; + char digest[DIGEST_LEN]; + int rv = -1; + + memset(sig, 0, sizeof(sig)); + if (router_get_extrainfo_hash(extrainfo_string, strlen(extrainfo_string), + digest) < 0 || + router_append_dirobj_signature(sig, sizeof(sig), digest, DIGEST_LEN, + ident_key) < 0) { + log_warn(LD_BUG, "Could not append signature to extra-info " + "descriptor."); + goto err; + } + smartlist_add_strdup(chunks, sig); + + rv = 0; + goto done; + + err: + rv = -1; + + done: + return rv; +} + +/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them + * with <b>ident_key</b>. + * + * If ExtraInfoStatistics is 1, also write aggregated statistics and related + * configuration data before signing. Most statistics also have an option that + * enables or disables that particular statistic. + * + * Always write pluggable transport lines. + * + * Return 0 on success, negative on failure. */ +int +extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair) +{ + int result; + static int write_stats_to_extrainfo = 1; + char *s = NULL, *cp, *s_dup = NULL; + smartlist_t *chunks = smartlist_new(); + extrainfo_t *ei_tmp = NULL; + const int emit_ed_sigs = signing_keypair && + extrainfo->cache_info.signing_key_cert; + int rv = 0; + + rv = extrainfo_dump_to_string_header_helper(chunks, extrainfo, + signing_keypair, + emit_ed_sigs); + if (rv < 0) + goto err; + + extrainfo_dump_to_string_stats_helper(chunks, write_stats_to_extrainfo); if (emit_ed_sigs) { - char sha256_digest[DIGEST256_LEN]; - smartlist_add_strdup(chunks, "router-sig-ed25519 "); - crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN, - ED_DESC_SIGNATURE_PREFIX, - chunks, "", DIGEST_SHA256); - ed25519_signature_t ed_sig; - char buf[ED25519_SIG_BASE64_LEN+1]; - if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN, - signing_keypair) < 0) + rv = extrainfo_dump_to_string_ed_sig_helper(chunks, signing_keypair); + if (rv < 0) goto err; - ed25519_signature_to_base64(buf, &ed_sig); - - smartlist_add_asprintf(chunks, "%s\n", buf); } + /* This is one of the three final chunks in the file. If the file is too big, + * other chunks are removed. So we must only add one chunk here. */ smartlist_add_strdup(chunks, "router-signature\n"); s = smartlist_join_strings(chunks, "", 0, NULL); while (strlen(s) > MAX_EXTRAINFO_UPLOAD_SIZE - DIROBJ_MAX_SIG_LEN) { /* So long as there are at least two chunks (one for the initial * extra-info line and one for the router-signature), we can keep removing - * things. */ - if (smartlist_len(chunks) > 2) { - /* We remove the next-to-last element (remember, len-1 is the last - element), since we need to keep the router-signature element. */ - int idx = smartlist_len(chunks) - 2; + * things. If emit_ed_sigs is true, we also keep 2 additional chunks at the + * end for the ed25519 signature. */ + const int required_chunks = emit_ed_sigs ? 4 : 2; + if (smartlist_len(chunks) > required_chunks) { + /* We remove the next-to-last or 4th-last element (remember, len-1 is the + * last element), since we need to keep the router-signature elements. */ + int idx = smartlist_len(chunks) - required_chunks; char *e = smartlist_get(chunks, idx); smartlist_del_keeporder(chunks, idx); log_warn(LD_GENERAL, "We just generated an extra-info descriptor " @@ -3285,15 +3401,10 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } - memset(sig, 0, sizeof(sig)); - if (router_get_extrainfo_hash(s, strlen(s), digest) < 0 || - router_append_dirobj_signature(sig, sizeof(sig), digest, DIGEST_LEN, - ident_key) < 0) { - log_warn(LD_BUG, "Could not append signature to extra-info " - "descriptor."); + rv = extrainfo_dump_to_string_rsa_sig_helper(chunks, ident_key, s); + if (rv < 0) goto err; - } - smartlist_add_strdup(chunks, sig); + tor_free(s); s = smartlist_join_strings(chunks, "", 0, NULL); @@ -3329,7 +3440,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk)); smartlist_free(chunks); tor_free(s_dup); - tor_free(ed_cert_line); extrainfo_free(ei_tmp); return result; diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c index 849f355990..ef2a44c40d 100644 --- a/src/feature/rend/rendmid.c +++ b/src/feature/rend/rendmid.c @@ -18,6 +18,7 @@ #include "feature/rend/rendmid.h" #include "feature/stats/rephist.h" #include "feature/hs/hs_circuitmap.h" +#include "feature/hs/hs_dos.h" #include "feature/hs/hs_intropoint.h" #include "core/or/or_circuit_st.h" @@ -116,6 +117,8 @@ rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request, /* Now, set up this circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); hs_circuitmap_register_intro_circ_v2_relay_side(circ, (uint8_t *)pk_digest); + token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(), + hs_dos_get_intro2_burst(), (uint32_t) approx_time()); log_info(LD_REND, "Established introduction point on circuit %u for service %s", @@ -180,6 +183,14 @@ rend_mid_introduce_legacy(or_circuit_t *circ, const uint8_t *request, goto err; } + /* Before sending, lets make sure this cell can be sent on the service + * circuit asking the DoS defenses. */ + if (!hs_dos_can_send_intro2(intro_circ)) { + log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v2 cell due to DoS " + "limitations. Sending NACK to client."); + goto err; + } + log_info(LD_REND, "Sending introduction request for service %s " "from circ %u to circ %u", diff --git a/src/include.am b/src/include.am index 77c126ba45..065bdc31cb 100644 --- a/src/include.am +++ b/src/include.am @@ -5,6 +5,8 @@ include src/lib/err/include.am include src/lib/cc/include.am include src/lib/ctime/include.am include src/lib/compress/include.am +include src/lib/conf/include.am +include src/lib/confmgt/include.am include src/lib/container/include.am include src/lib/crypt_ops/include.am include src/lib/defs/include.am diff --git a/src/lib/conf/.may_include b/src/lib/conf/.may_include new file mode 100644 index 0000000000..4285c3dcb8 --- /dev/null +++ b/src/lib/conf/.may_include @@ -0,0 +1,2 @@ +orconfig.h +lib/cc/*.h diff --git a/src/lib/conf/conftypes.h b/src/lib/conf/conftypes.h new file mode 100644 index 0000000000..e66ab3d5aa --- /dev/null +++ b/src/lib/conf/conftypes.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file conftypes.h + * @brief Types used to specify configurable options. + * + * This header defines the types that different modules will use in order to + * declare their configuration and state variables, and tell the configuration + * management code about those variables. From the individual module's point + * of view, its configuration and state are simply data structures. + * + * For defining new variable types, see var_type_def_st.h. + * + * For the code that manipulates variables defined via this module, see + * lib/confmgt/, especially typedvar.h and (later) structvar.h. The + * configuration manager is responsible for encoding, decoding, and + * maintaining the configuration structures used by the various modules. + * + * STATUS NOTE: This is a work in process refactoring. It is not yet possible + * for modules to define their own variables, and much of the configuration + * management code is still in src/app/config/. + **/ + +#ifndef TOR_SRC_LIB_CONF_CONFTYPES_H +#define TOR_SRC_LIB_CONF_CONFTYPES_H + +#include "lib/cc/torint.h" + +/** Enumeration of types which option values can take */ +typedef enum config_type_t { + CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ + CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ + CONFIG_TYPE_POSINT, /**< A non-negative integer less than MAX_INT */ + CONFIG_TYPE_INT, /**< Any integer. */ + CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ + CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ + CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional + * units */ + CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ + CONFIG_TYPE_DOUBLE, /**< A floating-point value */ + CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ + CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, + * 1 for true, and -1 for auto */ + CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ + CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and + * optional whitespace. */ + CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and + * optional whitespace, representing intervals in + * seconds, with optional units. We allow + * multiple values here for legacy reasons, but + * ignore every value after the first. */ + CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ + CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, + * mixed with other keywords. */ + CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize + * context-sensitive config lines when fetching. + */ + // XXXX this doesn't belong at this level of abstraction. + CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, + * parsed into a routerset_t. */ + CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ +} config_type_t; + +#ifdef TOR_UNIT_TESTS +/** + * Union used when building in test mode typechecking the members of a type + * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how + * it is used. */ +typedef union { + char **STRING; + char **FILENAME; + int *POSINT; /* yes, this is really an int, and not an unsigned int. For + * historical reasons, many configuration values are restricted + * to the range [0,INT_MAX], and stored in signed ints. + */ + uint64_t *UINT64; + int *INT; + int *INTERVAL; + int *MSEC_INTERVAL; + uint64_t *MEMUNIT; + double *DOUBLE; + int *BOOL; + int *AUTOBOOL; + time_t *ISOTIME; + struct smartlist_t **CSV; + int *CSV_INTERVAL; + struct config_line_t **LINELIST; + struct config_line_t **LINELIST_S; + struct config_line_t **LINELIST_V; + // XXXX this doesn't belong at this level of abstraction. + struct routerset_t **ROUTERSET; +} confparse_dummy_values_t; +#endif /* defined(TOR_UNIT_TESTS) */ + +#endif /* !defined(TOR_SRC_LIB_CONF_CONFTYPES_H) */ diff --git a/src/lib/conf/include.am b/src/lib/conf/include.am new file mode 100644 index 0000000000..25355697d2 --- /dev/null +++ b/src/lib/conf/include.am @@ -0,0 +1,4 @@ + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/lib/conf/conftypes.h diff --git a/src/lib/confmgt/.may_include b/src/lib/confmgt/.may_include new file mode 100644 index 0000000000..d85dbf6904 --- /dev/null +++ b/src/lib/confmgt/.may_include @@ -0,0 +1,9 @@ +orconfig.h +lib/cc/*.h +lib/conf/*.h +lib/confmgt/*.h +lib/container/*.h +lib/encoding/*.h +lib/log/*.h +lib/malloc/*.h +lib/string/*.h diff --git a/src/lib/confmgt/include.am b/src/lib/confmgt/include.am new file mode 100644 index 0000000000..a2c7649957 --- /dev/null +++ b/src/lib/confmgt/include.am @@ -0,0 +1,23 @@ +noinst_LIBRARIES += src/lib/libtor-confmgt.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-confmgt-testing.a +endif + +# ADD_C_FILE: INSERT SOURCES HERE. +src_lib_libtor_confmgt_a_SOURCES = \ + src/lib/confmgt/type_defs.c \ + src/lib/confmgt/typedvar.c \ + src/lib/confmgt/unitparse.c + +src_lib_libtor_confmgt_testing_a_SOURCES = \ + $(src_lib_libtor_confmgt_a_SOURCES) +src_lib_libtor_confmgt_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_confmgt_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/lib/confmgt/type_defs.h \ + src/lib/confmgt/typedvar.h \ + src/lib/confmgt/unitparse.h \ + src/lib/confmgt/var_type_def_st.h diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c new file mode 100644 index 0000000000..62b4c1019d --- /dev/null +++ b/src/lib/confmgt/type_defs.c @@ -0,0 +1,727 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file type_defs.c + * @brief Definitions for various low-level configuration types. + * + * This module creates a number of var_type_def_t objects, to be used by + * typedvar.c in manipulating variables. + * + * The types here are common types that can be implemented with Tor's + * low-level functionality. To define new types, see var_type_def_st.h. + **/ + +#include "orconfig.h" +#include "lib/conf/conftypes.h" +#include "lib/confmgt/typedvar.h" +#include "lib/confmgt/type_defs.h" +#include "lib/confmgt/unitparse.h" + +#include "lib/cc/compat_compiler.h" +#include "lib/conf/conftypes.h" +#include "lib/container/smartlist.h" +#include "lib/encoding/confline.h" +#include "lib/encoding/time_fmt.h" +#include "lib/log/escape.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/parse_int.h" +#include "lib/string/printf.h" + +#include "lib/confmgt/var_type_def_st.h" + +#include <stddef.h> +#include <string.h> + +////// +// CONFIG_TYPE_STRING +// CONFIG_TYPE_FILENAME +// +// These two types are the same for now, but they have different names. +////// + +static int +string_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + char **p = (char**)target; + *p = tor_strdup(value); + return 0; +} + +static char * +string_encode(const void *value, const void *params) +{ + (void)params; + const char **p = (const char**)value; + return *p ? tor_strdup(*p) : NULL; +} + +static void +string_clear(void *value, const void *params) +{ + (void)params; + char **p = (char**)value; + tor_free(*p); // sets *p to NULL. +} + +static const var_type_fns_t string_fns = { + .parse = string_parse, + .encode = string_encode, + .clear = string_clear, +}; + +///// +// CONFIG_TYPE_INT +// CONFIG_TYPE_POSINT +// +// These types are implemented as int, possibly with a restricted range. +///// + +typedef struct int_type_params_t { + int minval; + int maxval; +} int_parse_params_t; + +static const int_parse_params_t INT_PARSE_UNRESTRICTED = { + .minval = INT_MIN, + .maxval = INT_MAX, +}; + +static const int_parse_params_t INT_PARSE_POSINT = { + .minval = 0, + .maxval = INT_MAX, +}; + +static int +int_parse(void *target, const char *value, char **errmsg, const void *params) +{ + const int_parse_params_t *pp; + if (params) { + pp = params; + } else { + pp = &INT_PARSE_UNRESTRICTED; + } + int *p = target; + int ok=0; + *p = (int)tor_parse_long(value, 10, pp->minval, pp->maxval, &ok, NULL); + if (!ok) { + tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.", + value); + return -1; + } + return 0; +} + +static char * +int_encode(const void *value, const void *params) +{ + (void)params; + int v = *(int*)value; + char *result; + tor_asprintf(&result, "%d", v); + return result; +} + +static void +int_clear(void *value, const void *params) +{ + (void)params; + *(int*)value = 0; +} + +static bool +int_ok(const void *value, const void *params) +{ + const int_parse_params_t *pp = params; + if (pp) { + int v = *(int*)value; + return pp->minval <= v && v <= pp->maxval; + } else { + return true; + } +} + +static const var_type_fns_t int_fns = { + .parse = int_parse, + .encode = int_encode, + .clear = int_clear, + .ok = int_ok, +}; + +///// +// CONFIG_TYPE_UINT64 +// +// This type is an unrestricted u64. +///// + +static int +uint64_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + uint64_t *p = target; + int ok=0; + *p = tor_parse_uint64(value, 10, 0, UINT64_MAX, &ok, NULL); + if (!ok) { + tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.", + value); + return -1; + } + return 0; +} + +static char * +uint64_encode(const void *value, const void *params) +{ + (void)params; + uint64_t v = *(uint64_t*)value; + char *result; + tor_asprintf(&result, "%"PRIu64, v); + return result; +} + +static void +uint64_clear(void *value, const void *params) +{ + (void)params; + *(uint64_t*)value = 0; +} + +static const var_type_fns_t uint64_fns = { + .parse = uint64_parse, + .encode = uint64_encode, + .clear = uint64_clear, +}; + +///// +// CONFIG_TYPE_INTERVAL +// CONFIG_TYPE_MSEC_INTERVAL +// CONFIG_TYPE_MEMUNIT +// +// These types are implemented using the config_parse_units() function. +// The intervals are stored as ints, whereas memory units are stored as +// uint64_ts. +///// + +static int +units_parse_u64(void *target, const char *value, char **errmsg, + const void *params) +{ + const unit_table_t *table = params; + tor_assert(table); + uint64_t *v = (uint64_t*)target; + int ok=1; + *v = config_parse_units(value, table, &ok); + if (!ok) { + *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + return -1; + } + return 0; +} + +static int +units_parse_int(void *target, const char *value, char **errmsg, + const void *params) +{ + const unit_table_t *table = params; + tor_assert(table); + int *v = (int*)target; + int ok=1; + uint64_t u64 = config_parse_units(value, table, &ok); + if (!ok) { + *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + return -1; + } + if (u64 > INT_MAX) { + tor_asprintf(errmsg, "Provided value %s is too large", value); + return -1; + } + *v = (int) u64; + return 0; +} + +static bool +units_ok_int(const void *value, const void *params) +{ + (void)params; + int v = *(int*)value; + return v >= 0; +} + +static const var_type_fns_t memunit_fns = { + .parse = units_parse_u64, + .encode = uint64_encode, // doesn't use params + .clear = uint64_clear, // doesn't use params +}; + +static const var_type_fns_t interval_fns = { + .parse = units_parse_int, + .encode = int_encode, // doesn't use params + .clear = int_clear, // doesn't use params, + .ok = units_ok_int // can't use int_ok, since that expects int params. +}; + +///// +// CONFIG_TYPE_DOUBLE +// +// This is a nice simple double. +///// + +static int +double_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + double *v = (double*)target; + // XXXX This is the preexisting behavior, but we should detect errors here. + *v = atof(value); + return 0; +} + +static char * +double_encode(const void *value, const void *params) +{ + (void)params; + double v = *(double*)value; + char *result; + tor_asprintf(&result, "%f", v); + return result; +} + +static void +double_clear(void *value, const void *params) +{ + (void)params; + double *v = (double *)value; + *v = 0.0; +} + +static const var_type_fns_t double_fns = { + .parse = double_parse, + .encode = double_encode, + .clear = double_clear, +}; + +///// +// CONFIG_TYPE_BOOL +// CONFIG_TYPE_AUTOBOOL +// +// These types are implemented as a case-insensitive string-to-integer +// mapping. +///// + +typedef struct enumeration_table_t { + const char *name; + int value; +} enumeration_table_t; + +static int +enum_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + const enumeration_table_t *table = params; + int *p = (int *)target; + for (; table->name; ++table) { + if (!strcasecmp(value, table->name)) { + *p = table->value; + return 0; + } + } + tor_asprintf(errmsg, "Unrecognized value %s.", value); + return -1; +} + +static char * +enum_encode(const void *value, const void *params) +{ + int v = *(const int*)value; + const enumeration_table_t *table = params; + for (; table->name; ++table) { + if (v == table->value) + return tor_strdup(table->name); + } + return NULL; // error. +} + +static void +enum_clear(void *value, const void *params) +{ + int *p = (int*)value; + const enumeration_table_t *table = params; + tor_assert(table->name); + *p = table->value; +} + +static bool +enum_ok(const void *value, const void *params) +{ + int v = *(const int*)value; + const enumeration_table_t *table = params; + for (; table->name; ++table) { + if (v == table->value) + return true; + } + return false; +} + +static const enumeration_table_t enum_table_bool[] = { + { "0", 0 }, + { "1", 1 }, + { NULL, 0 }, +}; + +static const enumeration_table_t enum_table_autobool[] = { + { "0", 0 }, + { "1", 1 }, + { "auto", -1 }, + { NULL, 0 }, +}; + +static const var_type_fns_t enum_fns = { + .parse = enum_parse, + .encode = enum_encode, + .clear = enum_clear, + .ok = enum_ok, +}; + +///// +// CONFIG_TYPE_ISOTIME +// +// This is a time_t, encoded in ISO8601 format. +///// + +static int +time_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void) params; + time_t *p = target; + if (parse_iso_time(value, p) < 0) { + tor_asprintf(errmsg, "Invalid time %s", escaped(value)); + return -1; + } + return 0; +} + +static char * +time_encode(const void *value, const void *params) +{ + (void)params; + time_t v = *(const time_t *)value; + char *result = tor_malloc(ISO_TIME_LEN+1); + format_iso_time(result, v); + return result; +} + +static void +time_clear(void *value, const void *params) +{ + (void)params; + time_t *t = value; + *t = 0; +} + +static const var_type_fns_t time_fns = { + .parse = time_parse, + .encode = time_encode, + .clear = time_clear, +}; + +///// +// CONFIG_TYPE_CSV +// +// This type is a comma-separated list of strings, stored in a smartlist_t. +// An empty list may be encoded either as an empty smartlist, or as NULL. +///// + +static int +csv_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + smartlist_t **sl = (smartlist_t**)target; + *sl = smartlist_new(); + smartlist_split_string(*sl, value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + return 0; +} + +static char * +csv_encode(const void *value, const void *params) +{ + (void)params; + const smartlist_t *sl = *(const smartlist_t **)value; + if (! sl) + return tor_strdup(""); + + return smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); +} + +static void +csv_clear(void *value, const void *params) +{ + (void)params; + smartlist_t **sl = (smartlist_t**)value; + if (!*sl) + return; + SMARTLIST_FOREACH(*sl, char *, cp, tor_free(cp)); + smartlist_free(*sl); // clears pointer. +} + +static const var_type_fns_t csv_fns = { + .parse = csv_parse, + .encode = csv_encode, + .clear = csv_clear, +}; + +///// +// CONFIG_TYPE_CSV_INTERVAL +// +// This type used to be a list of time intervals, used to determine a download +// schedule. Now, only the first interval counts: everything after the first +// comma is discarded. +///// + +static int +legacy_csv_interval_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + /* We used to have entire smartlists here. But now that all of our + * download schedules use exponential backoff, only the first part + * matters. */ + const char *comma = strchr(value, ','); + const char *val = value; + char *tmp = NULL; + if (comma) { + tmp = tor_strndup(val, comma - val); + val = tmp; + } + + int rv = units_parse_int(target, val, errmsg, &time_units); + tor_free(tmp); + return rv; +} + +static const var_type_fns_t legacy_csv_interval_fns = { + .parse = legacy_csv_interval_parse, + .encode = int_encode, + .clear = int_clear, +}; + +///// +// CONFIG_TYPE_LINELIST +// CONFIG_TYPE_LINELIST_S +// CONFIG_TYPE_LINELIST_V +// +// A linelist is a raw config_line_t list. Order is preserved. +// +// The LINELIST type is used for homogeneous lists, where all the lines +// have the same key. +// +// The LINELIST_S and LINELIST_V types are used for the case where multiple +// lines of different keys are kept in a single list, to preserve their +// relative order. The unified list is stored as a "virtual" variable whose +// type is LINELIST_V; the individual sublists are treated as variables of +// type LINELIST_S. +// +// A linelist may be fragile or non-fragile. Assigning a line to a fragile +// linelist replaces the list with the line. If the line has the "APPEND" +// command set on it, or if the list is non-fragile, the line is appended. +// Either way, the new list is non-fragile. +///// + +static int +linelist_kv_parse(void *target, const struct config_line_t *line, + char **errmsg, const void *params) +{ + (void)params; + (void)errmsg; + config_line_t **lines = target; + + if (*lines && (*lines)->fragile) { + if (line->command == CONFIG_LINE_APPEND) { + (*lines)->fragile = 0; + } else { + config_free_lines(*lines); // sets it to NULL + } + } + + config_line_append(lines, line->key, line->value); + return 0; +} + +static int +linelist_kv_virt_noparse(void *target, const struct config_line_t *line, + char **errmsg, const void *params) +{ + (void)target; + (void)line; + (void)params; + *errmsg = tor_strdup("Cannot assign directly to virtual option."); + return -1; +} + +static struct config_line_t * +linelist_kv_encode(const char *key, const void *value, + const void *params) +{ + (void)key; + (void)params; + config_line_t *lines = *(config_line_t **)value; + return config_lines_dup(lines); +} + +static struct config_line_t * +linelist_s_kv_encode(const char *key, const void *value, + const void *params) +{ + (void)params; + config_line_t *lines = *(config_line_t **)value; + return config_lines_dup_and_filter(lines, key); +} + +static void +linelist_clear(void *target, const void *params) +{ + (void)params; + config_line_t **lines = target; + config_free_lines(*lines); // sets it to NULL +} + +static bool +linelist_eq(const void *a, const void *b, const void *params) +{ + (void)params; + const config_line_t *lines_a = *(const config_line_t **)a; + const config_line_t *lines_b = *(const config_line_t **)b; + return config_lines_eq(lines_a, lines_b); +} + +static int +linelist_copy(void *target, const void *value, const void *params) +{ + (void)params; + config_line_t **ptr = (config_line_t **)target; + const config_line_t *val = *(const config_line_t **)value; + config_free_lines(*ptr); + *ptr = config_lines_dup(val); + return 0; +} + +static const var_type_fns_t linelist_fns = { + .kv_parse = linelist_kv_parse, + .kv_encode = linelist_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +static const var_type_fns_t linelist_v_fns = { + .kv_parse = linelist_kv_virt_noparse, + .kv_encode = linelist_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +static const var_type_fns_t linelist_s_fns = { + .kv_parse = linelist_kv_parse, + .kv_encode = linelist_s_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +///// +// CONFIG_TYPE_ROUTERSET +// +// XXXX This type is not implemented here, since routerset_t is not available +// XXXX to this module. +///// + +///// +// CONFIG_TYPE_OBSOLETE +// +// Used to indicate an obsolete option. +// +// XXXX This is not a type, and should be handled at a higher level of +// XXXX abstraction. +///// + +static int +ignore_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)target; + (void)value; + (void)errmsg; + (void)params; + // XXXX move this to a higher level, once such a level exists. + log_warn(LD_GENERAL, "Skipping obsolete configuration option."); + return 0; +} + +static char * +ignore_encode(const void *value, const void *params) +{ + (void)value; + (void)params; + return NULL; +} + +static const var_type_fns_t ignore_fns = { + .parse = ignore_parse, + .encode = ignore_encode, +}; + +/** + * Table mapping conf_type_t values to var_type_def_t objects. + **/ +static const var_type_def_t type_definitions_table[] = { + [CONFIG_TYPE_STRING] = { "String", &string_fns, NULL }, + [CONFIG_TYPE_FILENAME] = { "Filename", &string_fns, NULL }, + [CONFIG_TYPE_INT] = { "SignedInteger", &int_fns, &INT_PARSE_UNRESTRICTED }, + [CONFIG_TYPE_POSINT] = { "Integer", &int_fns, &INT_PARSE_POSINT }, + [CONFIG_TYPE_UINT64] = { "Integer", &uint64_fns, NULL, }, + [CONFIG_TYPE_MEMUNIT] = { "DataSize", &memunit_fns, &memory_units }, + [CONFIG_TYPE_INTERVAL] = { "TimeInterval", &interval_fns, &time_units }, + [CONFIG_TYPE_MSEC_INTERVAL] = { "TimeMsecInterval", &interval_fns, + &time_msec_units }, + [CONFIG_TYPE_DOUBLE] = { "Float", &double_fns, NULL }, + [CONFIG_TYPE_BOOL] = { "Boolean", &enum_fns, &enum_table_bool }, + [CONFIG_TYPE_AUTOBOOL] = { "Boolean+Auto", &enum_fns, &enum_table_autobool }, + [CONFIG_TYPE_ISOTIME] = { "Time", &time_fns, NULL }, + [CONFIG_TYPE_CSV] = { "CommaList", &csv_fns, NULL }, + [CONFIG_TYPE_CSV_INTERVAL] = { "TimeInterval", &legacy_csv_interval_fns, + NULL }, + [CONFIG_TYPE_LINELIST] = { "LineList", &linelist_fns, NULL }, + [CONFIG_TYPE_LINELIST_S] = { "Dependent", &linelist_s_fns, NULL }, + [CONFIG_TYPE_LINELIST_V] = { "Virtual", &linelist_v_fns, NULL }, + [CONFIG_TYPE_OBSOLETE] = { "Obsolete", &ignore_fns, NULL } +}; + +/** + * Return a pointer to the var_type_def_t object for the given + * config_type_t value, or NULL if no such type definition exists. + **/ +const var_type_def_t * +lookup_type_def(config_type_t type) +{ + int t = type; + tor_assert(t >= 0); + if (t >= (int)ARRAY_LENGTH(type_definitions_table)) + return NULL; + return &type_definitions_table[t]; +} diff --git a/src/lib/confmgt/type_defs.h b/src/lib/confmgt/type_defs.h new file mode 100644 index 0000000000..ecf040529e --- /dev/null +++ b/src/lib/confmgt/type_defs.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file type_defs.h + * @brief Header for lib/confmgt/type_defs.c + **/ + +#ifndef TOR_LIB_CONFMGT_TYPE_DEFS_H +#define TOR_LIB_CONFMGT_TYPE_DEFS_H + +const struct var_type_def_t *lookup_type_def(config_type_t type); + +#endif /* !defined(TOR_LIB_CONFMGT_TYPE_DEFS_H) */ diff --git a/src/lib/confmgt/typedvar.c b/src/lib/confmgt/typedvar.c new file mode 100644 index 0000000000..fc45c44481 --- /dev/null +++ b/src/lib/confmgt/typedvar.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file typedvar.c + * @brief Functions for accessing a pointer as an object of a given type. + * + * These functions represent a low-level API for accessing a typed variable. + * They are used in the configuration system to examine and set fields in + * configuration objects used by individual modules. + * + * Almost no code should call these directly. + **/ + +#include "orconfig.h" +#include "lib/conf/conftypes.h" +#include "lib/confmgt/type_defs.h" +#include "lib/confmgt/typedvar.h" +#include "lib/encoding/confline.h" +#include "lib/log/escape.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/util_string.h" + +#include "lib/confmgt/var_type_def_st.h" + +#include <stddef.h> +#include <string.h> + +/** + * Try to parse a string in <b>value</b> that encodes an object of the type + * defined by <b>def</b>. + * + * On success, adjust the lvalue pointed to by <b>target</b> to hold that + * value, and return 0. On failure, set *<b>errmsg</b> to a newly allocated + * string holding an error message, and return -1. + **/ +int +typed_var_assign_ex(void *target, const char *value, char **errmsg, + const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + // clear old value if needed. + typed_var_free_ex(target, def); + + tor_assert(def->fns->parse); + return def->fns->parse(target, value, errmsg, def->params); +} + +/** + * Try to parse a single line from the head of<b>line</b> that encodes an + * object of the type defined in <b>def</b>. On success and failure, behave as + * typed_var_assign_ex(). + * + * All types for which keys are significant should use this function. + * + * Note that although multiple lines may be provided in <b>line</b>, + * only the first one is handled by this function. + **/ +int +typed_var_kvassign_ex(void *target, const config_line_t *line, + char **errmsg, const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + + if (def->fns->kv_parse) { + // We do _not_ free the old value here, since linelist options + // sometimes have append semantics. + return def->fns->kv_parse(target, line, errmsg, def->params); + } + + return typed_var_assign_ex(target, line->value, errmsg, def); +} + +/** + * Release storage held by a variable in <b>target</b> of type defined by + * <b>def</b>, and set <b>target</b> to a reasonable default. + **/ +void +typed_var_free_ex(void *target, const var_type_def_t *def) +{ + if (BUG(!def)) + return; + if (def->fns->clear) { + def->fns->clear(target, def->params); + } +} + +/** + * Encode a value of type <b>def</b> pointed to by <b>value</b>, and return + * its result in a newly allocated string. The string may need to be escaped. + * + * Returns NULL if this option has a NULL value, or on internal error. + **/ +char * +typed_var_encode_ex(const void *value, const var_type_def_t *def) +{ + if (BUG(!def)) + return NULL; + tor_assert(def->fns->encode); + return def->fns->encode(value, def->params); +} + +/** + * As typed_var_encode_ex(), but returns a newly allocated config_line_t + * object. The provided <b>key</b> is used as the key of the lines, unless + * the type is one (line a linelist) that encodes its own keys. + * + * This function may return a list of multiple lines. + * + * Returns NULL if there are no lines to encode, or on internal error. + */ +config_line_t * +typed_var_kvencode_ex(const char *key, const void *value, + const var_type_def_t *def) +{ + if (BUG(!def)) + return NULL; + if (def->fns->kv_encode) { + return def->fns->kv_encode(key, value, def->params); + } + char *encoded_value = typed_var_encode_ex(value, def); + if (!encoded_value) + return NULL; + + config_line_t *result = tor_malloc_zero(sizeof(config_line_t)); + result->key = tor_strdup(key); + result->value = encoded_value; + return result; +} + +/** + * Set <b>dest</b> to contain the same value as <b>src</b>. Both types + * must be as defined by <b>def</b>. + * + * Return 0 on success, and -1 on failure. + **/ +int +typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + if (def->fns->copy) { + // If we have been provided a copy fuction, use it. + return def->fns->copy(dest, src, def); + } + + // Otherwise, encode 'src' and parse the result into 'def'. + char *enc = typed_var_encode_ex(src, def); + if (!enc) { + typed_var_free_ex(dest, def); + return 0; + } + char *err = NULL; + int rv = typed_var_assign_ex(dest, enc, &err, def); + if (BUG(rv < 0)) { + log_warn(LD_BUG, "Encoded value %s was not parseable as a %s: %s", + escaped(enc), def->name, err?err:""); + } + tor_free(err); + tor_free(enc); + return rv; +} + +/** + * Return true if <b>a</b> and <b>b</b> are semantically equivalent. + * Both types must be as defined by <b>def</b>. + **/ +bool +typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def) +{ + if (BUG(!def)) + return false; + + if (def->fns->eq) { + // Use a provided eq function if we got one. + return def->fns->eq(a, b, def->params); + } + + // Otherwise, encode the values and compare them. + char *enc_a = typed_var_encode_ex(a, def); + char *enc_b = typed_var_encode_ex(b, def); + bool eq = !strcmp_opt(enc_a,enc_b); + tor_free(enc_a); + tor_free(enc_b); + return eq; +} + +/** + * Check whether <b>value</b> encodes a valid value according to the + * type definition in <b>def</b>. + */ +bool +typed_var_ok_ex(const void *value, const var_type_def_t *def) +{ + if (BUG(!def)) + return false; + + if (def->fns->ok) + return def->fns->ok(value, def->params); + + return true; +} + +/* ===== + * The functions below take a config_type_t instead of a var_type_def_t. + * I'd like to deprecate them eventually and use var_type_def_t everywhere, + * but for now they make migration easier. + * ===== */ + +/** + * As typed_var_assign_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +int +typed_var_assign(void *target, const char *value, char **errmsg, + config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_assign_ex(target, value, errmsg, def); +} + +/** + * As typed_var_kvassign_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +int +typed_var_kvassign(void *target, const config_line_t *line, char **errmsg, + config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_kvassign_ex(target, line, errmsg, def); +} + +/** + * As typed_var_free_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +void +typed_var_free(void *target, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_free_ex(target, def); +} + +/** + * As typed_var_encode_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +char * +typed_var_encode(const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_encode_ex(value, def); +} + +/** + * As typed_var_kvencode_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +config_line_t * +typed_var_kvencode(const char *key, const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_kvencode_ex(key, value, def); +} + +/** + * As typed_var_copy_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +int +typed_var_copy(void *dest, const void *src, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_copy_ex(dest, src, def); +} + +/** + * As typed_var_eq_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +bool +typed_var_eq(const void *a, const void *b, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_eq_ex(a, b, def); +} + +/** + * As typed_var_ok_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +bool +typed_var_ok(const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_ok_ex(value, def); +} diff --git a/src/lib/confmgt/typedvar.h b/src/lib/confmgt/typedvar.h new file mode 100644 index 0000000000..720ad54fc6 --- /dev/null +++ b/src/lib/confmgt/typedvar.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file typedvar.h + * @brief Header for lib/confmgt/typedvar.c + **/ + +#ifndef TOR_LIB_CONFMGT_TYPEDVAR_H +#define TOR_LIB_CONFMGT_TYPEDVAR_H + +#include <stdbool.h> + +enum config_type_t; +struct config_line_t; + +typedef struct var_type_fns_t var_type_fns_t; +typedef struct var_type_def_t var_type_def_t; + +int typed_var_assign(void *target, const char *value, char **errmsg, + enum config_type_t type); +void typed_var_free(void *target, enum config_type_t type); +char *typed_var_encode(const void *value, enum config_type_t type); +int typed_var_copy(void *dest, const void *src, enum config_type_t type); +bool typed_var_eq(const void *a, const void *b, enum config_type_t type); +bool typed_var_ok(const void *value, enum config_type_t type); + +int typed_var_kvassign(void *target, const struct config_line_t *line, + char **errmsg, enum config_type_t type); +struct config_line_t *typed_var_kvencode(const char *key, const void *value, + enum config_type_t type); + +int typed_var_assign_ex(void *target, const char *value, char **errmsg, + const var_type_def_t *def); +void typed_var_free_ex(void *target, const var_type_def_t *def); +char *typed_var_encode_ex(const void *value, const var_type_def_t *def); +int typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def); +bool typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def); +bool typed_var_ok_ex(const void *value, const var_type_def_t *def); + +int typed_var_kvassign_ex(void *target, const struct config_line_t *line, + char **errmsg, const var_type_def_t *def); +struct config_line_t *typed_var_kvencode_ex(const char *key, const void *value, + const var_type_def_t *def); + +#endif /* !defined(TOR_LIB_CONFMGT_TYPEDVAR_H) */ diff --git a/src/lib/confmgt/unitparse.c b/src/lib/confmgt/unitparse.c new file mode 100644 index 0000000000..c3ed8285a4 --- /dev/null +++ b/src/lib/confmgt/unitparse.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file unitparse.c + * @brief Functions for parsing values with units from a configuration file. + **/ + +#include "orconfig.h" +#include "lib/confmgt/unitparse.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/string/parse_int.h" +#include "lib/string/util_string.h" + +#include <string.h> + +/** Table to map the names of memory units to the number of bytes they + * contain. */ +const struct unit_table_t memory_units[] = { + { "", 1 }, + { "b", 1<< 0 }, + { "byte", 1<< 0 }, + { "bytes", 1<< 0 }, + { "kb", 1<<10 }, + { "kbyte", 1<<10 }, + { "kbytes", 1<<10 }, + { "kilobyte", 1<<10 }, + { "kilobytes", 1<<10 }, + { "kilobits", 1<<7 }, + { "kilobit", 1<<7 }, + { "kbits", 1<<7 }, + { "kbit", 1<<7 }, + { "m", 1<<20 }, + { "mb", 1<<20 }, + { "mbyte", 1<<20 }, + { "mbytes", 1<<20 }, + { "megabyte", 1<<20 }, + { "megabytes", 1<<20 }, + { "megabits", 1<<17 }, + { "megabit", 1<<17 }, + { "mbits", 1<<17 }, + { "mbit", 1<<17 }, + { "gb", 1<<30 }, + { "gbyte", 1<<30 }, + { "gbytes", 1<<30 }, + { "gigabyte", 1<<30 }, + { "gigabytes", 1<<30 }, + { "gigabits", 1<<27 }, + { "gigabit", 1<<27 }, + { "gbits", 1<<27 }, + { "gbit", 1<<27 }, + { "tb", UINT64_C(1)<<40 }, + { "tbyte", UINT64_C(1)<<40 }, + { "tbytes", UINT64_C(1)<<40 }, + { "terabyte", UINT64_C(1)<<40 }, + { "terabytes", UINT64_C(1)<<40 }, + { "terabits", UINT64_C(1)<<37 }, + { "terabit", UINT64_C(1)<<37 }, + { "tbits", UINT64_C(1)<<37 }, + { "tbit", UINT64_C(1)<<37 }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of seconds they + * contain. */ +const struct unit_table_t time_units[] = { + { "", 1 }, + { "second", 1 }, + { "seconds", 1 }, + { "minute", 60 }, + { "minutes", 60 }, + { "hour", 60*60 }, + { "hours", 60*60 }, + { "day", 24*60*60 }, + { "days", 24*60*60 }, + { "week", 7*24*60*60 }, + { "weeks", 7*24*60*60 }, + { "month", 2629728, }, /* about 30.437 days */ + { "months", 2629728, }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of milliseconds + * they contain. */ +const struct unit_table_t time_msec_units[] = { + { "", 1 }, + { "msec", 1 }, + { "millisecond", 1 }, + { "milliseconds", 1 }, + { "second", 1000 }, + { "seconds", 1000 }, + { "minute", 60*1000 }, + { "minutes", 60*1000 }, + { "hour", 60*60*1000 }, + { "hours", 60*60*1000 }, + { "day", 24*60*60*1000 }, + { "days", 24*60*60*1000 }, + { "week", 7*24*60*60*1000 }, + { "weeks", 7*24*60*60*1000 }, + { NULL, 0 }, +}; + +/** Parse a string <b>val</b> containing a number, zero or more + * spaces, and an optional unit string. If the unit appears in the + * table <b>u</b>, then multiply the number by the unit multiplier. + * On success, set *<b>ok</b> to 1 and return this product. + * Otherwise, set *<b>ok</b> to 0. + */ +uint64_t +config_parse_units(const char *val, const unit_table_t *u, int *ok) +{ + uint64_t v = 0; + double d = 0; + int use_float = 0; + char *cp; + + tor_assert(ok); + + v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); + if (!*ok || (cp && *cp == '.')) { + d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); + if (!*ok) + goto done; + use_float = 1; + } + + if (BUG(!cp)) { + // cp should always be non-NULL if the parse operation succeeds. + + // LCOV_EXCL_START + *ok = 1; + v = use_float ? ((uint64_t)d) : v; + goto done; + // LCOV_EXCL_STOP + } + + cp = (char*) eat_whitespace(cp); + + for ( ;u->unit;++u) { + if (!strcasecmp(u->unit, cp)) { + if (use_float) + v = (uint64_t)(u->multiplier * d); + else + v *= u->multiplier; + *ok = 1; + goto done; + } + } + log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); + *ok = 0; + done: + + if (*ok) + return v; + else + return 0; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * information (byte, KB, M, etc). On success, set *<b>ok</b> to true + * and return the number of bytes specified. Otherwise, set + * *<b>ok</b> to false and return 0. */ +uint64_t +config_parse_memunit(const char *s, int *ok) +{ + uint64_t u = config_parse_units(s, memory_units, ok); + return u; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * time in milliseconds. On success, set *<b>ok</b> to true and return + * the number of milliseconds in the provided interval. Otherwise, set + * *<b>ok</b> to 0 and return -1. */ +int +config_parse_msec_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_msec_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} + +/** Parse a string in the format "number unit", where unit is a unit of time. + * On success, set *<b>ok</b> to true and return the number of seconds in + * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. + */ +int +config_parse_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} diff --git a/src/lib/confmgt/unitparse.h b/src/lib/confmgt/unitparse.h new file mode 100644 index 0000000000..216361a7d4 --- /dev/null +++ b/src/lib/confmgt/unitparse.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file unitparse.h + * @brief Header for lib/confmgt/unitparse.c + **/ + +#ifndef TOR_LIB_CONFMGT_UNITPARSE_H +#define TOR_LIB_CONFMGT_UNITPARSE_H + +#include <lib/cc/torint.h> + +/** Mapping from a unit name to a multiplier for converting that unit into a + * base unit. Used by config_parse_unit. */ +typedef struct unit_table_t { + const char *unit; /**< The name of the unit */ + uint64_t multiplier; /**< How many of the base unit appear in this unit */ +} unit_table_t; + +extern const unit_table_t memory_units[]; +extern const unit_table_t time_units[]; +extern const struct unit_table_t time_msec_units[]; + +uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok); + +uint64_t config_parse_memunit(const char *s, int *ok); +int config_parse_msec_interval(const char *s, int *ok); +int config_parse_interval(const char *s, int *ok); + +#endif /* !defined(TOR_LIB_CONFMGT_UNITPARSE_H) */ diff --git a/src/lib/confmgt/var_type_def_st.h b/src/lib/confmgt/var_type_def_st.h new file mode 100644 index 0000000000..d142ee1104 --- /dev/null +++ b/src/lib/confmgt/var_type_def_st.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file var_type_def_st.h + * @brief Structure declarations for typedvar type definitions. + * + * This structure is used for defining new variable types. If you are not + * defining a new variable type for use by the configuration management + * system, you don't need this structure. + * + * For defining new variables, see the types in conftypes.h. + * + * For data-driven access to configuration variables, see the other members of + * lib/confmgt/. + * + * STATUS NOTE: It is not yet possible to actually define new variables + * outside of config.c, and many of the types that will eventually be used + * to do so are not yet moved. This will change as more of #29211 is + * completed. + **/ + +#ifndef TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H +#define TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H + +#include <stdbool.h> + +struct config_line_t; + +/** + * A structure full of functions pointers to implement a variable type. + * + * Every type MUST implement parse or kv_parse and encode or kv_encode; + * the other functions pointers MAY be NULL. + * + * All functions here take a <b>params</b> argument, whose value + * is determined by the type definition. Two types may have the + * same functions, but differ only in parameters. + **/ +struct var_type_fns_t { + /** + * Try to parse a string in <b>value</b> that encodes an object of this + * type. On success, adjust the lvalue pointed to by <b>target</b> to hold + * that value, and return 0. On failure, set *<b>errmsg</b> to a newly + * allocated string holding an error message, and return -1. + **/ + int (*parse)(void *target, const char *value, char **errmsg, + const void *params); + /** + * Try to parse a single line from the head of<b>line</b> that encodes + * an object of this type. On success and failure, behave as in the parse() + * function. + * + * If this function is absent, it is implemented in terms of parse(). + * + * All types for which keys are significant should use this method. For + * example, a "linelist" type records the actual keys that are given + * for each line, and so should use this method. + * + * Note that although multiple lines may be provided in <b>line</b>, + * only the first one should be handled by this function. + **/ + int (*kv_parse)(void *target, const struct config_line_t *line, + char **errmsg, const void *params); + /** + * Encode a value pointed to by <b>value</b> and return its result + * in a newly allocated string. The string may need to be escaped. + * + * If this function is absent, it is implemented in terms of kv_encode(). + * + * Returns NULL if this option has a NULL value, or on internal error. + * + * Requirement: all strings generated by encode() should produce a + * semantically equivalent value when given to parse(). + **/ + char *(*encode)(const void *value, const void *params); + /** + * As encode(), but returns a newly allocated config_line_t object. The + * provided <b>key</b> is used as the key of the lines, unless the type is + * one that encodes its own keys. + * + * Unlike kv_parse(), this function will return a list of multiple lines, + * if <b>value</b> is such that it must be encoded by multiple lines. + * + * Returns NULL if there are no lines to encode, or on internal error. + * + * If this function is absent, it is implemented in terms of encode(). + **/ + struct config_line_t *(*kv_encode)(const char *key, const void *value, + const void *params); + /** + * Free all storage held in <b>arg</b>, and set <b>arg</b> to a default + * value -- usually zero or NULL. + * + * If this function is absent, the default implementation does nothing. + **/ + void (*clear)(void *arg, const void *params); + /** + * Return true if <b>a</b> and <b>b</b> hold the same value, and false + * otherwise. + * + * If this function is absent, it is implemented by encoding both a and + * b and comparing their encoded strings for equality. + **/ + bool (*eq)(const void *a, const void *b, const void *params); + /** + * Try to copy the value from <b>value</b> into <b>target</b>. + * On success return 0; on failure return -1. + * + * If this function is absent, it is implemented by encoding the value + * into a string, and then parsing it into the target. + **/ + int (*copy)(void *target, const void *value, const void *params); + /** + * Check whether <b>value</b> holds a valid value according to the + * rules of this type; return true if it does and false if it doesn't. + * + * The default implementation for this function assumes that all + * values are valid. + **/ + bool (*ok)(const void *value, const void *params); +}; + +/** + * A structure describing a type that can be manipulated with the typedvar_* + * functions. + **/ +struct var_type_def_t { + /** + * The name of this type. Should not include spaces. Used for + * debugging, log messages, and the controller API. */ + const char *name; + /** + * A function table for this type. + */ + const struct var_type_fns_t *fns; + /** + * A pointer to a value that should be passed as the 'params' argument when + * calling the functions in this type's function table. + */ + const void *params; +}; + +#endif /* !defined(TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H) */ diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c index fdb575e03f..0d8384db13 100644 --- a/src/lib/encoding/confline.c +++ b/src/lib/encoding/confline.c @@ -256,7 +256,7 @@ config_lines_dup_and_filter(const config_line_t *inp, /** Return true iff a and b contain identical keys and values in identical * order. */ int -config_lines_eq(config_line_t *a, config_line_t *b) +config_lines_eq(const config_line_t *a, const config_line_t *b) { while (a && b) { if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value)) diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h index 56ea36bf61..12c554c6e7 100644 --- a/src/lib/encoding/confline.h +++ b/src/lib/encoding/confline.h @@ -50,7 +50,7 @@ const config_line_t *config_line_find(const config_line_t *lines, const char *key); const config_line_t *config_line_find_case(const config_line_t *lines, const char *key); -int config_lines_eq(config_line_t *a, config_line_t *b); +int config_lines_eq(const config_line_t *a, const config_line_t *b); int config_count_key(const config_line_t *a, const char *key); void config_free_lines_(config_line_t *front); #define config_free_lines(front) \ diff --git a/src/lib/evloop/.may_include b/src/lib/evloop/.may_include index 273de7bb94..54aa75fbff 100644 --- a/src/lib/evloop/.may_include +++ b/src/lib/evloop/.may_include @@ -8,6 +8,7 @@ lib/log/*.h lib/malloc/*.h lib/net/*.h lib/string/*.h +lib/subsys/*.h lib/testsupport/*.h lib/thread/*.h lib/time/*.h diff --git a/src/lib/evloop/evloop_sys.c b/src/lib/evloop/evloop_sys.c new file mode 100644 index 0000000000..56641a3175 --- /dev/null +++ b/src/lib/evloop/evloop_sys.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file evloop_sys.c + * @brief Subsystem definition for the event loop module + **/ + +#include "orconfig.h" +#include "lib/subsys/subsys.h" +#include "lib/evloop/compat_libevent.h" +#include "lib/evloop/evloop_sys.h" +#include "lib/log/log.h" + +static int +subsys_evloop_initialize(void) +{ + if (tor_init_libevent_rng() < 0) { + log_warn(LD_NET, "Problem initializing libevent RNG."); + return -1; + } + return 0; +} + +static void +subsys_evloop_postfork(void) +{ +#ifdef TOR_UNIT_TESTS + tor_libevent_postfork(); +#endif +} + +static void +subsys_evloop_shutdown(void) +{ + tor_libevent_free_all(); +} + +const struct subsys_fns_t sys_evloop = { + .name = "evloop", + .supported = true, + .level = -20, + .initialize = subsys_evloop_initialize, + .shutdown = subsys_evloop_shutdown, + .postfork = subsys_evloop_postfork, +}; diff --git a/src/lib/evloop/evloop_sys.h b/src/lib/evloop/evloop_sys.h new file mode 100644 index 0000000000..e6155c25b0 --- /dev/null +++ b/src/lib/evloop/evloop_sys.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file evloop_sys.h + * @brief Declare subsystem object for the event loop module. + **/ + +#ifndef TOR_LIB_EVLOOP_EVLOOP_SYS_H +#define TOR_LIB_EVLOOP_EVLOOP_SYS_H + +extern const struct subsys_fns_t sys_evloop; + +#endif /* !defined(TOR_LIB_EVLOOP_EVLOOP_SYS_H) */ diff --git a/src/lib/evloop/include.am b/src/lib/evloop/include.am index 6595b3a34b..41cd2f45c5 100644 --- a/src/lib/evloop/include.am +++ b/src/lib/evloop/include.am @@ -8,6 +8,7 @@ endif # ADD_C_FILE: INSERT SOURCES HERE. src_lib_libtor_evloop_a_SOURCES = \ src/lib/evloop/compat_libevent.c \ + src/lib/evloop/evloop_sys.c \ src/lib/evloop/procmon.c \ src/lib/evloop/timers.c \ src/lib/evloop/token_bucket.c \ @@ -21,6 +22,7 @@ src_lib_libtor_evloop_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ src/lib/evloop/compat_libevent.h \ + src/lib/evloop/evloop_sys.h \ src/lib/evloop/procmon.h \ src/lib/evloop/timers.h \ src/lib/evloop/token_bucket.h \ diff --git a/src/lib/evloop/token_bucket.c b/src/lib/evloop/token_bucket.c index ee6d631e3b..ec62d1b018 100644 --- a/src/lib/evloop/token_bucket.c +++ b/src/lib/evloop/token_bucket.c @@ -256,3 +256,55 @@ token_bucket_rw_dec(token_bucket_rw_t *bucket, flags |= TB_WRITE; return flags; } + +/** Initialize a token bucket in <b>bucket</b>, set up to allow <b>rate</b> + * per second, with a maximum burst of <b>burst</b>. The bucket is created + * such that <b>now_ts</b> is the current timestamp. The bucket starts out + * full. */ +void +token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst, uint32_t now_ts) +{ + memset(bucket, 0, sizeof(token_bucket_ctr_t)); + token_bucket_ctr_adjust(bucket, rate, burst); + token_bucket_ctr_reset(bucket, now_ts); +} + +/** Change the configured rate and burst of the given token bucket object in + * <b>bucket</b>. */ +void +token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst) +{ + token_bucket_cfg_init(&bucket->cfg, rate, burst); + token_bucket_raw_adjust(&bucket->counter, &bucket->cfg); +} + +/** Reset <b>bucket</b> to be full, as of timestamp <b>now_ts</b>. */ +void +token_bucket_ctr_reset(token_bucket_ctr_t *bucket, uint32_t now_ts) +{ + token_bucket_raw_reset(&bucket->counter, &bucket->cfg); + bucket->last_refilled_at_timestamp = now_ts; +} + +/** Refill <b>bucket</b> as appropriate, given that the current timestamp is + * <b>now_ts</b>. */ +void +token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts) +{ + const uint32_t elapsed_ticks = + (now_ts - bucket->last_refilled_at_timestamp); + if (elapsed_ticks > UINT32_MAX-(300*1000)) { + /* Either about 48 days have passed since the last refill, or the + * monotonic clock has somehow moved backwards. (We're looking at you, + * Windows.). We accept up to a 5 minute jump backwards as + * "unremarkable". + */ + return; + } + + token_bucket_raw_refill_steps(&bucket->counter, &bucket->cfg, + elapsed_ticks); + bucket->last_refilled_at_timestamp = now_ts; +} diff --git a/src/lib/evloop/token_bucket.h b/src/lib/evloop/token_bucket.h index 1ce6f1bf94..dde9bd65a4 100644 --- a/src/lib/evloop/token_bucket.h +++ b/src/lib/evloop/token_bucket.h @@ -103,6 +103,35 @@ token_bucket_rw_get_write(const token_bucket_rw_t *bucket) return token_bucket_raw_get(&bucket->write_bucket); } +/** + * A specialized bucket containing a single counter. + */ + +typedef struct token_bucket_ctr_t { + token_bucket_cfg_t cfg; + token_bucket_raw_t counter; + uint32_t last_refilled_at_timestamp; +} token_bucket_ctr_t; + +void token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst, uint32_t now_ts); +void token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst); +void token_bucket_ctr_reset(token_bucket_ctr_t *bucket, uint32_t now_ts); +void token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts); + +static inline bool +token_bucket_ctr_dec(token_bucket_ctr_t *bucket, ssize_t n) +{ + return token_bucket_raw_dec(&bucket->counter, n); +} + +static inline size_t +token_bucket_ctr_get(const token_bucket_ctr_t *bucket) +{ + return token_bucket_raw_get(&bucket->counter); +} + #ifdef TOKEN_BUCKET_PRIVATE /* To avoid making the rates too small, we consider units of "steps", diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 546af800a9..0a2c84caf2 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -373,7 +373,8 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) * * If <b>accept_regular</b> is set and the address is in neither recognized * reverse lookup hostname format, try parsing the address as a regular - * IPv4 or IPv6 address too. + * IPv4 or IPv6 address too. This mode will accept IPv6 addresses with or + * without square brackets. */ int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, @@ -1187,17 +1188,22 @@ fmt_addr32(uint32_t addr) } /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string - * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by - * square brackets. + * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * - * Return an address family on success, or -1 if an invalid address string is - * provided. */ -int -tor_addr_parse(tor_addr_t *addr, const char *src) + * If <b>allow_ipv6_without_brackets</b> is true, also allow IPv6 addresses + * without brackets. + * + * Always rejects IPv4 addresses with brackets. + * + * Returns an address family on success, or -1 if an invalid address string is + * provided. */ +static int +tor_addr_parse_impl(tor_addr_t *addr, const char *src, + bool allow_ipv6_without_brackets) { /* Holds substring of IPv6 address after removing square brackets */ char *tmp = NULL; - int result; + int result = -1; struct in_addr in_tmp; struct in6_addr in6_tmp; int brackets_detected = 0; @@ -1211,21 +1217,46 @@ tor_addr_parse(tor_addr_t *addr, const char *src) src = tmp = tor_strndup(src+1, strlen(src)-2); } - if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { - result = AF_INET6; - tor_addr_from_in6(addr, &in6_tmp); - } else if (!brackets_detected && - tor_inet_pton(AF_INET, src, &in_tmp) > 0) { - result = AF_INET; - tor_addr_from_in(addr, &in_tmp); - } else { - result = -1; + /* Try to parse an IPv6 address if it has brackets, or if IPv6 addresses + * without brackets are allowed */ + if (brackets_detected || allow_ipv6_without_brackets) { + if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { + result = AF_INET6; + tor_addr_from_in6(addr, &in6_tmp); + } + } + + /* Try to parse an IPv4 address without brackets */ + if (!brackets_detected) { + if (tor_inet_pton(AF_INET, src, &in_tmp) > 0) { + result = AF_INET; + tor_addr_from_in(addr, &in_tmp); + } + } + + /* Clear the address on error, to avoid returning uninitialised or partly + * parsed data. + */ + if (result == -1) { + memset(addr, 0, sizeof(tor_addr_t)); } tor_free(tmp); return result; } +/** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string + * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by + * square brackets. + * + * Returns an address family on success, or -1 if an invalid address string is + * provided. */ +int +tor_addr_parse(tor_addr_t *addr, const char *src) +{ + return tor_addr_parse_impl(addr, src, 1); +} + #ifdef HAVE_IFADDRS_TO_SMARTLIST /* * Convert a linked list consisting of <b>ifaddrs</b> structures @@ -1718,6 +1749,11 @@ get_interface_address6_list,(int severity, * form "ip" or "ip:0". Otherwise, accept those forms, and set * *<b>port_out</b> to <b>default_port</b>. * + * This function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * * Return 0 on success, -1 on failure. */ int tor_addr_port_parse(int severity, const char *addrport, @@ -1727,6 +1763,7 @@ tor_addr_port_parse(int severity, const char *addrport, int retval = -1; int r; char *addr_tmp = NULL; + bool has_port; tor_assert(addrport); tor_assert(address_out); @@ -1736,28 +1773,47 @@ tor_addr_port_parse(int severity, const char *addrport, if (r < 0) goto done; - if (!*port_out) { + has_port = !! *port_out; + /* If there's no port, use the default port, or fail if there is no default + */ + if (!has_port) { if (default_port >= 0) *port_out = default_port; else goto done; } - /* make sure that address_out is an IP address */ - if (tor_addr_parse(address_out, addr_tmp) < 0) + /* Make sure that address_out is an IP address. + * If there is no port in addrport, allow IPv6 addresses without brackets. */ + if (tor_addr_parse_impl(address_out, addr_tmp, !has_port) < 0) goto done; retval = 0; done: + /* Clear the address and port on error, to avoid returning uninitialised or + * partly parsed data. + */ + if (retval == -1) { + memset(address_out, 0, sizeof(tor_addr_t)); + *port_out = 0; + } tor_free(addr_tmp); return retval; } /** Given an address of the form "host[:port]", try to divide it into its host - * and port portions, setting *<b>address_out</b> to a newly allocated string - * holding the address portion and *<b>port_out</b> to the port (or 0 if no - * port is given). Return 0 on success, -1 on failure. */ + * and port portions. + * + * Like tor_addr_port_parse(), this function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * + * Sets *<b>address_out</b> to a newly allocated string holding the address + * portion, and *<b>port_out</b> to the port (or 0 if no port is given). + * + * Return 0 on success, -1 on failure. */ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out) @@ -1766,8 +1822,11 @@ tor_addr_port_split(int severity, const char *addrport, tor_assert(addrport); tor_assert(address_out); tor_assert(port_out); + /* We need to check for IPv6 manually because the logic below doesn't - * do a good job on IPv6 addresses that lack a port. */ + * do a good job on IPv6 addresses that lack a port. + * If an IPv6 address without square brackets is ambiguous, it gets parsed + * here as an address, rather than address:port. */ if (tor_addr_parse(&a_tmp, addrport) == AF_INET6) { *port_out = 0; *address_out = tor_strdup(addrport); @@ -1807,8 +1866,7 @@ tor_addr_port_split(int severity, const char *addrport, tor_free(address_); } - if (port_out) - *port_out = ok ? ((uint16_t) port_) : 0; + *port_out = ok ? ((uint16_t) port_) : 0; return ok ? 0 : -1; } diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c index 2dda491d14..e8d7d0d94d 100644 --- a/src/lib/net/resolve.c +++ b/src/lib/net/resolve.c @@ -35,6 +35,8 @@ * *<b>addr</b> to the proper IP address, in host byte order. Returns 0 * on success, -1 on failure; 1 on transient failure. * + * This function only accepts IPv4 addresses. + * * (This function exists because standard windows gethostbyname * doesn't treat raw IP addresses properly.) */ @@ -45,6 +47,11 @@ tor_lookup_hostname,(const char *name, uint32_t *addr)) tor_addr_t myaddr; int ret; + if (BUG(!addr)) + return -1; + + *addr = 0; + if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) return ret; @@ -56,12 +63,125 @@ tor_lookup_hostname,(const char *name, uint32_t *addr)) return -1; } +#ifdef HAVE_GETADDRINFO + +/* Host lookup helper for tor_addr_lookup(), when getaddrinfo() is + * available on this system. + * + * See tor_addr_lookup() for details. + */ +static int +tor_addr_lookup_host_getaddrinfo(const char *name, + uint16_t family, + tor_addr_t *addr) +{ + int err; + struct addrinfo *res=NULL, *res_p; + struct addrinfo *best=NULL; + struct addrinfo hints; + int result = -1; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + err = tor_getaddrinfo(name, NULL, &hints, &res); + /* The check for 'res' here shouldn't be necessary, but it makes static + * analysis tools happy. */ + if (!err && res) { + best = NULL; + for (res_p = res; res_p; res_p = res_p->ai_next) { + if (family == AF_UNSPEC) { + if (res_p->ai_family == AF_INET) { + best = res_p; + break; + } else if (res_p->ai_family == AF_INET6 && !best) { + best = res_p; + } + } else if (family == res_p->ai_family) { + best = res_p; + break; + } + } + if (!best) + best = res; + if (best->ai_family == AF_INET) { + tor_addr_from_in(addr, + &((struct sockaddr_in*)best->ai_addr)->sin_addr); + result = 0; + } else if (best->ai_family == AF_INET6) { + tor_addr_from_in6(addr, + &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); + result = 0; + } + tor_freeaddrinfo(res); + return result; + } + return (err == EAI_AGAIN) ? 1 : -1; +} + +#else /* !(defined(HAVE_GETADDRINFO)) */ + +/* Host lookup helper for tor_addr_lookup(), which calls getaddrinfo(). + * Used when gethostbyname() is not available on this system. + * + * See tor_addr_lookup() for details. + */ +static int +tor_addr_lookup_host_gethostbyname(const char *name, + tor_addr_t *addr) +{ + struct hostent *ent; + int err; +#ifdef HAVE_GETHOSTBYNAME_R_6_ARG + char buf[2048]; + struct hostent hostent; + int r; + r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); +#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) + char buf[2048]; + struct hostent hostent; + ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); +#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) + struct hostent_data data; + struct hostent hent; + memset(&data, 0, sizeof(data)); + err = gethostbyname_r(name, &hent, &data); + ent = err ? NULL : &hent; +#else + ent = gethostbyname(name); +#ifdef _WIN32 + err = WSAGetLastError(); +#else + err = h_errno; +#endif /* defined(_WIN32) */ +#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */ + if (ent) { + if (ent->h_addrtype == AF_INET) { + tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); + } else if (ent->h_addrtype == AF_INET6) { + tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); + } else { + tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type + } + return 0; + } +#ifdef _WIN32 + return (err == WSATRY_AGAIN) ? 1 : -1; +#else + return (err == TRY_AGAIN) ? 1 : -1; +#endif +} + +#endif /* defined(HAVE_GETADDRINFO) */ + /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set * *<b>addr</b> to the proper IP address and family. The <b>family</b> * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a * <i>preferred</i> family, though another one may be returned if only one * family is implemented for this address. * + * Like tor_addr_parse(), this function accepts IPv6 addresses with or without + * square brackets. + * * Return 0 on success, -1 on failure; 1 on transient failure. */ MOCK_IMPL(int, @@ -70,169 +190,134 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr)) /* Perhaps eventually this should be replaced by a tor_getaddrinfo or * something. */ - struct in_addr iaddr; - struct in6_addr iaddr6; + int parsed_family = 0; + int result = -1; + tor_assert(name); tor_assert(addr); tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC); + if (!*name) { /* Empty address is an error. */ - return -1; - } else if (tor_inet_pton(AF_INET, name, &iaddr)) { - /* It's an IPv4 IP. */ - if (family == AF_INET6) - return -1; - tor_addr_from_in(addr, &iaddr); - return 0; - } else if (tor_inet_pton(AF_INET6, name, &iaddr6)) { - if (family == AF_INET) - return -1; - tor_addr_from_in6(addr, &iaddr6); - return 0; + goto permfail; + } + + /* Is it an IP address? */ + parsed_family = tor_addr_parse(addr, name); + + if (parsed_family >= 0) { + /* If the IP address family matches, or was unspecified */ + if (parsed_family == family || family == AF_UNSPEC) { + goto success; + } else { + goto permfail; + } } else { + /* Clear the address after a failed tor_addr_parse(). */ + memset(addr, 0, sizeof(tor_addr_t)); #ifdef HAVE_GETADDRINFO - int err; - struct addrinfo *res=NULL, *res_p; - struct addrinfo *best=NULL; - struct addrinfo hints; - int result = -1; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - err = tor_getaddrinfo(name, NULL, &hints, &res); - /* The check for 'res' here shouldn't be necessary, but it makes static - * analysis tools happy. */ - if (!err && res) { - best = NULL; - for (res_p = res; res_p; res_p = res_p->ai_next) { - if (family == AF_UNSPEC) { - if (res_p->ai_family == AF_INET) { - best = res_p; - break; - } else if (res_p->ai_family == AF_INET6 && !best) { - best = res_p; - } - } else if (family == res_p->ai_family) { - best = res_p; - break; - } - } - if (!best) - best = res; - if (best->ai_family == AF_INET) { - tor_addr_from_in(addr, - &((struct sockaddr_in*)best->ai_addr)->sin_addr); - result = 0; - } else if (best->ai_family == AF_INET6) { - tor_addr_from_in6(addr, - &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); - result = 0; - } - tor_freeaddrinfo(res); - return result; - } - return (err == EAI_AGAIN) ? 1 : -1; + result = tor_addr_lookup_host_getaddrinfo(name, family, addr); + goto done; #else /* !(defined(HAVE_GETADDRINFO)) */ - struct hostent *ent; - int err; -#ifdef HAVE_GETHOSTBYNAME_R_6_ARG - char buf[2048]; - struct hostent hostent; - int r; - r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); -#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) - char buf[2048]; - struct hostent hostent; - ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); -#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) - struct hostent_data data; - struct hostent hent; - memset(&data, 0, sizeof(data)); - err = gethostbyname_r(name, &hent, &data); - ent = err ? NULL : &hent; -#else - ent = gethostbyname(name); -#ifdef _WIN32 - err = WSAGetLastError(); -#else - err = h_errno; -#endif -#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */ - if (ent) { - if (ent->h_addrtype == AF_INET) { - tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); - } else if (ent->h_addrtype == AF_INET6) { - tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); - } else { - tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type - } - return 0; - } -#ifdef _WIN32 - return (err == WSATRY_AGAIN) ? 1 : -1; -#else - return (err == TRY_AGAIN) ? 1 : -1; -#endif + result = tor_addr_lookup_host_gethostbyname(name, addr); + goto done; #endif /* defined(HAVE_GETADDRINFO) */ } + + /* If we weren't successful, and haven't already set the result, + * assume it's a permanent failure */ + permfail: + result = -1; + goto done; + success: + result = 0; + + /* We have set the result, now it's time to clean up */ + done: + if (result) { + /* Clear the address on error */ + memset(addr, 0, sizeof(tor_addr_t)); + } + return result; } /** Parse an address or address-port combination from <b>s</b>, resolve the * address as needed, and put the result in <b>addr_out</b> and (optionally) - * <b>port_out</b>. Return 0 on success, negative on failure. */ + * <b>port_out</b>. + * + * Like tor_addr_port_parse(), this function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * + * Return 0 on success, negative on failure. */ int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) { - const char *port; tor_addr_t addr; - uint16_t portval; + uint16_t portval = 0; char *tmp = NULL; + int rv = 0; + int result; tor_assert(s); tor_assert(addr_out); s = eat_whitespace(s); - if (*s == '[') { - port = strstr(s, "]"); - if (!port) - goto err; - tmp = tor_strndup(s+1, port-(s+1)); - port = port+1; - if (*port == ':') - port++; - else - port = NULL; - } else { - port = strchr(s, ':'); - if (port) - tmp = tor_strndup(s, port-s); - else - tmp = tor_strdup(s); - if (port) - ++port; + /* Try parsing s as an address:port first, so we don't have to duplicate + * the logic that rejects IPv6:Port with no square brackets. */ + rv = tor_addr_port_parse(LOG_WARN, s, &addr, &portval, 0); + /* That was easy, no DNS required. */ + if (rv == 0) + goto success; + + /* Now let's check for malformed IPv6 addresses and ports: + * tor_addr_port_parse() requires squared brackes if there is a port, + * and we want tor_addr_port_lookup() to have the same requirement. + * But we strip the port using tor_addr_port_split(), so tor_addr_lookup() + * only sees the address, and will accept it without square brackets. */ + int family = tor_addr_parse(&addr, s); + /* If tor_addr_parse() succeeds where tor_addr_port_parse() failed, we need + * to reject this address as malformed. */ + if (family >= 0) { + /* Double-check it's an IPv6 address. If not, we have a parsing bug. + */ + tor_assertf_nonfatal(family == AF_INET6, + "Wrong family: %d (should be IPv6: %d) which " + "failed IP:port parsing, but passed IP parsing. " + "input string: '%s'; parsed address: '%s'.", + family, AF_INET6, s, fmt_addr(&addr)); + goto err; } - if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) + /* Now we have a hostname. Let's split off the port, if any. */ + rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval); + if (rv < 0) goto err; - tor_free(tmp); - if (port) { - portval = (int) tor_parse_long(port, 10, 1, 65535, NULL, NULL); - if (!portval) - goto err; - } else { - portval = 0; - } + /* And feed the hostname to the lookup function. */ + if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) + goto err; + success: if (port_out) *port_out = portval; tor_addr_copy(addr_out, &addr); + result = 0; + goto done; - return 0; err: + /* Clear the address and port on error */ + memset(addr_out, 0, sizeof(tor_addr_t)); + if (port_out) + *port_out = 0; + result = -1; + + /* We have set the result, now it's time to clean up */ + done: tor_free(tmp); - return -1; + return result; } #ifdef USE_SANDBOX_GETADDRINFO diff --git a/src/lib/pubsub/pubsub_check.c b/src/lib/pubsub/pubsub_check.c index a3c22d4f25..bf1196df2c 100644 --- a/src/lib/pubsub/pubsub_check.c +++ b/src/lib/pubsub/pubsub_check.c @@ -172,34 +172,20 @@ pubsub_cfg_dump(const pubsub_cfg_t *cfg, int severity, const char *prefix) /** * Helper: fill a bitarray <b>out</b> with entries corresponding to the - * subsystems listed in <b>items</b>. If any subsystem is listed more than - * once, log a warning. Return 0 on success, -1 on failure. + * subsystems listed in <b>items</b>. **/ -static int +static void get_message_bitarray(const pubsub_adjmap_t *map, - message_id_t msg, const smartlist_t *items, - const char *operation, bitarray_t **out) { - bool ok = true; *out = bitarray_init_zero((unsigned)map->n_subsystems); if (! items) - return 0; + return; SMARTLIST_FOREACH_BEGIN(items, const pubsub_cfg_t *, cfg) { - if (bitarray_is_set(*out, cfg->subsys)) { - log_warn(LD_MESG|LD_BUG, - "Message \"%s\" is configured to be %s by subsystem " - "\"%s\" more than once.", - get_message_id_name(msg), operation, - get_subsys_id_name(cfg->subsys)); - ok = false; - } bitarray_set(*out, cfg->subsys); } SMARTLIST_FOREACH_END(cfg); - - return ok ? 0 : -1; } /** @@ -222,10 +208,8 @@ lint_message_graph(const pubsub_adjmap_t *map, bitarray_t *subscribed_by = NULL; bool ok = true; - if (get_message_bitarray(map, msg, pub, "published", &published_by) < 0) - ok = false; - if (get_message_bitarray(map, msg, sub, "subscribed", &subscribed_by) < 0) - ok = false; + get_message_bitarray(map, pub, &published_by); + get_message_bitarray(map, sub, &subscribed_by); /* Check whether any subsystem is publishing and subscribing the same * message. [??] diff --git a/src/test/include.am b/src/test/include.am index 85f9c9f880..1e20f3f53f 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -31,7 +31,11 @@ TESTSCRIPTS += \ endif if USEPYTHON -TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh +TESTSCRIPTS += \ + src/test/test_ntor.sh \ + src/test/test_hs_ntor.sh \ + src/test/test_bt.sh \ + scripts/maint/practracker/test_practracker.sh if COVERAGE_ENABLED # ... @@ -120,6 +124,7 @@ src_test_test_SOURCES += \ src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ + src/test/test_confparse.c \ src/test/test_connection.c \ src/test/test_conscache.c \ src/test/test_consdiff.c \ @@ -153,6 +158,7 @@ src_test_test_SOURCES += \ src/test/test_handles.c \ src/test/test_hs_cache.c \ src/test/test_hs_descriptor.c \ + src/test/test_hs_dos.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ @@ -193,6 +199,7 @@ src_test_test_SOURCES += \ src/test/test_status.c \ src/test/test_storagedir.c \ src/test/test_threads.c \ + src/test/test_token_bucket.c \ src/test/test_tortls.c \ src/test/test_util.c \ src/test/test_util_format.c \ diff --git a/src/test/test.c b/src/test/test.c index cac98dd839..b9a1da06f0 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -840,6 +840,7 @@ struct testgroup_t testgroups[] = { { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, + { "config/parse/", confparse_tests }, { "connection/", connection_tests }, { "conscache/", conscache_tests }, { "consdiff/", consdiff_tests }, @@ -876,6 +877,7 @@ struct testgroup_t testgroups[] = { { "hs_config/", hs_config_tests }, { "hs_control/", hs_control_tests }, { "hs_descriptor/", hs_descriptor }, + { "hs_dos/", hs_dos_tests }, { "hs_intropoint/", hs_intropoint_tests }, { "hs_ntor/", hs_ntor_tests }, { "hs_service/", hs_service_tests }, @@ -916,6 +918,7 @@ struct testgroup_t testgroups[] = { { "socks/", socks_tests }, { "status/" , status_tests }, { "storagedir/", storagedir_tests }, + { "token_bucket/", token_bucket_tests }, { "tortls/", tortls_tests }, #ifndef ENABLE_NSS { "tortls/openssl/", tortls_openssl_tests }, diff --git a/src/test/test.h b/src/test/test.h index 167fd090ac..f5c21bfe88 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -197,6 +197,7 @@ extern struct testcase_t circuitstats_tests[]; extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; +extern struct testcase_t confparse_tests[]; extern struct testcase_t connection_tests[]; extern struct testcase_t conscache_tests[]; extern struct testcase_t consdiff_tests[]; @@ -226,6 +227,7 @@ 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_dos_tests[]; extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t hs_ntor_tests[]; extern struct testcase_t hs_service_tests[]; @@ -272,6 +274,7 @@ extern struct testcase_t sr_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t thread_tests[]; +extern struct testcase_t token_bucket_tests[]; extern struct testcase_t tortls_openssl_tests[]; extern struct testcase_t tortls_tests[]; extern struct testcase_t util_format_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 05d8bf6c7b..0f50a43615 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -724,155 +724,570 @@ test_addr_ip6_helpers(void *arg) ; } +/* Test that addr_str successfully parses, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_PARSE_FMT(addr_str, expect_family, fmt_decorated, \ + expect_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that addr_str fails to parse, and: + * - the returned address is null. + */ +#define TEST_ADDR_PARSE_XFAIL(addr_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str and default_port successfully parse, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_PARSE_FMT(addr_port_str, default_port, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that addr_port_str and default_port fail to parse, and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_PARSE_XFAIL(addr_port_str, default_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as an IPv4 address using + * tor_lookup_hostname(), and: + * - the fmt_addr32() of the result is expect_str. + */ +#define TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, expect_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(addr_str, &addr32h); \ + tt_int_op(r, OP_EQ, 0); \ + tt_str_op(fmt_addr32(addr32h), OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse using tor_lookup_hostname(), with a + * permanent failure, and: + * - the returned address is 0. + */ +#define TEST_ADDR_V4_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(bad_str, &addr32h); \ + tt_int_op(r, OP_EQ, -1); \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_str as an IPv4 address using tor_lookup_hostname() + * does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is 0. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_V4_LOOKUP(host_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(host_str, &addr32h); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as a require_family IP address using + * tor_addr_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_LOOKUP_FMT(addr_str, require_family, expect_family, \ + fmt_decorated, expect_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_lookup(addr_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse as a require_family IP address using + * tor_addr_lookup(), with a permanent failure, and: + * - the returned address is null. + */ +#define TEST_ADDR_LOOKUP_XFAIL(bad_str, require_family) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_lookup(bad_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that looking up host_string as a require_family IP address using + * tor_addr_lookup(), does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is null. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_LOOKUP(host_str, require_family) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_lookup(host_str, require_family, &addr); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str successfully parses as an IP address and port + * using tor_addr_port_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_port_lookup(addr_port_str, &addr, &port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that bad_str fails to parse as an IP address and port + * using tor_addr_port_lookup(), and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_lookup(bad_str, &addr, &port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_port_str as an IP address using + * tor_addr_port_lookup(), does something sensible: + * - the result is -1 or 0. + * - if the result is a failure, the returned address is null, and the + * returned port is zero, + * - if the result is a success, the returned port is expect_success_port, + * and the returned family is AF_INET or AF_INET6. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_lookup(host_port_str, &addr, &port); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 0); \ + if (r == -1) { \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + } else { \ + tt_assert(tor_addr_family(&addr) == AF_INET || \ + tor_addr_family(&addr) == AF_INET6); \ + tt_int_op(port, OP_EQ, expect_success_port); \ + } \ + STMT_END + +/* Test that addr_str successfully parses as a canonical IPv4 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup() with AF_INET, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_port_lookup(), because there is no port. + */ +#define TEST_ADDR_V4_PARSE_CANONICAL(addr_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 111, AF_INET, 0, \ + addr_str, 111); \ + TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET, 0, addr_str, 0); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET, AF_INET, 0, addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET6); \ + STMT_END + +/* Test that addr_str successfully parses as a canonical fmt_decorated + * IPv6 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE_CANONICAL(addr_str, fmt_decorated) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 222, AF_INET6, fmt_decorated, \ + addr_str, 222); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, addr_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_str successfully parses, and the fmt_decorated canonical + * IPv6 string is expect_str. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port. + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE(addr_str, fmt_decorated, expect_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, expect_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 333, AF_INET6, fmt_decorated, \ + expect_str, 333); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, expect_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical IPv4 address + * string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port. + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V4_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 444, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical undecorated + * IPv6 address string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port, and because it only + * supports IPv4, + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V6_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 555, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + STMT_END + +/* Test that bad_str fails to parse due to a bad address or port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(). + */ +#define TEST_ADDR_PARSE_XFAIL_MALFORMED(bad_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_XFAIL(bad_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, 666); \ + TEST_ADDR_V4_LOOKUP_XFAIL(bad_str); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET6); \ + TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str); \ + STMT_END + +/* Test that host_str is treated as a hostname, and not an address. + * Check for success or failure using the network-dependent functions: + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(), expecting a zero port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port. + */ +#define TEST_HOSTNAME(host_str) \ + STMT_BEGIN \ + TEST_HOST_V4_LOOKUP(host_str); \ + TEST_HOST_LOOKUP(host_str, AF_UNSPEC); \ + TEST_HOST_LOOKUP(host_str, AF_INET); \ + TEST_HOST_LOOKUP(host_str, AF_INET6); \ + TEST_HOST_PORT_LOOKUP(host_str, 0); \ + TEST_ADDR_PARSE_XFAIL(host_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, 777); \ + STMT_END + +/* Test that host_port_str is treated as a hostname and port, and not a + * hostname or an address. + * Check for success or failure using the network-dependent function: + * - tor_addr_port_lookup(), expecting expect_success_port if the lookup is + * successful. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), because it doesn't support ports, + * - tor_addr_lookup(), regardless of the address family, because it doesn't + * support ports. + */ +#define TEST_HOSTNAME_PORT(host_port_str, expect_success_port) \ + STMT_BEGIN \ + TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port); \ + TEST_ADDR_PARSE_XFAIL(host_port_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, 888); \ + TEST_ADDR_V4_LOOKUP_XFAIL(host_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET6); \ + STMT_END + /** Test tor_addr_parse() and tor_addr_port_parse(). */ static void test_addr_parse(void *arg) { - int r; - tor_addr_t addr; - char buf[TOR_ADDR_BUF_LEN]; - uint16_t port = 0; - - /* Correct call. */ (void)arg; - r= tor_addr_parse(&addr, "192.0.2.1"); - tt_int_op(r,OP_EQ, AF_INET); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - - r= tor_addr_parse(&addr, "11:22::33:44"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "[11:22::33:44]"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "11:22:33:44:55:66:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22:33:44:55:66:102:304"); - - r= tor_addr_parse(&addr, "11:22::33:44:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44:102:304"); - /* Empty string. */ - r= tor_addr_parse(&addr, ""); - tt_int_op(r,OP_EQ, -1); + /* Correct calls. */ + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.1"); + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.2"); - /* Square brackets around IPv4 address. */ - r= tor_addr_parse(&addr, "[192.0.2.1]"); - tt_int_op(r,OP_EQ, -1); + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::33:44]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::1]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[2::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[11:22:33:44:55:66:77:88]", 1); - /* Only left square bracket. */ - r= tor_addr_parse(&addr, "[11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + /* Allow IPv6 without square brackets, when there is no port, but only if + * there is a default port */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::33:44", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::1", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("2::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("11:22:33:44:55:66:77:88", 0); - /* Only right square bracket. */ - r= tor_addr_parse(&addr, "11:22::33:44]"); - tt_int_op(r,OP_EQ, -1); + /* IPv6-mapped IPv4 addresses. Tor doesn't really use these. */ + TEST_ADDR_V6_PARSE("11:22:33:44:55:66:1.2.3.4", 0, + "11:22:33:44:55:66:102:304"); - /* Leading colon. */ - r= tor_addr_parse(&addr, ":11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + TEST_ADDR_V6_PARSE("11:22::33:44:1.2.3.4", 0, + "11:22::33:44:102:304"); - /* Trailing colon. */ - r= tor_addr_parse(&addr, "11:22::33:44:"); - tt_int_op(r,OP_EQ, -1); - - /* Too many hex words in IPv4-mapped IPv6 address. */ - r= tor_addr_parse(&addr, "11:22:33:44:55:66:77:88:1.2.3.4"); - tt_int_op(r,OP_EQ, -1); - - /* Correct call. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.1:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - tt_int_op(port,OP_EQ, 1234); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "::1"); - tt_int_op(port,OP_EQ, 1234); - - /* Domain name. */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Ports. */ + TEST_ADDR_V4_PORT_PARSE("192.0.2.1:1234", "192.0.2.1", 1234); + TEST_ADDR_V6_PORT_PARSE("[::1]:1234", "::1", 1234); - /* Only IP. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Host names. */ + TEST_HOSTNAME("localhost"); + TEST_HOSTNAME_PORT("localhost:1234", 1234); + TEST_HOSTNAME_PORT("localhost:0", 0); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,200); + TEST_HOSTNAME("torproject.org"); + TEST_HOSTNAME_PORT("torproject.org:56", 56); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + TEST_HOSTNAME("probably-not-a-valid-dns.name-tld"); + TEST_HOSTNAME_PORT("probably-not-a-valid-dns.name-tld:789", 789); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, 400); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,400); + /* Malformed addresses. */ + /* Empty string. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(""); - /* Bad port. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Square brackets around IPv4 address. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.1]"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.3]:12345"); - /* Only domain name */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Only left square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[11:22::33:44"); - /* Bad IP address */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Only right square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44]"); + + /* Leading colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(":11:22::33:44"); + + /* Trailing colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:"); - /* Make sure that the default port has lower priority than the real - one */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:1337", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1337); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1369", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1369); + /* Bad port. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:66666"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:77777"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:88888"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:99999"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:-2"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:-3"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:-4"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:1 bad"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:bad-port-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port-1"); + + /* Bad hostname */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid:22222"); + + /* Ambiguous cases */ + /* Too many hex words in IPv4-mapped IPv6 address. + * But some OS host lookup routines accept it as a hostname, or + * as an IP address?? (I assume they discard unused characters). */ + TEST_HOSTNAME("11:22:33:44:55:66:77:88:1.2.3.4"); + + /* IPv6 address with port and no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address:port ? */ + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + /* Is it a port, or are there too many hex words? + * We reject it either way, but some OS host lookup routines accept it as an + * IPv6 address:port */ + TEST_HOSTNAME_PORT("11:22:33:44:55:66:77:88:99", 99); + /* But we accept it if it has square brackets. */ + TEST_ADDR_V6_PORT_PARSE("[11:22:33:44:55:66:77:88]:99", + "11:22:33:44:55:66:77:88",99); + + /* Bad IPv4 address + * We reject it, but some OS host lookup routines accept it as an + * IPv4 address[:port], with a zero last octet */ + TEST_HOSTNAME("192.0.1"); + TEST_HOSTNAME_PORT("192.0.2:1234", 1234); + + /* More bad IPv6 addresses and ports: no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address[:port] */ + TEST_HOSTNAME_PORT("::1:12345", 12345); + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + + /* And this is an ambiguous case, which is interpreted as an IPv6 address. */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::88:99", 0); + /* Use square brackets to resolve the ambiguity */ + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::88:99]", 1); + TEST_ADDR_V6_PORT_PARSE("[11:22::88]:99", + "11:22::88",99); done: ; diff --git a/src/test/test_btrack.c b/src/test/test_btrack.c index 48486fb5a1..21e88a57b6 100644 --- a/src/test/test_btrack.c +++ b/src/test/test_btrack.c @@ -4,6 +4,7 @@ #include "core/or/or.h" #include "test/test.h" +#include "test_helpers.h" #include "test/log_test_helpers.h" #define OCIRC_EVENT_PRIVATE @@ -12,48 +13,75 @@ #include "core/or/orconn_event.h" static void +send_state(const orconn_state_msg_t *msg_in) +{ + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_state_publish(msg); +} + +static void +send_status(const orconn_status_msg_t *msg_in) +{ + orconn_status_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_status_publish(msg); +} + +static void +send_chan(const ocirc_chan_msg_t *msg_in) +{ + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + ocirc_chan_publish(msg); +} + +static void test_btrack_launch(void *arg) { - orconn_event_msg_t conn; - ocirc_event_msg_t circ; + orconn_state_msg_t conn; + ocirc_chan_msg_t circ; + memset(&conn, 0, sizeof(conn)); + memset(&circ, 0, sizeof(circ)); (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + conn.gid = 1; + conn.chan = 1; + conn.proxy_type = PROXY_NONE; + conn.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.type = OCIRC_MSGTYPE_CHAN; - circ.u.chan.chan = 1; - circ.u.chan.onehop = true; + circ.chan = 1; + circ.onehop = true; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=1 onehop=1"); expect_log_msg_containing("ORCONN BEST_ANY state -1->1 gid=1"); teardown_capture_of_logs(); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=2 chan=2 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.u.chan.chan = 2; - circ.u.chan.onehop = false; + circ.chan = 2; + circ.onehop = false; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=2 onehop=0"); expect_log_msg_containing("ORCONN BEST_AP state -1->1 gid=2"); teardown_capture_of_logs(); @@ -65,27 +93,28 @@ test_btrack_launch(void *arg) static void test_btrack_delete(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t state; + orconn_status_msg_t status; + memset(&state, 0, sizeof(state)); + memset(&status, 0, sizeof(status)); (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + state.gid = 1; + state.chan = 1; + state.proxy_type = PROXY_NONE; + state.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&state); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0"); teardown_capture_of_logs(); - conn.type = ORCONN_MSGTYPE_STATUS; - conn.u.status.gid = 1; - conn.u.status.status = OR_CONN_EVENT_CLOSED; - conn.u.status.reason = 0; + status.gid = 1; + status.status = OR_CONN_EVENT_CLOSED; + status.reason = 0; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_status(&status); expect_log_msg_containing("ORCONN DELETE gid=1 status=3 reason=0"); teardown_capture_of_logs(); @@ -94,7 +123,7 @@ test_btrack_delete(void *arg) } struct testcase_t btrack_tests[] = { - { "launch", test_btrack_launch, TT_FORK, 0, NULL }, - { "delete", test_btrack_delete, TT_FORK, 0, NULL }, + { "launch", test_btrack_launch, TT_FORK, &helper_pubsub_setup, NULL }, + { "delete", test_btrack_delete, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index 5d012e462b..885246628e 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -289,8 +289,6 @@ test_channelpadding_timers(void *arg) channel_t *chans[CHANNELS_TO_TEST]; (void)arg; - tor_libevent_postfork(); - if (!connection_array) connection_array = smartlist_new(); @@ -393,7 +391,6 @@ test_channelpadding_killonehop(void *arg) channelpadding_decision_t decision; int64_t new_time; (void)arg; - tor_libevent_postfork(); routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t)); monotime_init(); @@ -502,8 +499,6 @@ test_channelpadding_consensus(void *arg) int64_t new_time; (void)arg; - tor_libevent_postfork(); - /* * Params tested: * nf_pad_before_usage @@ -898,8 +893,6 @@ test_channelpadding_decide_to_pad_channel(void *arg) connection_array = smartlist_new(); (void)arg; - tor_libevent_postfork(); - monotime_init(); monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 0c23091594..196d8cd355 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -177,6 +177,6 @@ struct testcase_t circuitbuild_tests[] = { { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, { "upgrade_from_guard_wait", test_upgrade_from_guard_wait, TT_FORK, - NULL, NULL }, + &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c index 25f8fd311b..934ddb0208 100644 --- a/src/test/test_circuitpadding.c +++ b/src/test/test_circuitpadding.c @@ -4,9 +4,11 @@ #define CIRCUITPADDING_MACHINES_PRIVATE #define NETWORKSTATUS_PRIVATE #define CRYPT_PATH_PRIVATE +#define RELAY_PRIVATE #include "core/or/or.h" #include "test/test.h" +#include "test/log_test_helpers.h" #include "lib/testsupport/testsupport.h" #include "core/or/connection_or.h" #include "core/or/channel.h" @@ -3152,6 +3154,29 @@ test_circuitpadding_hs_machines(void *arg) UNMOCK(circpad_machine_schedule_padding); } +/** Test that we effectively ignore non-padding cells in padding circuits. */ +static void +test_circuitpadding_ignore_non_padding_cells(void *arg) +{ + int retval; + relay_header_t rh; + + (void) arg; + + client_side = (circuit_t *)origin_circuit_new(); + client_side->purpose = CIRCUIT_PURPOSE_C_CIRCUIT_PADDING; + + rh.command = RELAY_COMMAND_BEGIN; + + setup_full_capture_of_logs(LOG_INFO); + retval = handle_relay_cell_command(NULL, client_side, NULL, NULL, &rh, 0); + tt_int_op(retval, OP_EQ, 0); + expect_log_msg_containing("Ignored cell"); + + done: + ; +} + #define TEST_CIRCUITPADDING(name, flags) \ { #name, test_##name, (flags), NULL, NULL } @@ -3175,5 +3200,6 @@ struct testcase_t circuitpadding_tests[] = { TEST_CIRCUITPADDING(circuitpadding_token_removal_exact, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_manage_circuit_lifetime, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_hs_machines, TT_FORK), + TEST_CIRCUITPADDING(circuitpadding_ignore_non_padding_cells, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c index 2a09622f09..9bfaabeb2f 100644 --- a/src/test/test_circuitstats.c +++ b/src/test/test_circuitstats.c @@ -197,7 +197,7 @@ test_circuitstats_hoplen(void *arg) } #define TEST_CIRCUITSTATS(name, flags) \ - { #name, test_##name, (flags), NULL, NULL } + { #name, test_##name, (flags), &helper_pubsub_setup, NULL } struct testcase_t circuitstats_tests[] = { TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK), diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 5d625483da..ecd97e3474 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -151,8 +151,6 @@ test_compat_libevent_postloop_events(void *arg) mainloop_event_t *a = NULL, *b = NULL; periodic_timer_t *timed = NULL; - tor_libevent_postfork(); - /* If postloop events don't work, then these events will activate one * another ad infinitum and, and the periodic event will never occur. */ b = mainloop_event_postloop_new(activate_event_cb, &a); diff --git a/src/test/test_confparse.c b/src/test/test_confparse.c new file mode 100644 index 0000000000..dde61b1c81 --- /dev/null +++ b/src/test/test_confparse.c @@ -0,0 +1,904 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * Tests for confparse.c module that we use to parse various + * configuration/state file types. + */ + +#define CONFPARSE_PRIVATE +#include "orconfig.h" + +#include "core/or/or.h" +#include "lib/encoding/confline.h" +#include "feature/nodelist/routerset.h" +#include "app/config/confparse.h" +#include "test/test.h" +#include "test/log_test_helpers.h" + +#include "lib/confmgt/unitparse.h" + +typedef struct test_struct_t { + uint32_t magic; + char *s; + char *fn; + int pos; + int i; + int deprecated_int; + uint64_t u64; + int interval; + int msec_interval; + uint64_t mem; + double dbl; + int boolean; + int autobool; + time_t time; + smartlist_t *csv; + int csv_interval; + config_line_t *lines; + config_line_t *mixed_lines; + routerset_t *routerset; + int hidden_int; + config_line_t *mixed_hidden_lines; + + config_line_t *extra_lines; +} test_struct_t; + +static test_struct_t test_struct_t_dummy; + +#define VAR(name,conftype,member,initvalue) \ + { name, CONFIG_TYPE_##conftype, offsetof(test_struct_t, member), \ + initvalue CONF_TEST_MEMBERS(test_struct_t, conftype, member) } + +#define V(name,conftype,initvalue) \ + VAR( #name, conftype, name, initvalue ) + +#define OBSOLETE(name) \ + { name, CONFIG_TYPE_OBSOLETE, 0, NULL, {.INT=NULL} } + +static config_var_t test_vars[] = { + V(s, STRING, "hello"), + V(fn, FILENAME, NULL), + V(pos, POSINT, NULL), + V(i, INT, "-10"), + V(deprecated_int, INT, "3"), + V(u64, UINT64, NULL), + V(interval, INTERVAL, "10 seconds"), + V(msec_interval, MSEC_INTERVAL, "150 msec"), + V(mem, MEMUNIT, "10 MB"), + V(dbl, DOUBLE, NULL), + V(boolean, BOOL, "0"), + V(autobool, AUTOBOOL, "auto"), + V(time, ISOTIME, NULL), + V(csv, CSV, NULL), + V(csv_interval, CSV_INTERVAL, "5 seconds"), + V(lines, LINELIST, NULL), + VAR("MixedLines", LINELIST_V, mixed_lines, NULL), + VAR("LineTypeA", LINELIST_S, mixed_lines, NULL), + VAR("LineTypeB", LINELIST_S, mixed_lines, NULL), + OBSOLETE("obsolete"), + V(routerset, ROUTERSET, NULL), + VAR("__HiddenInt", POSINT, hidden_int, "0"), + VAR("MixedHiddenLines", LINELIST_V, mixed_hidden_lines, NULL), + VAR("__HiddenLineA", LINELIST_S, mixed_hidden_lines, NULL), + VAR("VisibleLineB", LINELIST_S, mixed_hidden_lines, NULL), + + END_OF_CONFIG_VARS, +}; + +static config_abbrev_t test_abbrevs[] = { + { "uint", "pos", 0, 0 }, + { "float", "dbl", 0, 1 }, + { NULL, NULL, 0, 0 } +}; + +static config_deprecation_t test_deprecation_notes[] = { + { "deprecated_int", "This integer is deprecated." }, + { NULL, NULL } +}; + +static int +test_validate_cb(void *old_options, void *options, void *default_options, + int from_setconf, char **msg) +{ + (void)old_options; + (void)default_options; + (void)from_setconf; + (void)msg; + test_struct_t *ts = options; + + if (ts->i == 0xbad) { + *msg = tor_strdup("bad value for i"); + return -1; + } + return 0; +} + +static void test_free_cb(void *options); + +#define TEST_MAGIC 0x1337 + +static config_format_t test_fmt = { + sizeof(test_struct_t), + TEST_MAGIC, + offsetof(test_struct_t, magic), + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + test_free_cb, + NULL, +}; + +static void +test_free_cb(void *options) +{ + if (!options) + return; + + config_free(&test_fmt, options); +} + +/* Make sure that config_init sets everything to the right defaults. */ +static void +test_confparse_init(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_init(&test_fmt, tst); + + // Make sure that options are initialized right. */ + tt_uint_op(tst->magic, OP_EQ, TEST_MAGIC); + tt_str_op(tst->s, OP_EQ, "hello"); + tt_ptr_op(tst->fn, OP_EQ, NULL); + tt_int_op(tst->pos, OP_EQ, 0); + tt_int_op(tst->i, OP_EQ, -10); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, 0); + tt_int_op(tst->interval, OP_EQ, 10); + tt_int_op(tst->msec_interval, OP_EQ, 150); + tt_u64_op(tst->mem, OP_EQ, 10 * 1024 * 1024); + tt_double_op(tst->dbl, OP_LT, .0000000001); + tt_double_op(tst->dbl, OP_GT, -0.0000000001); + tt_int_op(tst->boolean, OP_EQ, 0); + tt_int_op(tst->autobool, OP_EQ, -1); + tt_i64_op(tst->time, OP_EQ, 0); + tt_ptr_op(tst->csv, OP_EQ, NULL); + tt_int_op(tst->csv_interval, OP_EQ, 5); + tt_ptr_op(tst->lines, OP_EQ, NULL); + tt_ptr_op(tst->mixed_lines, OP_EQ, NULL); + tt_int_op(tst->hidden_int, OP_EQ, 0); + + done: + config_free(&test_fmt, tst); +} + +static const char simple_settings[] = + "s this is a \n" + "fn /simple/test of the\n" + "uint 77\n" // this is an abbrev + "i 3\n" + "u64 1000000000000 \n" + "interval 5 minutes \n" + "msec_interval 5 minutes \n" + "mem 10\n" + "dbl 6.060842\n" + "BOOLEAN 1\n" + "aUtObOOl 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration, parsing , system \n" + "csv_interval 10 seconds, 5 seconds, 10 hours\n" + "lines hello\n" + "LINES world\n" + "linetypea i d\n" + "linetypeb i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "__hiddenint 11\n" + "__hiddenlineA XYZ\n" + "visiblelineB ABC\n"; + +/* Return a configuration object set up from simple_settings above. */ +static test_struct_t * +get_simple_config(void) +{ + test_struct_t *result = NULL; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines(simple_settings, &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + result = tst; + tst = NULL; // prevent free + done: + tor_free(msg); + config_free_lines(lines); + config_free(&test_fmt, tst); + return result; +} + +/* Make sure that config_assign can parse things. */ +static void +test_confparse_assign_simple(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + + tt_str_op(tst->s, OP_EQ, "this is a"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); + tt_int_op(tst->pos, OP_EQ, 77); + tt_int_op(tst->i, OP_EQ, 3); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, UINT64_C(1000000000000)); + tt_int_op(tst->interval, OP_EQ, 5 * 60); + tt_int_op(tst->msec_interval, OP_EQ, 5 * 60 * 1000); + tt_u64_op(tst->mem, OP_EQ, 10); + tt_double_op(tst->dbl, OP_LT, 6.060843); + tt_double_op(tst->dbl, OP_GT, 6.060841); + tt_int_op(tst->boolean, OP_EQ, 1); + tt_int_op(tst->autobool, OP_EQ, 0); + tt_i64_op(tst->time, OP_EQ, 1560520731); + tt_ptr_op(tst->csv, OP_NE, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 3); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "configuration"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "parsing"); + tt_str_op(smartlist_get(tst->csv, 2), OP_EQ, "system"); + tt_int_op(tst->csv_interval, OP_EQ, 10); + tt_int_op(tst->hidden_int, OP_EQ, 11); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(!tst->lines->next->next); + + tt_assert(tst->mixed_lines); + tt_str_op(tst->mixed_lines->key, OP_EQ, "LineTypeA"); + tt_str_op(tst->mixed_lines->value, OP_EQ, "i d"); + tt_assert(tst->mixed_lines->next); + tt_str_op(tst->mixed_lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(tst->mixed_lines->next->value, OP_EQ, "i c"); + tt_assert(!tst->mixed_lines->next->next); + + tt_assert(tst->mixed_hidden_lines); + tt_str_op(tst->mixed_hidden_lines->key, OP_EQ, "__HiddenLineA"); + tt_str_op(tst->mixed_hidden_lines->value, OP_EQ, "XYZ"); + tt_assert(tst->mixed_hidden_lines->next); + tt_str_op(tst->mixed_hidden_lines->next->key, OP_EQ, "VisibleLineB"); + tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC"); + tt_assert(!tst->mixed_hidden_lines->next->next); + + done: + config_free(&test_fmt, tst); +} + +/* Try to assign to an obsolete option, and make sure we get a warning. */ +static void +test_confparse_assign_obsolete(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("obsolete option here", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("Skipping obsolete configuration option"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to assign to an deprecated option, and make sure we get a warning + * but the assignment works anyway. */ +static void +test_confparse_assign_deprecated(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("deprecated_int 7", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("This integer is deprecated."); + + tt_int_op(tst->deprecated_int, OP_EQ, 7); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to re-assign an option name that has been depreacted in favor of + * another. */ +static void +test_confparse_assign_replaced(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("float 1000\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("use 'dbl' instead."); + + tt_double_op(tst->dbl, OP_GT, 999.999); + tt_double_op(tst->dbl, OP_LT, 1000.001); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to set a linelist value with no option. */ +static void +test_confparse_assign_emptystring(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("lines\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("has no value"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to set a the same option twice; make sure we get a warning. */ +static void +test_confparse_assign_twice(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("pos 10\n" + "pos 99\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("used more than once"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +typedef struct badval_test_t { + const char *cfg; + const char *expect_msg; +} badval_test_t; + +/* Try to set an option and make sure that we get a failure and an expected + * warning. */ +static void +test_confparse_assign_badval(void *arg) +{ + const badval_test_t *bt = arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines(bt->cfg, &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + if (! strstr(msg, bt->expect_msg)) { + TT_DIE(("'%s' did not contain '%s'" , msg, bt->expect_msg)); + } + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Various arguments for badval test. + * + * Note that the expected warnings here are _very_ truncated, since we + * are writing these tests before a refactoring that we expect will + * change them. + */ +static const badval_test_t bv_notint = { "pos X\n", "malformed" }; +static const badval_test_t bv_negint = { "pos -10\n", "out of bounds" }; +static const badval_test_t bv_badu64 = { "u64 u64\n", "malformed" }; +static const badval_test_t bv_badcsvi1 = + { "csv_interval 10 wl\n", "malformed" }; +static const badval_test_t bv_badcsvi2 = + { "csv_interval cl,10\n", "malformed" }; +static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" }; +static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" }; +static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"}; +static const badval_test_t bv_badabool = + { "autobool 7\n", "Unrecognized value" }; +static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" }; +static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" }; +static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" }; + +/* Try config_dump(), and make sure it behaves correctly */ +static void +test_confparse_dump(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + char *dumped = NULL; + + /* Minimal version. */ + dumped = config_dump(&test_fmt, NULL, tst, 1, 0); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + /* Maximal */ + tor_free(dumped); + dumped = config_dump(&test_fmt, NULL, tst, 0, 0); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "deprecated_int 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + /* commented */ + tor_free(dumped); + dumped = config_dump(&test_fmt, NULL, tst, 0, 1); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "# deprecated_int 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + done: + config_free(&test_fmt, tst); + tor_free(dumped); +} + +/* Try confparse_reset_line(), and make sure it behaves correctly */ +static void +test_confparse_reset(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + + config_reset_line(&test_fmt, tst, "interval", 0); + tt_int_op(tst->interval, OP_EQ, 0); + + config_reset_line(&test_fmt, tst, "interval", 1); + tt_int_op(tst->interval, OP_EQ, 10); + + done: + config_free(&test_fmt, tst); +} + +/* Try setting options a second time on a config object, and make sure + * it behaves correctly. */ +static void +test_confparse_reassign(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + char *msg = NULL, *rs = NULL; + + int r = config_get_lines( + "s eleven\n" + "i 12\n" + "lines 13\n" + "csv 14,15\n" + "routerset 127.0.0.1\n", + &lines, 0); + r = config_assign(&test_fmt, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_str_op(tst->s, OP_EQ, "eleven"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); // unchanged + tt_int_op(tst->pos, OP_EQ, 77); // unchanged + tt_int_op(tst->i, OP_EQ, 12); + tt_ptr_op(tst->lines, OP_NE, NULL); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "13"); + tt_ptr_op(tst->lines->next, OP_EQ, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 2); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "14"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "15"); + + rs = routerset_to_string(tst->routerset); + tt_str_op(rs, OP_EQ, "127.0.0.1"); + + // Try again with the CLEAR_FIRST and USE_DEFAULTS flags + r = config_assign(&test_fmt, tst, lines, + CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg); + tt_int_op(r, OP_EQ, 0); + + tt_ptr_op(msg, OP_EQ, NULL); + tt_str_op(tst->s, OP_EQ, "eleven"); + // tt_ptr_op(tst->fn, OP_EQ, NULL); //XXXX why is this not cleared? + // tt_int_op(tst->pos, OP_EQ, 0); //XXXX why is this not cleared? + tt_int_op(tst->i, OP_EQ, 12); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); + tor_free(rs); +} + +/* Try setting options a second time on a config object, using the +foo + * linelist-extending syntax. */ +static void +test_confparse_reassign_extend(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + char *msg = NULL; + + int r = config_get_lines( + "+lines 13\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(tst->lines->next->next); + tt_str_op(tst->lines->next->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->next->value, OP_EQ, "13"); + tt_assert(tst->lines->next->next->next == NULL); + config_free_lines(lines); + + r = config_get_lines( + "/lines\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + config_free_lines(lines); + + config_free(&test_fmt, tst); + tst = get_simple_config(); + r = config_get_lines( + "/lines away!\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Test out confparse_get_assigned(). */ +static void +test_confparse_get_assigned(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + + lines = config_get_assigned_option(&test_fmt, tst, "I", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "i"); + tt_str_op(lines->value, OP_EQ, "3"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "this is a"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "obsolete", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(&test_fmt, tst, "nonesuch", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(&test_fmt, tst, "mixedlines", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeA"); + tt_str_op(lines->value, OP_EQ, "i d"); + tt_assert(lines->next); + tt_str_op(lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->next->value, OP_EQ, "i c"); + tt_assert(lines->next->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "linetypeb", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->value, OP_EQ, "i c"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + tor_free(tst->s); + tst->s = tor_strdup("Hello\nWorld"); + lines = config_get_assigned_option(&test_fmt, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\""); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); +} + +/* Another variant, which accepts and stores unrecognized lines.*/ +#define ETEST_MAGIC 13371337 + +static config_var_t extra = VAR("__extra", LINELIST, extra_lines, NULL); + +static config_format_t etest_fmt = { + sizeof(test_struct_t), + ETEST_MAGIC, + offsetof(test_struct_t, magic), + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + test_free_cb, + &extra, +}; + +/* Try out the feature where we can store unrecognized lines and dump them + * again. (State files use this.) */ +static void +test_confparse_extra_lines(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&etest_fmt); + config_line_t *lines = NULL; + char *msg = NULL, *dump = NULL; + + config_init(&etest_fmt, tst); + + int r = config_get_lines( + "unknotty addita\n" + "pos 99\n" + "wombat knish\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(&etest_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->extra_lines); + + dump = config_dump(&etest_fmt, NULL, tst, 1, 0); + tt_str_op(dump, OP_EQ, + "pos 99\n" + "unknotty addita\n" + "wombat knish\n"); + + done: + tor_free(msg); + tor_free(dump); + config_free_lines(lines); + config_free(&etest_fmt, tst); +} + +static void +test_confparse_unitparse(void *args) +{ + (void)args; + /* spot-check a few memunit values. */ + int ok = 3; + tt_u64_op(config_parse_memunit("100 MB", &ok), OP_EQ, 100<<20); + tt_assert(ok); + tt_u64_op(config_parse_memunit("100 TB", &ok), OP_EQ, UINT64_C(100)<<40); + tt_assert(ok); + // This is a floating-point value, but note that 1.5 can be represented + // precisely. + tt_u64_op(config_parse_memunit("1.5 MB", &ok), OP_EQ, 3<<19); + tt_assert(ok); + + /* Try some good intervals and msec intervals */ + tt_int_op(config_parse_interval("2 days", &ok), OP_EQ, 48*3600); + tt_assert(ok); + tt_int_op(config_parse_interval("1.5 hour", &ok), OP_EQ, 5400); + tt_assert(ok); + tt_u64_op(config_parse_interval("1 minute", &ok), OP_EQ, 60); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("2 days", &ok), OP_EQ, 48*3600*1000); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("10 msec", &ok), OP_EQ, 10); + tt_assert(ok); + + /* Try a couple of unitless values. */ + tt_int_op(config_parse_interval("10", &ok), OP_EQ, 10); + tt_assert(ok); + tt_u64_op(config_parse_interval("15.0", &ok), OP_EQ, 15); + tt_assert(ok); + + /* u64 overflow */ + /* XXXX our implementation does not currently detect this. See bug 30920. */ + /* + tt_u64_op(config_parse_memunit("20000000 TB", &ok), OP_EQ, 0); + tt_assert(!ok); + */ + + /* i32 overflow */ + tt_int_op(config_parse_interval("1000 months", &ok), OP_EQ, -1); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("4 weeks", &ok), OP_EQ, -1); + tt_assert(!ok); + + /* bad units */ + tt_u64_op(config_parse_memunit("7 nybbles", &ok), OP_EQ, 0); + tt_assert(!ok); + // XXXX these next two should return -1 according to the documentation. + tt_int_op(config_parse_interval("7 cowznofski", &ok), OP_EQ, 0); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("1 kalpa", &ok), OP_EQ, 0); + tt_assert(!ok); + + done: + ; +} + +#define CONFPARSE_TEST(name, flags) \ + { #name, test_confparse_ ## name, flags, NULL, NULL } + +#define BADVAL_TEST(name) \ + { "badval_" #name, test_confparse_assign_badval, 0, \ + &passthrough_setup, (void*)&bv_ ## name } + +struct testcase_t confparse_tests[] = { + CONFPARSE_TEST(init, 0), + CONFPARSE_TEST(assign_simple, 0), + CONFPARSE_TEST(assign_obsolete, 0), + CONFPARSE_TEST(assign_deprecated, 0), + CONFPARSE_TEST(assign_replaced, 0), + CONFPARSE_TEST(assign_emptystring, 0), + CONFPARSE_TEST(assign_twice, 0), + BADVAL_TEST(notint), + BADVAL_TEST(negint), + BADVAL_TEST(badu64), + BADVAL_TEST(badcsvi1), + BADVAL_TEST(badcsvi2), + BADVAL_TEST(nonoption), + BADVAL_TEST(badmem), + BADVAL_TEST(badbool), + BADVAL_TEST(badabool), + BADVAL_TEST(badtime), + BADVAL_TEST(virt), + BADVAL_TEST(rs), + CONFPARSE_TEST(dump, 0), + CONFPARSE_TEST(reset, 0), + CONFPARSE_TEST(reassign, 0), + CONFPARSE_TEST(reassign_extend, 0), + CONFPARSE_TEST(get_assigned, 0), + CONFPARSE_TEST(extra_lines, 0), + CONFPARSE_TEST(unitparse, 0), + END_OF_TESTCASES +}; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index ee48d656bd..b9cbe0a14d 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -9,6 +9,7 @@ #include "feature/control/control.h" #include "feature/control/control_cmd.h" #include "feature/control/control_getinfo.h" +#include "feature/control/control_proto.h" #include "feature/client/entrynodes.h" #include "feature/hs/hs_common.h" #include "feature/nodelist/networkstatus.h" @@ -201,42 +202,58 @@ static const control_cmd_syntax_t one_arg_kwargs_syntax = { static const parse_test_params_t parse_one_arg_kwargs_params = TESTPARAMS( one_arg_kwargs_syntax, one_arg_kwargs_tests ); +static char *reply_str = NULL; +/* Mock for control_write_reply that copies the string for inspection + * by tests */ +static void +mock_control_write_reply(control_connection_t *conn, int code, int c, + const char *s) +{ + (void)conn; + (void)code; + (void)c; + tor_free(reply_str); + reply_str = tor_strdup(s); +} + static void test_add_onion_helper_keyarg_v3(void *arg) { 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; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit ED25519-V3 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); 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); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); /* Test discarding the private key. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); 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); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); @@ -256,9 +273,10 @@ test_add_onion_helper_keyarg_v3(void *arg) tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); tt_assert(key_blob); + tor_free(reply_str); ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tor_free(key_blob); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); @@ -266,7 +284,7 @@ test_add_onion_helper_keyarg_v3(void *arg) 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); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); } @@ -274,7 +292,8 @@ test_add_onion_helper_keyarg_v3(void *arg) done: tor_free(pk.v3); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } static void @@ -285,72 +304,73 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_t *pk1 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; char *encoded = NULL; char *arg_str = NULL; (void) arg; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit RSA1024 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test "BEST" key generation (Assumes BEST = RSA1024). */ crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test discarding the private key. */ crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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_ptr_op(reply_str, OP_EQ, NULL); /* Test generating a invalid key type. */ 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); + &pk, &hs_version, NULL); 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); + tt_assert(reply_str); /* Test loading a RSA1024 key. */ - tor_free(err_msg); + tor_free(reply_str); pk1 = pk_generate(0); tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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_ptr_op(reply_str, OP_EQ, NULL); tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0); /* Test loading a invalid key type. */ @@ -359,36 +379,37 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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); + tt_assert(reply_str); /* Test loading a invalid key. */ tor_free(arg_str); crypto_pk_free(pk.v2); pk.v2 = NULL; - tor_free(err_msg); + tor_free(reply_str); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); 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); + tt_assert(reply_str); done: crypto_pk_free(pk1); crypto_pk_free(pk.v2); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); tor_free(encoded); tor_free(arg_str); + UNMOCK(control_write_reply); } static void @@ -542,49 +563,52 @@ static void test_add_onion_helper_clientauth(void *arg) { rend_authorized_client_t *client = NULL; - char *err_msg = NULL; int created = 0; (void)arg; + MOCK(control_write_reply, mock_control_write_reply); /* Test "ClientName" only. */ - client = add_onion_helper_clientauth("alice", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice", &created, NULL); tt_assert(client); tt_assert(created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test "ClientName:Blob" */ + tor_free(reply_str); client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B", - &created, &err_msg); + &created, NULL); tt_assert(client); tt_assert(!created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test invalid client names */ + tor_free(reply_str); client = add_onion_helper_clientauth("no*asterisks*allowed", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid auth cookie */ - client = add_onion_helper_clientauth("alice:12345", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice:12345", &created, NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid syntax */ + tor_free(reply_str); client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); done: rend_authorized_client_free(client); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } /* Mocks and data/variables used for GETINFO download status tests */ diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 910aacace3..9fb2bc7256 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -7,6 +7,7 @@ #define CONTROL_EVENTS_PRIVATE #define OCIRC_EVENT_PRIVATE #define ORCONN_EVENT_PRIVATE +#include "app/main/subsysmgr.h" #include "core/or/or.h" #include "core/or/channel.h" #include "core/or/channeltls.h" @@ -16,6 +17,7 @@ #include "core/mainloop/connection.h" #include "feature/control/control_events.h" #include "test/test.h" +#include "test/test_helpers.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" @@ -394,38 +396,40 @@ test_cntev_dirboot_defer_orconn(void *arg) } static void -setup_orconn_state(orconn_event_msg_t *msg, uint64_t gid, uint64_t chan, +setup_orconn_state(orconn_state_msg_t *msg, uint64_t gid, uint64_t chan, int proxy_type) { - msg->type = ORCONN_MSGTYPE_STATE; - msg->u.state.gid = gid; - msg->u.state.chan = chan; - msg->u.state.proxy_type = proxy_type; + msg->gid = gid; + msg->chan = chan; + msg->proxy_type = proxy_type; } static void -send_orconn_state(orconn_event_msg_t *msg, uint8_t state) +send_orconn_state(const orconn_state_msg_t *msg_in, uint8_t state) { - msg->u.state.state = state; - orconn_event_publish(msg); + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + msg->state = state; + orconn_state_publish(msg); } static void send_ocirc_chan(uint32_t gid, uint64_t chan, bool onehop) { - ocirc_event_msg_t msg; + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = OCIRC_MSGTYPE_CHAN; - msg.u.chan.gid = gid; - msg.u.chan.chan = chan; - msg.u.chan.onehop = onehop; - ocirc_event_publish(&msg); + msg->gid = gid; + msg->chan = chan; + msg->onehop = onehop; + ocirc_chan_publish(msg); } static void test_cntev_orconn_state(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -442,8 +446,8 @@ test_cntev_orconn_state(void *arg) send_orconn_state(&conn, OR_CONN_STATE_OPEN); assert_bootmsg("15 TAG=handshake_done"); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); /* It doesn't know it's an origin circuit yet */ assert_bootmsg("15 TAG=handshake_done"); @@ -464,7 +468,8 @@ test_cntev_orconn_state(void *arg) static void test_cntev_orconn_state_pt(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -484,8 +489,8 @@ test_cntev_orconn_state_pt(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("76 TAG=ap_conn_pt"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -499,7 +504,8 @@ test_cntev_orconn_state_pt(void *arg) static void test_cntev_orconn_state_proxy(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -519,8 +525,8 @@ test_cntev_orconn_state_proxy(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("78 TAG=ap_conn_proxy"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -534,15 +540,18 @@ test_cntev_orconn_state_proxy(void *arg) #define TEST(name, flags) \ { #name, test_cntev_ ## name, flags, 0, NULL } +#define T_PUBSUB(name, setup) \ + { #name, test_cntev_ ## name, TT_FORK, &helper_pubsub_setup, NULL } + struct testcase_t controller_event_tests[] = { TEST(sum_up_cell_stats, TT_FORK), TEST(append_cell_stats, TT_FORK), TEST(format_cell_stats, TT_FORK), TEST(event_mask, TT_FORK), - TEST(dirboot_defer_desc, TT_FORK), - TEST(dirboot_defer_orconn, TT_FORK), - TEST(orconn_state, TT_FORK), - TEST(orconn_state_pt, TT_FORK), - TEST(orconn_state_proxy, TT_FORK), + T_PUBSUB(dirboot_defer_desc, TT_FORK), + T_PUBSUB(dirboot_defer_orconn, TT_FORK), + T_PUBSUB(orconn_state, TT_FORK), + T_PUBSUB(orconn_state_pt, TT_FORK), + T_PUBSUB(orconn_state_proxy, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 38aca90266..cb53a4e662 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -587,6 +587,6 @@ struct testcase_t extorport_tests[] = { { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL }, { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK, NULL, NULL }, - { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL }, + { "handshake", test_ext_or_handshake, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 489c257761..b4389f2d17 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -17,12 +17,17 @@ #include "lib/buf/buffers.h" #include "app/config/config.h" #include "app/config/confparse.h" +#include "app/main/subsysmgr.h" #include "core/mainloop/connection.h" #include "lib/crypt_ops/crypto_rand.h" #include "core/mainloop/mainloop.h" #include "feature/nodelist/nodelist.h" #include "core/or/relay.h" #include "feature/nodelist/routerlist.h" +#include "lib/dispatch/dispatch.h" +#include "lib/dispatch/dispatch_naming.h" +#include "lib/pubsub/pubsub_build.h" +#include "lib/pubsub/pubsub_connect.h" #include "lib/encoding/confline.h" #include "lib/net/resolve.h" @@ -303,3 +308,54 @@ helper_parse_options(const char *conf) } return opt; } + +/** + * Dispatch alertfn callback: flush all messages right now. Implements + * DELIV_IMMEDIATE. + **/ +static void +alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void) arg; + dispatch_flush(d, chan, INT_MAX); +} + +/** + * Setup helper for tests that need pubsub active + * + * Does not hook up mainloop events. Does set immediate delivery for + * all channels. + */ +void * +helper_setup_pubsub(const struct testcase_t *testcase) +{ + dispatch_t *dispatcher = NULL; + pubsub_builder_t *builder = pubsub_builder_new(); + channel_id_t chan = get_channel_id("orconn"); + + (void)testcase; + (void)subsystems_add_pubsub(builder); + dispatcher = pubsub_builder_finalize(builder, NULL); + tor_assert(dispatcher); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + chan = get_channel_id("ocirc"); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + return dispatcher; +} + +/** + * Cleanup helper for tests that need pubsub active + */ +int +helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_) +{ + dispatch_t *dispatcher = dispatcher_; + + (void)testcase; + dispatch_free(dispatcher); + return 1; +} + +const struct testcase_setup_t helper_pubsub_setup = { + helper_setup_pubsub, helper_cleanup_pubsub +}; diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 9e376a563d..d82072bb34 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -7,6 +7,7 @@ #define BUFFERS_PRIVATE #include "core/or/or.h" +#include "tinytest.h" const char *get_yesterday_date_str(void); @@ -31,5 +32,10 @@ or_options_t *helper_parse_options(const char *conf); extern const char TEST_DESCRIPTORS[]; +void *helper_setup_pubsub(const struct testcase_t *); +int helper_cleanup_pubsub(const struct testcase_t *, void *); + +extern const struct testcase_setup_t helper_pubsub_setup; + #endif /* !defined(TOR_TEST_HELPERS_H) */ diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index d71f8b6b18..86ac7e7fb1 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -10,6 +10,7 @@ #define DIRCACHE_PRIVATE #define DIRCLIENT_PRIVATE #define HS_CACHE_PRIVATE +#define TOR_CHANNEL_INTERNAL_ #include "trunnel/ed25519_cert.h" #include "feature/hs/hs_cache.h" @@ -20,7 +21,12 @@ #include "core/mainloop/connection.h" #include "core/proto/proto_http.h" #include "lib/crypt_ops/crypto_format.h" +#include "core/or/circuitlist.h" +#include "core/or/channel.h" +#include "core/or/edge_connection_st.h" +#include "core/or/or_circuit_st.h" +#include "core/or/or_connection_st.h" #include "feature/dircommon/dir_connection_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -232,6 +238,8 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) /* The dir conn we are going to simulate */ dir_connection_t *conn = NULL; + edge_connection_t *edge_conn = NULL; + or_circuit_t *or_circ = NULL; /* First extract the blinded public key that we are going to use in our query, and then build the actual query string. */ @@ -245,8 +253,16 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) /* Simulate an HTTP GET request to the HSDir */ conn = dir_connection_new(AF_INET); tt_assert(conn); + TO_CONN(conn)->linked = 1; /* Signal that it is encrypted. */ tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); - TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */ + + /* Pretend this conn is anonymous. */ + edge_conn = edge_connection_new(CONN_TYPE_EXIT, AF_INET); + TO_CONN(conn)->linked_conn = TO_CONN(edge_conn); + or_circ = or_circuit_new(0, NULL); + or_circ->p_chan = tor_malloc_zero(sizeof(channel_t)); + edge_conn->on_circuit = TO_CIRCUIT(or_circ); + retval = directory_handle_command_get(conn, hsdir_query_str, NULL, 0); tt_int_op(retval, OP_EQ, 0); @@ -263,8 +279,11 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) done: tor_free(hsdir_query_str); - if (conn) + if (conn) { + tor_free(or_circ->p_chan); + connection_free_minimal(TO_CONN(conn)->linked_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 0d25a98bb3..fb497d52a1 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -37,6 +37,7 @@ #include "feature/hs/hs_config.h" #include "feature/hs/hs_ident.h" #include "feature/hs/hs_cache.h" +#include "feature/rend/rendcache.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" #include "core/mainloop/connection.h" @@ -1007,6 +1008,92 @@ test_close_intro_circuits_new_desc(void *arg) UNMOCK(networkstatus_get_live_consensus); } +static void +test_close_intro_circuits_cache_clean(void *arg) +{ + int ret; + ed25519_keypair_t service_kp; + circuit_t *circ = NULL; + origin_circuit_t *ocirc = NULL; + hs_descriptor_t *desc1 = NULL; + + (void) arg; + + hs_init(); + rend_cache_init(); + + /* This is needed because of the client cache expiration timestamp is based + * on having a consensus. See cached_client_descriptor_has_expired(). */ + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + /* Set consensus time */ + parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", + &mock_ns.valid_until); + + /* Generate service keypair */ + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0)); + + /* Create and add to the global list a dummy client introduction circuits. + * We'll then make sure the hs_ident is attached to a dummy descriptor. */ + circ = dummy_origin_circuit_new(0); + tt_assert(circ); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; + ocirc = TO_ORIGIN_CIRCUIT(circ); + + /* Build the first descriptor and cache it. */ + { + char *encoded; + desc1 = hs_helper_build_hs_desc_with_ip(&service_kp); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded); + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + /* Store it */ + ret = hs_cache_store_as_client(encoded, &service_kp.pubkey); + tt_int_op(ret, OP_EQ, 0); + tor_free(encoded); + tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey)); + } + + /* We'll pick one introduction point and associate it with the circuit. */ + { + const hs_desc_intro_point_t *ip = + smartlist_get(desc1->encrypted_data.intro_points, 0); + tt_assert(ip); + ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey, + HS_IDENT_CIRCUIT_INTRO); + ed25519_pubkey_copy(ô->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + } + + /* Before we are about to clean up the intro circuits, make sure it is + * actually there. */ + tt_assert(circuit_get_next_intro_circ(NULL, true)); + + /* Cleanup the client cache. The ns valid after time is what decides if the + * descriptor has expired so put it in the future enough (72h) so we are + * sure to always expire. */ + mock_ns.valid_after = approx_time() + (72 * 24 * 60 * 60); + hs_cache_clean_as_client(0); + + /* Once stored, our intro circuit should be closed because it is related to + * an old introduction point that doesn't exists anymore. */ + tt_assert(!circuit_get_next_intro_circ(NULL, true)); + + done: + circuit_free(circ); + hs_descriptor_free(desc1); + hs_free_all(); + rend_cache_free_all(); + UNMOCK(networkstatus_get_live_consensus); +} + struct testcase_t hs_client_tests[] = { { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy, TT_FORK, NULL, NULL }, @@ -1026,6 +1113,8 @@ struct testcase_t hs_client_tests[] = { TT_FORK, NULL, NULL }, { "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc, TT_FORK, NULL, NULL }, + { "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index abded6021e..de3f7e04f7 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -502,6 +502,7 @@ test_desc_reupload_logic(void *arg) pubkey_hex, strlen(pubkey_hex)); hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr); service = tor_malloc_zero(sizeof(hs_service_t)); + tt_assert(service); memcpy(service->onion_address, onion_addr, sizeof(service->onion_address)); ed25519_secret_key_generate(&service->keys.identity_sk, 0); ed25519_public_key_generate(&service->keys.identity_pk, diff --git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c new file mode 100644 index 0000000000..3dfa057a4a --- /dev/null +++ b/src/test/test_hs_dos.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2017-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_cell.c + * \brief Test hidden service cell functionality. + */ + +#define CIRCUITLIST_PRIVATE +#define NETWORKSTATUS_PRIVATE + +#include "test/test.h" +#include "test/test_helpers.h" +#include "test/log_test_helpers.h" + +#include "app/config/config.h" + +#include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/or_circuit_st.h" + +#include "feature/hs/hs_dos.h" +#include "feature/nodelist/networkstatus.h" + +static void +setup_mock_consensus(void) +{ + current_ns_consensus = tor_malloc_zero(sizeof(networkstatus_t)); + current_ns_consensus->net_params = smartlist_new(); + smartlist_add(current_ns_consensus->net_params, + (void *) "HiddenServiceEnableIntroDoSDefense=1"); + hs_dos_consensus_has_changed(current_ns_consensus); +} + +static void +free_mock_consensus(void) +{ + smartlist_free(current_ns_consensus->net_params); + tor_free(current_ns_consensus); +} + +static void +test_can_send_intro2(void *arg) +{ + uint32_t now = (uint32_t) approx_time(); + or_circuit_t *or_circ = NULL; + + (void) arg; + + hs_init(); + hs_dos_init(); + + get_options_mutable()->ORPort_set = 1; + setup_mock_consensus(); + + or_circ = or_circuit_new(1, NULL); + + /* Make that circuit a service intro point. */ + circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT); + /* Initialize the INTRODUCE2 token bucket for the rate limiting. */ + token_bucket_ctr_init(&or_circ->introduce2_bucket, hs_dos_get_intro2_rate(), + hs_dos_get_intro2_burst(), now); + + /* Brand new circuit, we should be able to send INTRODUCE2 cells. */ + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + + /* Simulate that 10 cells have arrived in 1 second. There should be no + * refill since the bucket is already at maximum on the first cell. */ + update_approx_time(++now); + for (int i = 0; i < 10; i++) { + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + hs_dos_get_intro2_burst() - 10); + + /* Fully refill the bucket minus 1 cell. */ + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + hs_dos_get_intro2_burst() - 1); + + /* Receive an INTRODUCE2 at each second. We should have the bucket full + * since at every second it gets refilled. */ + for (int i = 0; i < 10; i++) { + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + /* Last check if we can send the cell decrements the bucket so minus 1. */ + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + hs_dos_get_intro2_burst() - 1); + + /* Manually reset bucket for next test. */ + token_bucket_ctr_reset(&or_circ->introduce2_bucket, now); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + hs_dos_get_intro2_burst()); + + /* Do a full burst in the current second which should empty the bucket and + * we shouldn't be allowed to send one more cell after that. We go minus 1 + * cell else the very last check if we can send the INTRO2 cell returns + * false because the bucket goes down to 0. */ + for (uint32_t i = 0; i < hs_dos_get_intro2_burst() - 1; i++) { + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 1); + /* Get the last remaining cell, we shouldn't be allowed to send it. */ + tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0); + + /* Make sure the next 100 cells aren't allowed and bucket stays at 0. */ + for (int i = 0; i < 100; i++) { + tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0); + } + + /* One second has passed, we should have the rate minus 1 cell added. */ + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + hs_dos_get_intro2_rate() - 1); + + done: + circuit_free_(TO_CIRCUIT(or_circ)); + + hs_free_all(); + free_mock_consensus(); +} + +struct testcase_t hs_dos_tests[] = { + { "can_send_intro2", test_can_send_intro2, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 732836fb5b..7b01809f96 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -16,6 +16,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "core/or/or.h" +#include "core/or/channel.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "ht.h" @@ -25,6 +26,7 @@ #include "feature/hs/hs_cell.h" #include "feature/hs/hs_circuitmap.h" #include "feature/hs/hs_common.h" +#include "feature/hs/hs_dos.h" #include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_service.h" @@ -118,6 +120,8 @@ helper_create_intro_circuit(void) or_circuit_t *circ = or_circuit_new(0, NULL); tt_assert(circ); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100, + (uint32_t) approx_time()); done: return circ; } @@ -693,6 +697,17 @@ test_introduce1_suitable_circuit(void *arg) tt_int_op(ret, OP_EQ, 0); } + /* Single hop circuit should not be allowed. */ + { + circ = or_circuit_new(0, NULL); + circ->p_chan = tor_malloc_zero(sizeof(channel_t)); + circ->p_chan->is_client = 1; + ret = circuit_is_suitable_for_introduce1(circ); + tor_free(circ->p_chan); + circuit_free_(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + done: ; } @@ -888,43 +903,63 @@ test_received_introduce1_handling(void *arg) UNMOCK(relay_send_command_from_edge_); } +static void * +hs_subsystem_setup_fn(const struct testcase_t *tc) +{ + (void) tc; + + return NULL; +} + +static int +hs_subsystem_cleanup_fn(const struct testcase_t *tc, void *arg) +{ + (void) tc; + (void) arg; + + return 1; +} + +static struct testcase_setup_t test_setup = { + hs_subsystem_setup_fn, hs_subsystem_cleanup_fn +}; + struct testcase_t hs_intropoint_tests[] = { { "intro_point_registration", - test_intro_point_registration, TT_FORK, NULL, NULL }, + test_intro_point_registration, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_keytype", - test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_keytype, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_keytype2", - test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_keytype2, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_purpose", - test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_purpose, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_sig", - test_establish_intro_wrong_sig, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_sig, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_sig_len", - test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_sig_len, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_auth_key_len", - test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_mac", - test_establish_intro_wrong_mac, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_mac, TT_FORK, NULL, &test_setup}, { "introduce1_suitable_circuit", - test_introduce1_suitable_circuit, TT_FORK, NULL, NULL }, + test_introduce1_suitable_circuit, TT_FORK, NULL, &test_setup}, { "introduce1_is_legacy", - test_introduce1_is_legacy, TT_FORK, NULL, NULL }, + test_introduce1_is_legacy, TT_FORK, NULL, &test_setup}, { "introduce1_validation", - test_introduce1_validation, TT_FORK, NULL, NULL }, + test_introduce1_validation, TT_FORK, NULL, &test_setup}, { "received_introduce1_handling", - test_received_introduce1_handling, TT_FORK, NULL, NULL }, + test_received_introduce1_handling, TT_FORK, NULL, &test_setup}, END_OF_TESTCASES }; - diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index a303f10411..2e4be4e295 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -1265,6 +1265,7 @@ test_service_event(void *arg) /* Set a service for this circuit. */ service = helper_create_service(); + tt_assert(service); ed25519_pubkey_copy(&circ->hs_ident->identity_pk, &service->keys.identity_pk); diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index 4a6d90d97e..104e973b1f 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -383,8 +383,10 @@ make_intro_from_plaintext( /* Output the cell */ *cell_out = cell; + cell = NULL; done: + tor_free(cell); return cell_len; } @@ -535,4 +537,3 @@ struct testcase_t introduce_tests[] = { INTRODUCE_LEGACY(late_parse_v3), END_OF_TESTCASES }; - diff --git a/src/test/test_options.c b/src/test/test_options.c index 7009910b0f..64fcd011e7 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -258,13 +258,17 @@ test_options_validate(void *arg) WANT_ERR("BridgeRelay 1\nDirCache 0", "We're a bridge but DirCache is disabled.", PH_VALIDATE); + // XXXX We should replace this with a more full error message once #29211 + // XXXX is done. It is truncated for now because at the current stage + // XXXX of refactoring, we can't give a full error message like before. WANT_ERR_LOG("HeartbeatPeriod 21 snarks", - "Interval 'HeartbeatPeriod 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); + // XXXX As above. WANT_ERR_LOG("LogTimeGranularity 21 snarks", - "Msec interval 'LogTimeGranularity 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); OK("HeartbeatPeriod 1 hour", PH_VALIDATE); OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); @@ -1343,29 +1347,6 @@ test_options_validate__token_bucket(void *ignored) } static void -test_options_validate__recommended_packages(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_WARN); - options_test_data_t *tdata = get_options_test_data( - "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n" - "RecommendedPackages invalid-package-line\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_no_log_msg("Invalid RecommendedPackage line " - "invalid-package-line will be ignored\n"); - - done: - escaped(NULL); // This will free the leaking memory from the previous escaped - teardown_capture_of_logs(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__fetch_dir(void *ignored) { (void)ignored; @@ -4200,7 +4181,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(exclude_nodes), LOCAL_VALIDATE_TEST(node_families), LOCAL_VALIDATE_TEST(token_bucket), - LOCAL_VALIDATE_TEST(recommended_packages), LOCAL_VALIDATE_TEST(fetch_dir), LOCAL_VALIDATE_TEST(conn_limit), LOCAL_VALIDATE_TEST(paths_needed), diff --git a/src/test/test_pubsub_build.c b/src/test/test_pubsub_build.c index ce5bf60080..021323fbf1 100644 --- a/src/test/test_pubsub_build.c +++ b/src/test/test_pubsub_build.c @@ -493,48 +493,6 @@ test_pubsub_build_sub_many(void *arg) tor_free(sysname); } -/* The same subsystem can only declare one publish or subscribe. */ -static void -test_pubsub_build_pubsub_redundant(void *arg) -{ - (void)arg; - pubsub_builder_t *b = NULL; - dispatch_t *dispatcher = NULL; - pubsub_connector_t *c = NULL; - - b = pubsub_builder_new(); - seed_pubsub_builder_basic(b); - pub_binding_t btmp; - - { - c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2")); - DISPATCH_ADD_SUB(c, main, bunch_of_coconuts); - pubsub_add_pub_(c, &btmp, get_channel_id("main"), - get_message_id("yes_we_have_no"), - get_msg_type_id("string"), - 0 /* flags */, - "somewhere.c", 22); - pubsub_connector_free(c); - }; - - setup_full_capture_of_logs(LOG_WARN); - dispatcher = pubsub_builder_finalize(b, NULL); - b = NULL; - tt_assert(dispatcher == NULL); - - expect_log_msg_containing( - "Message \"yes_we_have_no\" is configured to be published by " - "subsystem \"sys2\" more than once."); - expect_log_msg_containing( - "Message \"bunch_of_coconuts\" is configured to be subscribed by " - "subsystem \"sys2\" more than once."); - - done: - pubsub_builder_free(b); - dispatch_free(dispatcher); - teardown_capture_of_logs(); -} - /* It's fine to declare the excl flag. */ static void test_pubsub_build_excl_ok(void *arg) @@ -614,7 +572,6 @@ struct testcase_t pubsub_build_tests[] = { T(pubsub_same, TT_FORK), T(pubsub_multi, TT_FORK), T(sub_many, TT_FORK), - T(pubsub_redundant, TT_FORK), T(excl_ok, TT_FORK), T(excl_bad, TT_FORK), END_OF_TESTCASES diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh index e0d8394d38..d6d9d86668 100755 --- a/src/test/test_rebind.sh +++ b/src/test/test_rebind.sh @@ -12,8 +12,6 @@ if test "$UNAME_OS" = 'CYGWIN' || \ fi fi -exitcode=0 - tmpdir= clean () { if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then diff --git a/src/test/test_token_bucket.c b/src/test/test_token_bucket.c new file mode 100644 index 0000000000..31670718d9 --- /dev/null +++ b/src/test/test_token_bucket.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_bwmgt.c + * \brief tests for bandwidth management / token bucket functions + */ + +#define TOKEN_BUCKET_PRIVATE + +#include "core/or/or.h" +#include "test/test.h" + +#include "lib/evloop/token_bucket.h" + +// an imaginary time, in timestamp units. Chosen so it will roll over. +static const uint32_t START_TS = UINT32_MAX - 1000; +static const uint32_t RATE = 10; +static const uint32_t BURST = 50; + +static void +test_token_bucket_ctr_init(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + tt_uint_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_adjust(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Increase burst. */ + token_bucket_ctr_adjust(&tb, RATE, BURST * 2); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST * 2); + + /* Decrease burst but still above bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST + 10); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST + 10); + + /* Decrease burst below bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST - 1); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST - 1); + + /* Change rate. */ + token_bucket_ctr_adjust(&tb, RATE * 2, BURST); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE * 2); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_dec(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Simple decrement by one. */ + tt_uint_op(0, OP_EQ, token_bucket_ctr_dec(&tb, 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + + /* Down to 0. Becomes empty. */ + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST - 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, 0); + + /* Reset and try to underflow. */ + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST + 1)); + tt_int_op(tb.counter.bucket, OP_EQ, -1); + + /* Keep underflowing shouldn't flag the bucket as empty. */ + tt_uint_op(false, OP_EQ, token_bucket_ctr_dec(&tb, BURST)); + tt_int_op(tb.counter.bucket, OP_EQ, - (int32_t) (BURST + 1)); + + done: + ; +} + +static void +test_token_bucket_ctr_refill(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Reduce of half the bucket and let a single second go before refill. */ + token_bucket_ctr_dec(&tb, BURST / 2); + tt_int_op(tb.counter.bucket, OP_EQ, BURST / 2); + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* No time change, nothing should move. */ + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* Add 99 seconds, bucket should be back to a full BURST. */ + token_bucket_ctr_refill(&tb, START_TS + 99); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 99); + + /* Empty bucket at once. */ + token_bucket_ctr_dec(&tb, BURST); + tt_int_op(tb.counter.bucket, OP_EQ, 0); + /* On second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 100); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 100); + tt_int_op(tb.counter.bucket, OP_EQ, RATE); + /* A second second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 101); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 101); + tt_int_op(tb.counter.bucket, OP_EQ, RATE * 2); + + done: + ; +} + +#define TOKEN_BUCKET(name) \ + { #name, test_token_bucket_ ## name , 0, NULL, NULL } + +struct testcase_t token_bucket_tests[] = { + TOKEN_BUCKET(ctr_init), + TOKEN_BUCKET(ctr_adjust), + TOKEN_BUCKET(ctr_dec), + TOKEN_BUCKET(ctr_refill), + END_OF_TESTCASES +}; diff --git a/src/test/test_util.c b/src/test/test_util.c index 2faadd4e19..c56d3488ba 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5399,6 +5399,13 @@ test_util_socketpair(void *arg) tt_skip(); } #endif /* defined(__FreeBSD__) */ +#ifdef ENETUNREACH + if (ersatz && socketpair_result == -ENETUNREACH) { + /* We can also fail with -ENETUNREACH if we have no network stack at + * all. */ + tt_skip(); + } +#endif tt_int_op(0, OP_EQ, socketpair_result); tt_assert(SOCKET_OK(fds[0])); diff --git a/src/tools/tor-print-ed-signing-cert.c b/src/tools/tor-print-ed-signing-cert.c index 1f1a01ab5c..43a1d7bcbd 100644 --- a/src/tools/tor-print-ed-signing-cert.c +++ b/src/tools/tor-print-ed-signing-cert.c @@ -10,11 +10,13 @@ #include "lib/cc/torint.h" /* TOR_PRIdSZ */ #include "lib/crypt_ops/crypto_format.h" #include "lib/malloc/malloc.h" +#include "lib/encoding/time_fmt.h" int main(int argc, char **argv) { ed25519_cert_t *cert = NULL; + char rfc1123_buf[RFC1123_TIME_LEN+1] = ""; if (argc != 2) { fprintf(stderr, "Usage:\n"); @@ -59,6 +61,11 @@ main(int argc, char **argv) printf("Expires at: %s", ctime(&expires_at)); + format_rfc1123_time(rfc1123_buf, expires_at); + printf("RFC 1123 timestamp: %s\n", rfc1123_buf); + + printf("UNIX timestamp: %ld\n", (long int)expires_at); + ed25519_cert_free(cert); return 0; diff --git a/src/trunnel/channelpadding_negotiation.c b/src/trunnel/channelpadding_negotiation.c index 59e6b38384..d96496e90c 100644 --- a/src/trunnel/channelpadding_negotiation.c +++ b/src/trunnel/channelpadding_negotiation.c @@ -1,4 +1,4 @@ -/* channelpadding_negotiation.c -- generated by Trunnel v1.5.2. +/* channelpadding_negotiation.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/channelpadding_negotiation.h b/src/trunnel/channelpadding_negotiation.h index fcfc232fea..3f96174f68 100644 --- a/src/trunnel/channelpadding_negotiation.h +++ b/src/trunnel/channelpadding_negotiation.h @@ -1,4 +1,4 @@ -/* channelpadding_negotiation.h -- generated by Trunnel v1.5.2. +/* channelpadding_negotiation.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/circpad_negotiation.c b/src/trunnel/circpad_negotiation.c index 236be06ada..547818f2ec 100644 --- a/src/trunnel/circpad_negotiation.c +++ b/src/trunnel/circpad_negotiation.c @@ -1,4 +1,4 @@ -/* circpad_negotiation.c -- generated by Trunnel v1.5.2. +/* circpad_negotiation.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/circpad_negotiation.h b/src/trunnel/circpad_negotiation.h index d09080dc16..ba9155019e 100644 --- a/src/trunnel/circpad_negotiation.h +++ b/src/trunnel/circpad_negotiation.h @@ -1,4 +1,4 @@ -/* circpad_negotiation.h -- generated by Trunnel v1.5.2. +/* circpad_negotiation.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c index 1276c7a505..86b79ef9b6 100644 --- a/src/trunnel/ed25519_cert.c +++ b/src/trunnel/ed25519_cert.c @@ -1,4 +1,4 @@ -/* ed25519_cert.c -- generated by Trunnel v1.5.2. +/* ed25519_cert.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h index e086c6fced..bd91ce1055 100644 --- a/src/trunnel/ed25519_cert.h +++ b/src/trunnel/ed25519_cert.h @@ -1,4 +1,4 @@ -/* ed25519_cert.h -- generated by Trunnel v1.5.2. +/* ed25519_cert.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_common.c b/src/trunnel/hs/cell_common.c index af223560c1..830af5c78b 100644 --- a/src/trunnel/hs/cell_common.c +++ b/src/trunnel/hs/cell_common.c @@ -1,4 +1,4 @@ -/* cell_common.c -- generated by Trunnel v1.5.2. +/* cell_common.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_common.h b/src/trunnel/hs/cell_common.h index e08eedfdb3..c84d17d8e5 100644 --- a/src/trunnel/hs/cell_common.h +++ b/src/trunnel/hs/cell_common.h @@ -1,4 +1,4 @@ -/* cell_common.h -- generated by Trunnel v1.5.2. +/* cell_common.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_establish_intro.c b/src/trunnel/hs/cell_establish_intro.c index ae3b7b1bc8..99ceadbda4 100644 --- a/src/trunnel/hs/cell_establish_intro.c +++ b/src/trunnel/hs/cell_establish_intro.c @@ -1,4 +1,4 @@ -/* cell_establish_intro.c -- generated by Trunnel v1.5.2. +/* cell_establish_intro.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_establish_intro.h b/src/trunnel/hs/cell_establish_intro.h index ccaef5488c..1908645aa6 100644 --- a/src/trunnel/hs/cell_establish_intro.h +++ b/src/trunnel/hs/cell_establish_intro.h @@ -1,4 +1,4 @@ -/* cell_establish_intro.h -- generated by Trunnel v1.5.2. +/* cell_establish_intro.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_introduce1.c b/src/trunnel/hs/cell_introduce1.c index 53b3d299f2..016c9fa8d6 100644 --- a/src/trunnel/hs/cell_introduce1.c +++ b/src/trunnel/hs/cell_introduce1.c @@ -1,4 +1,4 @@ -/* cell_introduce1.c -- generated by Trunnel v1.5.2. +/* cell_introduce1.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_introduce1.h b/src/trunnel/hs/cell_introduce1.h index 986a531ca7..8dabff3cb5 100644 --- a/src/trunnel/hs/cell_introduce1.h +++ b/src/trunnel/hs/cell_introduce1.h @@ -1,4 +1,4 @@ -/* cell_introduce1.h -- generated by Trunnel v1.5.2. +/* cell_introduce1.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_rendezvous.c b/src/trunnel/hs/cell_rendezvous.c index 53cb609138..1204e93cfc 100644 --- a/src/trunnel/hs/cell_rendezvous.c +++ b/src/trunnel/hs/cell_rendezvous.c @@ -1,4 +1,4 @@ -/* cell_rendezvous.c -- generated by Trunnel v1.5.2. +/* cell_rendezvous.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_rendezvous.h b/src/trunnel/hs/cell_rendezvous.h index 39e14da25b..5a8c2ff52a 100644 --- a/src/trunnel/hs/cell_rendezvous.h +++ b/src/trunnel/hs/cell_rendezvous.h @@ -1,4 +1,4 @@ -/* cell_rendezvous.h -- generated by Trunnel v1.5.2. +/* cell_rendezvous.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/include.am b/src/trunnel/include.am index ce15570b15..6c3a5ff06b 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -11,7 +11,7 @@ TRUNNELINPUTS = \ src/trunnel/link_handshake.trunnel \ src/trunnel/pwbox.trunnel \ src/trunnel/channelpadding_negotiation.trunnel \ - src/trunnel/sendme.trunnel \ + src/trunnel/sendme_cell.trunnel \ src/trunnel/socks5.trunnel \ src/trunnel/circpad_negotiation.trunnel @@ -25,7 +25,7 @@ TRUNNELSOURCES = \ src/trunnel/hs/cell_introduce1.c \ src/trunnel/hs/cell_rendezvous.c \ src/trunnel/channelpadding_negotiation.c \ - src/trunnel/sendme.c \ + src/trunnel/sendme_cell.c \ src/trunnel/socks5.c \ src/trunnel/netinfo.c \ src/trunnel/circpad_negotiation.c @@ -42,7 +42,7 @@ TRUNNELHEADERS = \ src/trunnel/hs/cell_introduce1.h \ src/trunnel/hs/cell_rendezvous.h \ src/trunnel/channelpadding_negotiation.h \ - src/trunnel/sendme.h \ + src/trunnel/sendme_cell.h \ src/trunnel/socks5.h \ src/trunnel/netinfo.h \ src/trunnel/circpad_negotiation.h diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c index 03ead31c62..76db4b0e29 100644 --- a/src/trunnel/link_handshake.c +++ b/src/trunnel/link_handshake.c @@ -1,4 +1,4 @@ -/* link_handshake.c -- generated by Trunnel v1.5.2. +/* link_handshake.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h index 6a23483adc..0c7ac36b1b 100644 --- a/src/trunnel/link_handshake.h +++ b/src/trunnel/link_handshake.h @@ -1,4 +1,4 @@ -/* link_handshake.h -- generated by Trunnel v1.5.2. +/* link_handshake.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/netinfo.c b/src/trunnel/netinfo.c index 5d815b9b12..d7d0cddc89 100644 --- a/src/trunnel/netinfo.c +++ b/src/trunnel/netinfo.c @@ -1,4 +1,4 @@ -/* netinfo.c -- generated by Trunnel v1.5.2. +/* netinfo.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/netinfo.h b/src/trunnel/netinfo.h index ac46e603ba..37c2ae3c2d 100644 --- a/src/trunnel/netinfo.h +++ b/src/trunnel/netinfo.h @@ -1,4 +1,4 @@ -/* netinfo.h -- generated by Trunnel v1.5.2. +/* netinfo.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c index c356515d36..c159a5e687 100644 --- a/src/trunnel/pwbox.c +++ b/src/trunnel/pwbox.c @@ -1,4 +1,4 @@ -/* pwbox.c -- generated by Trunnel v1.5.2. +/* pwbox.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h index a9a421408a..36d595f4ef 100644 --- a/src/trunnel/pwbox.h +++ b/src/trunnel/pwbox.h @@ -1,4 +1,4 @@ -/* pwbox.h -- generated by Trunnel v1.5.2. +/* pwbox.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/sendme.c b/src/trunnel/sendme_cell.c index 262b915234..b9f8fe967f 100644 --- a/src/trunnel/sendme.c +++ b/src/trunnel/sendme_cell.c @@ -1,11 +1,11 @@ -/* sendme.c -- generated by Trunnel v1.5.2. +/* sendme_cell.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ #include <stdlib.h> #include "trunnel-impl.h" -#include "sendme.h" +#include "sendme_cell.h" #define TRUNNEL_SET_ERROR_CODE(obj) \ do { \ @@ -15,8 +15,8 @@ #if defined(__COVERITY__) || defined(__clang_analyzer__) /* If we're running a static analysis tool, we don't want it to complain * that some of our remaining-bytes checks are dead-code. */ -int sendme_deadcode_dummy__ = 0; -#define OR_DEADCODE_DUMMY || sendme_deadcode_dummy__ +int sendmecell_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || sendmecell_deadcode_dummy__ #else #define OR_DEADCODE_DUMMY #endif diff --git a/src/trunnel/sendme.h b/src/trunnel/sendme_cell.h index f3c3dd78c4..45efb9f10d 100644 --- a/src/trunnel/sendme.h +++ b/src/trunnel/sendme_cell.h @@ -1,9 +1,9 @@ -/* sendme.h -- generated by Trunnel v1.5.2. +/* sendme_cell.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ -#ifndef TRUNNEL_SENDME_H -#define TRUNNEL_SENDME_H +#ifndef TRUNNEL_SENDME_CELL_H +#define TRUNNEL_SENDME_CELL_H #include <stdint.h> #include "trunnel.h" diff --git a/src/trunnel/sendme.trunnel b/src/trunnel/sendme_cell.trunnel index 300963e679..300963e679 100644 --- a/src/trunnel/sendme.trunnel +++ b/src/trunnel/sendme_cell.trunnel diff --git a/src/trunnel/socks5.c b/src/trunnel/socks5.c index 057a52b042..f32862e353 100644 --- a/src/trunnel/socks5.c +++ b/src/trunnel/socks5.c @@ -1,4 +1,4 @@ -/* socks5.c -- generated by Trunnel v1.5.2. +/* socks5.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/socks5.h b/src/trunnel/socks5.h index d3bea152e7..23ac64faba 100644 --- a/src/trunnel/socks5.h +++ b/src/trunnel/socks5.h @@ -1,4 +1,4 @@ -/* socks5.h -- generated by Trunnel v1.5.2. +/* socks5.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index bb1f42ef19..25bfb9ada5 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.4.1.5-dev" +#define VERSION "0.4.2.0-alpha-dev" |