diff options
434 files changed, 16859 insertions, 8260 deletions
diff --git a/.gitignore b/.gitignore index 4dc8867a89..c00fbe97e1 100644 --- a/.gitignore +++ b/.gitignore @@ -212,6 +212,7 @@ uptime-*.json /src/test/fuzz/lf-fuzz-* # /src/tools/ +/src/tools/libtorrunner.a /src/tools/tor-checkkey /src/tools/tor-resolve /src/tools/tor-cov-resolve diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ba981d5f14..d2d0d55dd4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,7 +26,7 @@ update: - ssh-add <(echo "$DEPLOY_KEY") # For Docker builds disable host key checking. Be aware that by adding that - # you are suspectible to man-in-the-middle attacks. + # you are susceptible to man-in-the-middle attacks. # WARNING: Use this only with the Docker executor, if you use it with shell # you will overwrite your user's SSH config. - mkdir -p ~/.ssh diff --git a/.travis.yml b/.travis.yml index 19bbe6df01..6c6e1ace2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,6 +66,7 @@ env: ## more than one entry causes unwanted matrix entries with ## unspecified compilers. - RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" + # - RUST_OPTIONS="--enable-rust" TOR_RUST_DEPENDENCIES=true # - RUST_OPTIONS="" matrix: @@ -80,6 +81,8 @@ matrix: include: - compiler: gcc - compiler: gcc + env: RUST_OPTIONS="--enable-rust" TOR_RUST_DEPENDENCIES=true + - compiler: gcc env: RUST_OPTIONS="" ## The "sudo: required" forces non-containerized builds, working ## around a Travis CI environment issue: clang LeakAnalyzer fails @@ -89,6 +92,9 @@ matrix: sudo: required - compiler: clang sudo: required + env: RUST_OPTIONS="--enable-rust" TOR_RUST_DEPENDENCIES=true + - compiler: clang + sudo: required env: RUST_OPTIONS="" before_install: @@ -114,6 +120,10 @@ install: - if [[ "$RUST_OPTIONS" != "" ]]; then which cargo; fi - if [[ "$RUST_OPTIONS" != "" ]]; then rustc --version; fi - if [[ "$RUST_OPTIONS" != "" ]]; then cargo --version; fi + ## If we're testing rust builds in offline-mode, then set up our vendored dependencies + - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule init ; fi + - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule update; fi + - if [[ "$TOR_RUST_DEPENDENCIES" == "true" ]]; then export TOR_RUST_DEPENDENCIES=$PWD/src/ext/rust/crates; fi script: - ./autogen.sh @@ -1,3 +1,1733 @@ +Changes in version 0.3.3.2-alpha - 2018-02-10 + Tor 0.3.3.2-alpha is the second alpha in the 0.3.3.x series. It + introduces a mechanism to handle the high loads that many relay + operators have been reporting recently. It also fixes several bugs in + older releases. If this new code proves reliable, we plan to backport + it to older supported release series. + + o Major features (denial-of-service mitigation): + - Give relays some defenses against the recent network overload. We + start with three defenses (default parameters in parentheses). + First: if a single client address makes too many concurrent + connections (>100), hang up on further connections. Second: if a + single client address makes circuits too quickly (more than 3 per + second, with an allowed burst of 90) while also having too many + connections open (3), refuse new create cells for the next while + (1-2 hours). Third: if a client asks to establish a rendezvous + point to you directly, ignore the request. These defenses can be + manually controlled by new torrc options, but relays will also + take guidance from consensus parameters, so there's no need to + configure anything manually. Implements ticket 24902. + + o Major bugfixes (netflow padding): + - Stop adding unneeded channel padding right after we finish + flushing to a connection that has been trying to flush for many + seconds. Instead, treat all partial or complete flushes as + activity on the channel, which will defer the time until we need + to add padding. This fix should resolve confusing and scary log + messages like "Channel padding timeout scheduled 221453ms in the + past." Fixes bug 22212; bugfix on 0.3.1.1-alpha. + + o Major bugfixes (protocol versions): + - Add Link protocol version 5 to the supported protocols list. Fixes + bug 25070; bugfix on 0.3.1.1-alpha. + + o Major bugfixes (scheduler, consensus): + - The scheduler subsystem was failing to promptly notice changes in + consensus parameters, making it harder to switch schedulers + network-wide. Fixes bug 24975; bugfix on 0.3.2.1-alpha. + + o Minor features (denial-of-service avoidance): + - Make our OOM handler aware of the geoip client history cache so it + doesn't fill up the memory. This check is important for IPv6 and + our DoS mitigation subsystem. Closes ticket 25122. + + o Minor features (directory authority): + - When directory authorities are unable to add signatures to a + pending consensus, log the reason why. Closes ticket 24849. + + o Minor features (geoip): + - Update geoip and geoip6 to the February 7 2018 Maxmind GeoLite2 + Country database. + + o Minor features (logging, diagnostic): + - When logging a failure to create an onion service's descriptor, + also log what the problem with the descriptor was. Diagnostic for + ticket 24972. + + o Minor bugfix (channel connection): + - Use the actual observed address of an incoming relay connection, + not the canonical address of the relay from its descriptor, when + making decisions about how to handle the incoming connection. + Fixes bug 24952; bugfix on 0.2.4.11-alpha. Patch by "ffmancera". + + o Minor bugfix (directory authority): + - Directory authorities, when refusing a descriptor from a rejected + relay, now explicitly tell the relay (in its logs) to set a valid + ContactInfo address and contact the bad-relays@ mailing list. + Fixes bug 25170; bugfix on 0.2.9.1. + + o Minor bugfixes (all versions of Tor): + - Use the "misspell" tool to detect and fix typos throughout the + source code. Fixes bug 23650; bugfix on various versions of Tor. + Patch from Deepesh Pathak. + + o Minor bugfixes (circuit, cannibalization): + - Don't cannibalize preemptively-built circuits if we no longer + recognize their first hop. This situation can happen if our Guard + relay went off the consensus after the circuit was created. Fixes + bug 24469; bugfix on 0.0.6. + + o Minor bugfixes (correctness): + - Remove a nonworking, unnecessary check to see whether a circuit + hop's identity digest was set when the circuit failed. Fixes bug + 24927; bugfix on 0.2.4.4-alpha. + + o Minor bugfixes (logging): + - Don't treat inability to store a cached consensus object as a bug: + it can happen normally when we are out of disk space. Fixes bug + 24859; bugfix on 0.3.1.1-alpha. + - Fix a (mostly harmless) race condition when invoking + LOG_PROTOCOL_WARN message from a subthread while the torrc options + are changing. Fixes bug 23954; bugfix on 0.1.1.9-alpha. + + o Minor bugfixes (onion services): + - Remove a BUG() statement when a client fetches an onion descriptor + that has a lower revision counter than the one in its cache. This + can happen in normal circumstances due to HSDir desync. Fixes bug + 24976; bugfix on 0.3.2.1-alpha. + - If we are configured to offer a single onion service, don't log + long-term established one hop rendezvous points in the heartbeat. + Fixes bug 25116; bugfix on 0.2.9.6-rc. + + o Minor bugfixes (performance): + - Avoid calling protocol_list_supports_protocol() from inside tight + loops when running with cached routerinfo_t objects. Instead, + summarize the relevant protocols as flags in the routerinfo_t, as + we do for routerstatus_t objects. This change simplifies our code + a little, and saves a large amount of short-term memory allocation + operations. Fixes bug 25008; bugfix on 0.2.9.4-alpha. + + o Minor bugfixes (Rust FFI): + - Fix a minor memory leak which would happen whenever the C code + would call the Rust implementation of + protover_get_supported_protocols(). This was due to the C version + returning a static string, whereas the Rust version newly allocated + a CString to pass accross the FFI boundary. Consequently, the C + code was not expecting to need to free() what it was given. Fixes + bug 25127; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (scheduler, KIST): + - Avoid adding the same channel twice in the KIST scheduler pending + list, which would waste CPU cycles. Fixes bug 24700; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (unit test, monotonic time): + - Increase a constant (1msec to 10msec) in the monotonic time test + that makes sure the nsec/usec/msec times read are synchronized. + This change was needed to accommodate slow systems like armel or + when the clock_gettime() is not a VDSO on the running kernel. + Fixes bug 25113; bugfix on 0.2.9.1. + + o Minor bugfixes (v3 onion services): + - Look at the "HSRend" protocol version, not the "HSDir" protocol + version, when deciding whether a consensus entry can support the + v3 onion service protocol as a rendezvous point. Fixes bug 25105; + bugfix on 0.3.2.1-alpha. + + o Code simplification and refactoring: + - Remove the unused nodelist_recompute_all_hsdir_indices(). Closes + ticket 25108. + - Remove a series of counters used to track circuit extend attempts + and connection status but that in reality we aren't using for + anything other than stats logged by a SIGUSR1 signal. Closes + ticket 25163. + + o Documentation (man page): + - The HiddenServiceVersion torrc option accepts only one number: + either version 2 or 3. Closes ticket 25026; bugfix + on 0.3.2.2-alpha. + + +Changes in version 0.3.3.1-alpha - 2018-01-25 + Tor 0.3.3.1-alpha is the first release in the 0.3.3.x series. It adds + several new features to Tor, including several improvements to + bootstrapping, and support for an experimental "vanguards" feature to + resist guard discovery attacks. This series also includes better + support for applications that need to embed Tor or manage v3 + onion services. + + o Major features (embedding): + - There is now a documented stable API for programs that need to + embed Tor. See tor_api.h for full documentation and known bugs. + Closes ticket 23684. + - Tor now has support for restarting in the same process. + Controllers that run Tor using the "tor_api.h" interface can now + restart Tor after Tor has exited. This support is incomplete, + however: we fixed crash bugs that prevented it from working at + all, but many bugs probably remain, including a possibility of + security issues. Implements ticket 24581. + + o Major features (IPv6, directory documents): + - Add consensus method 27, which adds IPv6 ORPorts to the microdesc + consensus. This information makes it easier for IPv6 clients to + bootstrap and choose reachable entry guards. Implements 23826. + - Add consensus method 28, which removes IPv6 ORPorts from + microdescriptors. Now that the consensus contains IPv6 ORPorts, + they are redundant in microdescs. This change will be used by Tor + clients on 0.2.8.x and later. (That is to say, with all Tor + clients having IPv6 bootstrap and guard support.) Implements 23828. + - Expand the documentation for AuthDirHasIPv6Connectivity when it is + set by different numbers of authorities. Fixes 23870 + on 0.2.4.1-alpha. + + o Major features (onion service v3, control port): + - The control port now supports commands and events for v3 onion + services. It is now possible to create ephemeral v3 services using + ADD_ONION. Additionally, several events (HS_DESC, HS_DESC_CONTENT, + CIRC and CIRC_MINOR) and commands (GETINFO, HSPOST, ADD_ONION and + DEL_ONION) have been extended to support v3 onion services. Closes + ticket 20699; implements proposal 284. + + o Major features (onion services): + - Provide torrc options to pin the second and third hops of onion + service circuits to a list of nodes. The option HSLayer2Guards + pins the second hop, and the option HSLayer3Guards pins the third + hop. These options are for use in conjunction with experiments + with "vanguards" for preventing guard enumeration attacks. Closes + ticket 13837. + + o Major features (rust, portability, experimental): + - Tor now ships with an optional implementation of one of its + smaller modules (protover.c) in the Rust programming language. To + try it out, install a Rust build environment, and configure Tor + with "--enable-rust --enable-cargo-online-mode". This should not + cause any user-visible changes, but should help us gain more + experience with Rust, and plan future Rust integration work. + Implementation by Chelsea Komlo. Closes ticket 22840. + + o Major features (storage, configuration): + - Users can store cached directory documents somewhere other than + the DataDirectory by using the CacheDirectory option. Similarly, + the storage location for relay's keys can be overridden with the + KeyDirectory option. Closes ticket 22703. + + o Major features (v3 onion services, ipv6): + - When v3 onion service clients send introduce cells, they now + include the IPv6 address of the rendezvous point, if it has one. + Current v3 onion services running 0.3.2 ignore IPv6 addresses, but + in future Tor versions, IPv6-only v3 single onion services will be + able to use IPv6 addresses to connect directly to the rendezvous + point. Closes ticket 23577. Patch by Neel Chauhan. + + o Major bugfixes (onion services, retry behavior): + - Fix an "off by 2" error in counting rendezvous failures on the + onion service side. While we thought we would stop the rendezvous + attempt after one failed circuit, we were actually making three + circuit attempts before giving up. Now switch to a default of 2, + and allow the consensus parameter "hs_service_max_rdv_failures" to + override. Fixes bug 24895; bugfix on 0.0.6. + - New-style (v3) onion services now obey the "max rendezvous circuit + attempts" logic. Previously they would make as many rendezvous + circuit attempts as they could fit in the MAX_REND_TIMEOUT second + window before giving up. Fixes bug 24894; bugfix on 0.3.2.1-alpha. + + o Major bugfixes (relays): + - Fix a set of false positives where relays would consider + connections to other relays as being client-only connections (and + thus e.g. deserving different link padding schemes) if those + relays fell out of the consensus briefly. Now we look only at the + initial handshake and whether the connection authenticated as a + relay. Fixes bug 24898; bugfix on 0.3.1.1-alpha. + + o Minor feature (IPv6): + - Make IPv6-only clients wait for microdescs for relays, even if we + were previously using descriptors (or were using them as a bridge) + and have a cached descriptor for them. Implements 23827. + - When a consensus has IPv6 ORPorts, make IPv6-only clients use + them, rather than waiting to download microdescriptors. + Implements 23827. + + o Minor features (cleanup): + - Tor now deletes the CookieAuthFile and ExtORPortCookieAuthFile + when it stops. Closes ticket 23271. + + o Minor features (defensive programming): + - Most of the functions in Tor that free objects have been replaced + with macros that free the objects and set the corresponding + pointers to NULL. This change should help prevent a large class of + dangling pointer bugs. Closes ticket 24337. + - Where possible, the tor_free() macro now only evaluates its input + once. Part of ticket 24337. + - Check that microdesc ed25519 ids are non-zero in + node_get_ed25519_id() before returning them. Implements 24001, + patch by "aruna1234". + + o Minor features (directory authority): + - Make the "Exit" flag assignment only depend on whether the exit + policy allows connections to ports 80 and 443. Previously relays + would get the Exit flag if they allowed connections to one of + these ports and also port 6667. Resolves ticket 23637. + + o Minor features (embedding): + - Tor can now start with a preauthenticated control connection + created by the process that launched it. This feature is meant for + use by programs that want to launch and manage a Tor process + without allowing other programs to manage it as well. For more + information, see the __OwningControllerFD option documented in + control-spec.txt. Closes ticket 23900. + - On most errors that would cause Tor to exit, it now tries to + return from the tor_main() function, rather than calling the + system exit() function. Most users won't notice a difference here, + but it should make a significant for programs that run Tor inside + a separate thread: they should now be able to survive Tor's exit + conditions rather than having Tor shut down the entire process. + Closes ticket 23848. + - Applications that want to embed Tor can now tell Tor not to + register any of its own POSIX signal handlers, using the + __DisableSignalHandlers option. Closes ticket 24588. + + o Minor features (fallback directory list): + - Avoid selecting fallbacks that change their IP addresses too + often. Select more fallbacks by ignoring the Guard flag, and + allowing lower cutoffs for the Running and V2Dir flags. Also allow + a lower bandwidth, and a higher number of fallbacks per operator + (5% of the list). Implements ticket 24785. + - Update the fallback whitelist and blacklist based on opt-ins and + relay changes. Closes tickets 22321, 24678, 22527, 24135, + and 24695. + + o Minor features (fallback directory mirror configuration): + - Add a nickname to each fallback in a C comment. This makes it + easier for operators to find their relays, and allows stem to use + nicknames to identify fallbacks. Implements ticket 24600. + - Add a type and version header to the fallback directory mirror + file. Also add a delimiter to the end of each fallback entry. This + helps external parsers like stem and Relay Search. Implements + ticket 24725. + - Add an extrainfo cache flag for each fallback in a C comment. This + allows stem to use fallbacks to fetch extra-info documents, rather + than using authorities. Implements ticket 22759. + - Add the generateFallbackDirLine.py script for automatically + generating fallback directory mirror lines from relay fingerprints. + No more typos! Add the lookupFallbackDirContact.py script for + automatically looking up operator contact info from relay + fingerprints. Implements ticket 24706, patch by teor and atagar. + - Reject any fallback directory mirror that serves an expired + consensus. Implements ticket 20942, patch by "minik". + - Remove commas and equals signs from external string inputs to the + fallback list. This avoids format confusion attacks. Implements + ticket 24726. + - Remove the "weight=10" line from fallback directory mirror + entries. Ticket 24681 will maintain the current fallback weights + by changing Tor's default fallback weight to 10. Implements + ticket 24679. + - Stop logging excessive information about fallback netblocks. + Implements ticket 24791. + + o Minor features (forward-compatibility): + - If a relay supports some link authentication protocol that we do + not recognize, then include that relay's ed25519 key when telling + other relays to extend to it. Previously, we treated future + versions as if they were too old to support ed25519 link + authentication. Closes ticket 20895. + + o Minor features (heartbeat): + - Add onion service information to our heartbeat logs, displaying + stats about the activity of configured onion services. Closes + ticket 24896. + + o Minor features (instrumentation, development): + - Add the MainloopStats option to allow developers to get + instrumentation information from the main event loop via the + heartbeat messages. We hope to use this to improve Tor's behavior + when it's trying to sleep. Closes ticket 24605. + + o Minor features (log messages): + - Improve a warning message that happens when we fail to re-parse an + old router because of an expired certificate. Closes ticket 20020. + - Make the log more quantitative when we hit MaxMemInQueues + threshold exposing some values. Closes ticket 24501. + + o Minor features (logging, android): + - Added support for the Android logging subsystem. Closes + ticket 24362. + + o Minor features (performance): + - Support predictive circuit building for onion service circuits + with multiple layers of guards. Closes ticket 23101. + - Use stdatomic.h where available, rather than mutexes, to implement + atomic_counter_t. Closes ticket 23953. + + o Minor features (performance, 32-bit): + - Improve performance on 32-bit systems by avoiding 64-bit division + when calculating the timestamp in milliseconds for channel padding + computations. Implements ticket 24613. + - Improve performance on 32-bit systems by avoiding 64-bit division + when timestamping cells and buffer chunks for OOM calculations. + Implements ticket 24374. + + o Minor features (performance, OSX, iOS): + - Use the mach_approximate_time() function (when available) to + implement coarse monotonic time. Having a coarse time function + should avoid a large number of system calls, and improve + performance slightly, especially under load. Closes ticket 24427. + + o Minor features (performance, windows): + - Improve performance on Windows Vista and Windows 7 by adjusting + TCP send window size according to the recommendation from + SIO_IDEAL_SEND_BACKLOG_QUERY. Closes ticket 22798. Patch + from Vort. + + o Minor features (relay): + - Implement an option, ReducedExitPolicy, to allow an Tor exit relay + operator to use a more reasonable ("reduced") exit policy, rather + than the default one. If you want to run an exit node without + thinking too hard about which ports to allow, this one is for you. + Closes ticket 13605. Patch from Neel Chauhan. + + o Minor features (testing, debugging, embedding): + - For development purposes, Tor now has a mode in which it runs for + a few seconds, then stops, and starts again without exiting the + process. This mode is meant to help us debug various issues with + ticket 23847. To use this feature, compile with + --enable-restart-debugging, and set the TOR_DEBUG_RESTART + environment variable. This is expected to crash a lot, and is + really meant for developers only. It will likely be removed in a + future release. Implements ticket 24583. + + o Minor bugfix (network IPv6 test): + - Tor's test scripts now check if "ping -6 ::1" works when the user + runs "make test-network-all". Fixes bug 24677; bugfix on + 0.2.9.3-alpha. Patch by "ffmancera". + + o Minor bugfixes (build, rust): + - Fix output of autoconf checks to display success messages for Rust + dependencies and a suitable rustc compiler version. Fixes bug + 24612; bugfix on 0.3.1.3-alpha. + - When building with Rust on OSX, link against libresolv, to work + around the issue at https://github.com/rust-lang/rust/issues/46797. + Fixes bug 24652; bugfix on 0.3.1.1-alpha. + - Don't pass the --quiet option to cargo: it seems to suppress some + errors, which is not what we want to do when building. Fixes bug + 24518; bugfix on 0.3.1.7. + - Build correctly when building from outside Tor's source tree with + the TOR_RUST_DEPENDENCIES option set. Fixes bug 22768; bugfix + on 0.3.1.7. + + o Minor bugfixes (directory authorities, IPv6): + - When creating a routerstatus (vote) from a routerinfo (descriptor), + set the IPv6 address to the unspecified IPv6 address, and + explicitly initialize the port to zero. Fixes bug 24488; bugfix + on 0.2.4.1-alpha. + + o Minor bugfixes (fallback directory mirrors): + - Make updateFallbackDirs.py search harder for python. (Some OSs + don't put it in /usr/bin.) Fixes bug 24708; bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (hibernation, bandwidth accounting, shutdown): + - When hibernating, close connections normally and allow them to + flush. Fixes bug 23571; bugfix on 0.2.4.7-alpha. Also fixes + bug 7267. + - Do not attempt to launch self-reachability tests when entering + hibernation. Fixes a case of bug 12062; bugfix on 0.0.9pre5. + - Resolve several bugs related to descriptor fetching on bridge + clients with bandwidth accounting enabled. (This combination is + not recommended!) Fixes a case of bug 12062; bugfix + on 0.2.0.3-alpha. + - When hibernating, do not attempt to launch DNS checks. Fixes a + case of bug 12062; bugfix on 0.1.2.2-alpha. + - When hibernating, do not try to upload or download descriptors. + Fixes a case of bug 12062; bugfix on 0.0.9pre5. + + o Minor bugfixes (IPv6, bridges): + - Tor now always sets IPv6 preferences for bridges. Fixes bug 24573; + bugfix on 0.2.8.2-alpha. + - Tor now sets IPv6 address in the routerstatus as well as in the + router descriptors when updating addresses for a bridge. Closes + ticket 24572; bugfix on 0.2.4.5-alpha. Patch by "ffmancera". + + o Minor bugfixes (linux seccomp2 sandbox): + - When running with the sandbox enabled, reload configuration files + correctly even when %include was used. Previously we would crash. + Fixes bug 22605; bugfix on 0.3.1. Patch from Daniel Pinto. + + o Minor bugfixes (memory leaks): + - Avoid possible at-exit memory leaks related to use of Libevent's + event_base_once() function. (This function tends to leak memory if + the event_base is closed before the event fires.) Fixes bug 24584; + bugfix on 0.2.8.1-alpha. + - Fix a harmless memory leak in tor-resolve. Fixes bug 24582; bugfix + on 0.2.1.1-alpha. + + o Minor bugfixes (OSX): + - Don't exit the Tor process if setrlimit() fails to change the file + limit (which can happen sometimes on some versions of OSX). Fixes + bug 21074; bugfix on 0.0.9pre5. + + o Minor bugfixes (performance, fragile-hardening): + - Improve the performance of our consensus-diff application code + when Tor is built with the --enable-fragile-hardening option set. + Fixes bug 24826; bugfix on 0.3.1.1-alpha. + + o Minor bugfixes (performance, timeouts): + - Consider circuits for timeout as soon as they complete a hop. This + is more accurate than applying the timeout in + circuit_expire_building() because that function is only called + once per second, which is now too slow for typical timeouts on the + current network. Fixes bug 23114; bugfix on 0.2.2.2-alpha. + - Use onion service circuits (and other circuits longer than 3 hops) + to calculate a circuit build timeout. Previously, Tor only + calculated its build timeout based on circuits that planned to be + exactly 3 hops long. With this change, we include measurements + from all circuits at the point where they complete their third + hop. Fixes bug 23100; bugfix on 0.2.2.2-alpha. + + o Minor bugfixes (testing): + - Give out Exit flags in bootstrapping networks. Fixes bug 24137; + bugfix on 0.2.3.1-alpha. + - Fix a memory leak in the scheduler/loop_kist unit test. Fixes bug + 25005; bugfix on 0.3.2.7-rc. + + o Code simplification and refactoring: + - Remove /usr/athena from search path in configure.ac. Closes + ticket 24363. + - Remove duplicate code in node_has_curve25519_onion_key() and + node_get_curve25519_onion_key(), and add a check for a zero + microdesc curve25519 onion key. Closes ticket 23966, patch by + "aruna1234" and teor. + - Rewrite channel_rsa_id_group_set_badness to reduce temporary + memory allocations with large numbers of OR connections (e.g. + relays). Closes ticket 24119. + - Separate the function that deletes ephemeral files when Tor + stops gracefully. + - Small changes to Tor's buf_t API to make it suitable for use as a + general-purpose safe string constructor. Closes ticket 22342. + - Switch -Wnormalized=id to -Wnormalized=nfkc in configure.ac to + avoid source code identifier confusion. Closes ticket 24467. + - The tor_git_revision[] constant no longer needs to be redeclared + by everything that links against the rest of Tor. Done as part of + ticket 23845, to simplify our external API. + - We make extend_info_from_node() use node_get_curve25519_onion_key() + introduced in ticket 23577 to access the curve25519 public keys + rather than accessing it directly. Closes ticket 23760. Patch by + Neel Chauhan. + - Add a function to log channels' scheduler state changes to aid + debugging efforts. Closes ticket 24531. + + o Documentation: + - Add documentation on how to build tor with Rust dependencies + without having to be online. Closes ticket 22907; bugfix + on 0.3.0.3-alpha. + - Clarify the behavior of RelayBandwidth{Rate,Burst} with client + traffic. Closes ticket 24318. + - Document that OutboundBindAddress doesn't apply to DNS requests. + Closes ticket 22145. Patch from Aruna Maurya. + - Document that operators who run more than one relay or bridge are + expected to set MyFamily and ContactInfo correctly. Closes + ticket 24526. + + o Code simplification and refactoring (channels): + - Remove the incoming and outgoing channel queues. These were never + used, but still took up a step in our fast path. + - The majority of the channel unit tests have been rewritten and the + code coverage has now been raised to 83.6% for channel.c. Closes + ticket 23709. + - Remove other dead code from the channel subsystem: All together, + this cleanup has removed more than 1500 lines of code overall and + adding very little except for unit test. + + o Code simplification and refactoring (circuit rendezvous): + - Split the client-size rendezvous circuit lookup into two + functions: one that returns only established circuits and another + that returns all kinds of circuits. Closes ticket 23459. + + o Code simplification and refactoring (controller): + - Make most of the variables in networkstatus_getinfo_by_purpose() + const. Implements ticket 24489. + + +Changes in version 0.3.2.9 - 2018-01-09 + Tor 0.3.2.9 is the first stable release in the 0.3.2 series. + + The 0.3.2 series includes our long-anticipated new onion service + design, with numerous security features. (For more information, see + our blog post at https://blog.torproject.org/fall-harvest.) We also + have a new circuit scheduler algorithm for improved performance on + relays everywhere (see https://blog.torproject.org/kist-and-tell), + along with many smaller features and bugfixes. + + Per our stable release policy, we plan to support each stable release + series for at least the next nine months, or for three months after + the first stable release of the next series: whichever is longer. If + you need a release with long-term support, we recommend that you stay + with the 0.2.9 series. + + Below is a list of the changes since 0.3.2.8-rc. For a list of all + changes since 0.3.1, see the ReleaseNotes file. + + o Minor features (fallback directory mirrors): + - The fallback directory list has been re-generated based on the + current status of the network. Tor uses fallback directories to + bootstrap when it doesn't yet have up-to-date directory + information. Closes ticket 24801. + - Make the default DirAuthorityFallbackRate 0.1, so that clients + prefer to bootstrap from fallback directory mirrors. This is a + follow-up to 24679, which removed weights from the default + fallbacks. Implements ticket 24681. + + o Minor features (geoip): + - Update geoip and geoip6 to the January 5 2018 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (address selection): + - When the fascist_firewall_choose_address_ functions don't find a + reachable address, set the returned address to the null address + and port. This is a precautionary measure, because some callers do + not check the return value. Fixes bug 24736; bugfix + on 0.2.8.2-alpha. + + o Minor bugfixes (compilation): + - Resolve a few shadowed-variable warnings in the onion service + code. Fixes bug 24634; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (portability, msvc): + - Fix a bug in the bit-counting parts of our timing-wheel code on + MSVC. (Note that MSVC is still not a supported build platform, due + to cryptographic timing channel risks.) Fixes bug 24633; bugfix + on 0.2.9.1-alpha. + + +Changes in version 0.3.2.8-rc - 2017-12-21 + Tor 0.3.2.8-rc fixes a pair of bugs in the KIST and KISTLite + schedulers that had led servers under heavy load to overload their + outgoing connections. All relay operators running earlier 0.3.2.x + versions should upgrade. This version also includes a mitigation for + over-full DESTROY queues leading to out-of-memory conditions: if it + works, we will soon backport it to earlier release series. + + This is the second release candidate in the 0.3.2 series. If we find + no new bugs or regression here, then the first stable 0.3.2 release + will be nearly identical to this. + + o Major bugfixes (KIST, scheduler): + - The KIST scheduler did not correctly account for data already + enqueued in each connection's send socket buffer, particularly in + cases when the TCP/IP congestion window was reduced between + scheduler calls. This situation lead to excessive per-connection + buffering in the kernel, and a potential memory DoS. Fixes bug + 24665; bugfix on 0.3.2.1-alpha. + + o Minor features (geoip): + - Update geoip and geoip6 to the December 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (hidden service v3): + - Bump hsdir_spread_store parameter from 3 to 4 in order to increase + the probability of reaching a service for a client missing + microdescriptors. Fixes bug 24425; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (memory usage): + - When queuing DESTROY cells on a channel, only queue the circuit-id + and reason fields: not the entire 514-byte cell. This fix should + help mitigate any bugs or attacks that fill up these queues, and + free more RAM for other uses. Fixes bug 24666; bugfix + on 0.2.5.1-alpha. + + o Minor bugfixes (scheduler, KIST): + - Use a sane write limit for KISTLite when writing onto a connection + buffer instead of using INT_MAX and shoving as much as it can. + Because the OOM handler cleans up circuit queues, we are better + off at keeping them in that queue instead of the connection's + buffer. Fixes bug 24671; bugfix on 0.3.2.1-alpha. + + +Changes in version 0.3.2.7-rc - 2017-12-14 + Tor 0.3.2.7-rc fixes various bugs in earlier versions of Tor, + including some that could affect reliability or correctness. + + This is the first release candidate in the 0.3.2 series. If we find no + new bugs or regression here, then the first stable 0.3.2. release will + be nearly identical to this. + + o Major bugfixes (circuit prediction): + - Fix circuit prediction logic so that a client doesn't treat a port + as being "handled" by a circuit if that circuit already has + isolation settings on it. This change should make Tor clients more + responsive by improving their chances of having a pre-created + circuit ready for use when a request arrives. Fixes bug 18859; + bugfix on 0.2.3.3-alpha. + + o Minor features (logging): + - Provide better warnings when the getrandom() syscall fails. Closes + ticket 24500. + + o Minor features (portability): + - Tor now compiles correctly on arm64 with libseccomp-dev installed. + (It doesn't yet work with the sandbox enabled.) Closes + ticket 24424. + + o Minor bugfixes (bridge clients, bootstrap): + - Retry directory downloads when we get our first bridge descriptor + during bootstrap or while reconnecting to the network. Keep + retrying every time we get a bridge descriptor, until we have a + reachable bridge. Fixes part of bug 24367; bugfix on 0.2.0.3-alpha. + - Stop delaying bridge descriptor fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when we + have at least one reachable bridge. Fixes part of bug 24367; + bugfix on 0.2.0.3-alpha. + - Stop delaying directory fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when + all our bridges are definitely unreachable. Fixes part of bug + 24367; bugfix on 0.2.0.3-alpha. + + o Minor bugfixes (compilation): + - Fix a signed/unsigned comparison warning introduced by our fix to + TROVE-2017-009. Fixes bug 24480; bugfix on 0.2.5.16. + + o Minor bugfixes (correctness): + - Fix several places in our codebase where a C compiler would be + likely to eliminate a check, based on assuming that undefined + behavior had not happened elsewhere in the code. These cases are + usually a sign of redundant checking or dubious arithmetic. Found + by Georg Koppen using the "STACK" tool from Wang, Zeldovich, + Kaashoek, and Solar-Lezama. Fixes bug 24423; bugfix on various + Tor versions. + + o Minor bugfixes (onion service v3): + - Fix a race where an onion service would launch a new intro circuit + after closing an old one, but fail to register it before freeing + the previously closed circuit. This bug was making the service + unable to find the established intro circuit and thus not upload + its descriptor, thus making a service unavailable for up to 24 + hours. Fixes bug 23603; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (scheduler, KIST): + - Properly set the scheduler state of an unopened channel in the + KIST scheduler main loop. This prevents a harmless but annoying + log warning. Fixes bug 24502; bugfix on 0.3.2.4-alpha. + - Avoid a possible integer overflow when computing the available + space on the TCP buffer of a channel. This had no security + implications; but could make KIST allow too many cells on a + saturated connection. Fixes bug 24590; bugfix on 0.3.2.1-alpha. + - Downgrade to "info" a harmless warning about the monotonic time + moving backwards: This can happen on platform not supporting + monotonic time. Fixes bug 23696; bugfix on 0.3.2.1-alpha. + + +Changes in version 0.3.2.6-alpha - 2017-12-01 + This version of Tor is the latest in the 0.3.2 alpha series. It + includes fixes for several important security issues. All Tor users + should upgrade to this release, or to one of the other releases coming + out today. + + o Major bugfixes (security): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Minor feature (relay statistics): + - Change relay bandwidth reporting stats interval from 4 hours to 24 + hours in order to reduce the efficiency of guard discovery + attacks. Fixes ticket 23856. + + o Minor features (directory authority): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor bugfixes (client): + - By default, do not enable storage of client-side DNS values. These + values were unused by default previously, but they should not have + been cached at all. Fixes bug 24050; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.1.9 - 2017-12-01: + Tor 0.3.1.9 backports important security and stability fixes from the + 0.3.2 development series. All Tor users should upgrade to this + release, or to another of the releases coming out today. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (bridge): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (onion service, backport from 0.3.2.5-alpha): + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.0.13 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.9.14 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.8.17 - 2017-12-01 + Tor 0.2.8.17 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please upgrade with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path through + ourselves, even in the case where we have somehow lost the version of + our descriptor appearing in the consensus. Fixes part of bug 21534; + bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 + and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.5.16 - 2017-12-01 + Tor 0.2.5.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.3.2.5-alpha - 2017-11-22 + Tor 0.3.2.5-alpha is the fifth alpha release in the 0.3.2.x series. It + fixes several stability and reliability bugs, including a fix for + intermittent bootstrapping failures that some people have been seeing + since the 0.3.0.x series. + + Please test this alpha out -- many of these fixes will soon be + backported to stable Tor versions if no additional bugs are found + in them. + + o Major bugfixes (bootstrapping): + - Fetch descriptors aggressively whenever we lack enough to build + circuits, regardless of how many descriptors we are missing. + Previously, we would delay launching the fetch when we had fewer + than 15 missing descriptors, even if some of those descriptors + were blocking circuits from building. Fixes bug 23985; bugfix on + 0.1.1.11-alpha. The effects of this bug became worse in + 0.3.0.3-alpha, when we began treating missing descriptors from our + primary guards as a reason to delay circuits. + - Don't try fetching microdescriptors from relays that have failed + to deliver them in the past. Fixes bug 23817; bugfix + on 0.3.0.1-alpha. + + o Minor features (directory authority): + - Make the "Exit" flag assignment only depend on whether the exit + policy allows connections to ports 80 and 443. Previously relays + would get the Exit flag if they allowed connections to one of + these ports and also port 6667. Resolves ticket 23637. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (linux seccomp2 sandbox): + - Update the sandbox rules so that they should now work correctly + with Glibc 2.26. Closes ticket 24315. + + o Minor features (logging): + - Downgrade a pair of log messages that could occur when an exit's + resolver gave us an unusual (but not forbidden) response. Closes + ticket 24097. + - Improve the message we log when re-enabling circuit build timeouts + after having received a consensus. Closes ticket 20963. + + o Minor bugfixes (compilation): + - Fix a memory leak warning in one of the libevent-related + configuration tests that could occur when manually specifying + -fsanitize=address. Fixes bug 24279; bugfix on 0.3.0.2-alpha. + Found and patched by Alex Xu. + - When detecting OpenSSL on Windows from our configure script, make + sure to try linking with the ws2_32 library. Fixes bug 23783; + bugfix on 0.3.2.2-alpha. + + o Minor bugfixes (control port, linux seccomp2 sandbox): + - Avoid a crash when attempting to use the seccomp2 sandbox together + with the OwningControllerProcess feature. Fixes bug 24198; bugfix + on 0.2.5.1-alpha. + + o Minor bugfixes (control port, onion services): + - Report "FAILED" instead of "UPLOAD_FAILED" "FAILED" for the + HS_DESC event when a service is not able to upload a descriptor. + Fixes bug 24230; bugfix on 0.2.7.1-alpha. + + o Minor bugfixes (directory cache): + - Recover better from empty or corrupt files in the consensus cache + directory. Fixes bug 24099; bugfix on 0.3.1.1-alpha. + - When a consensus diff calculation is only partially successful, + only record the successful parts as having succeeded. Partial + success can happen if (for example) one compression method fails + but the others succeed. Previously we misrecorded all the + calculations as having succeeded, which would later cause a + nonfatal assertion failure. Fixes bug 24086; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (logging): + - Only log once if we notice that KIST support is gone. Fixes bug + 24158; bugfix on 0.3.2.1-alpha. + - Suppress a log notice when relay descriptors arrive. We already + have a bootstrap progress for this so no need to log notice + everytime tor receives relay descriptors. Microdescriptors behave + the same. Fixes bug 23861; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (network layer): + - When closing a connection via close_connection_immediately(), we + mark it as "not blocked on bandwidth", to prevent later calls from + trying to unblock it, and give it permission to read. This fixes a + backtrace warning that can happen on relays under various + circumstances. Fixes bug 24167; bugfix on 0.1.0.1-rc. + + o Minor bugfixes (onion services): + - The introduction circuit was being timed out too quickly while + waiting for the rendezvous circuit to complete. Keep the intro + circuit around longer instead of timing out and reopening new ones + constantly. Fixes bug 23681; bugfix on 0.2.4.8-alpha. + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + - Silence a warning about failed v3 onion descriptor uploads that + can happen naturally under certain edge cases. Fixes part of bug + 23662; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (tests): + - Fix a memory leak in one of the bridge-distribution test cases. + Fixes bug 24345; bugfix on 0.3.2.3-alpha. + - Fix a bug in our fuzzing mock replacement for crypto_pk_checksig(), + to correctly handle cases where a caller gives it an RSA key of + under 160 bits. (This is not actually a bug in Tor itself, but + rather in our fuzzing code.) Fixes bug 24247; bugfix on + 0.3.0.3-alpha. Found by OSS-Fuzz as issue 4177. + + o Documentation: + - Add notes in man page regarding OS support for the various + scheduler types. Attempt to use less jargon in the scheduler + section. Closes ticket 24254. + + +Changes in version 0.3.2.4-alpha - 2017-11-08 + Tor 0.3.2.4-alpha is the fourth alpha release in the 0.3.2.x series. + It fixes several stability and reliability bugs, especially including + a major reliability issue that has been plaguing fast exit relays in + recent months. + + o Major bugfixes (exit relays, DNS): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (scheduler, channel): + - Stop processing scheduled channels if they closed while flushing + cells. This can happen if the write on the connection fails + leading to the channel being closed while in the scheduler loop. + Fixes bug 23751; bugfix on 0.3.2.1-alpha. + + o Minor features (logging, scheduler): + - Introduce a SCHED_BUG() function to log extra information about + the scheduler state if we ever catch a bug in the scheduler. + Closes ticket 23753. + + o Minor features (removed deprecations): + - The ClientDNSRejectInternalAddresses flag can once again be set in + non-testing Tor networks, so long as they do not use the default + directory authorities. This change also removes the deprecation of + this flag from 0.2.9.2-alpha. Closes ticket 21031. + + o Minor features (testing): + - Our fuzzing tests now test the encrypted portions of v3 onion + service descriptors. Implements more of 21509. + + o Minor bugfixes (directory client): + - On failure to download directory information, delay retry attempts + by a random amount based on the "decorrelated jitter" algorithm. + Our previous delay algorithm tended to produce extra-long delays + too easily. Fixes bug 23816; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (IPv6, v3 single onion services): + - Remove buggy code for IPv6-only v3 single onion services, and + reject attempts to configure them. This release supports IPv4, + dual-stack, and IPv6-only v3 onion services; and IPv4 and dual- + stack v3 single onion services. Fixes bug 23820; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (logging, relay): + - Give only a protocol warning when the ed25519 key is not + consistent between the descriptor and microdescriptor of a relay. + This can happen, for instance, if the relay has been flagged + NoEdConsensus. Fixes bug 24025; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (manpage, onion service): + - Document that the HiddenServiceNumIntroductionPoints option is + 0-10 for v2 services and 0-20 for v3 services. Fixes bug 24115; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (memory leaks): + - Fix a minor memory leak at exit in the KIST scheduler. This bug + should have no user-visible impact. Fixes bug 23774; bugfix + on 0.3.2.1-alpha. + - Fix a memory leak when decrypting a badly formatted v3 onion + service descriptor. Fixes bug 24150; bugfix on 0.3.2.1-alpha. + Found by OSS-Fuzz; this is OSS-Fuzz issue 3994. + + o Minor bugfixes (onion services): + - Cache some needed onion service client information instead of + constantly computing it over and over again. Fixes bug 23623; + bugfix on 0.3.2.1-alpha. + - Properly retry HSv3 descriptor fetches when missing required + directory information. Fixes bug 23762; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (path selection): + - When selecting relays by bandwidth, avoid a rounding error that + could sometimes cause load to be imbalanced incorrectly. + Previously, we would always round upwards; now, we round towards + the nearest integer. This had the biggest effect when a relay's + weight adjustments should have given it weight 0, but it got + weight 1 instead. Fixes bug 23318; bugfix on 0.2.4.3-alpha. + - When calculating the fraction of nodes that have descriptors, and + all nodes in the network have zero bandwidths, count the number of + nodes instead. Fixes bug 23318; bugfix on 0.2.4.10-alpha. + - Actually log the total bandwidth in compute_weighted_bandwidths(). + Fixes bug 24170; bugfix on 0.2.4.3-alpha. + + o Minor bugfixes (relay, crash): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing): + - Fix a spurious fuzzing-only use of an uninitialized value. Found + by Brian Carpenter. Fixes bug 24082; bugfix on 0.3.0.3-alpha. + - Test that IPv6-only clients can use microdescriptors when running + "make test-network-all". Requires chutney master 61c28b9 or later. + Closes ticket 24109. + + +Changes in version 0.3.2.3-alpha - 2017-10-27 + Tor 0.3.2.3-alpha is the third release in the 0.3.2 series. It fixes + numerous small bugs in earlier versions of 0.3.2.x, and adds a new + directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Minor features (bridge): + - Bridge relays can now set the BridgeDistribution config option to + add a "bridge-distribution-request" line to their bridge + descriptor, which tells BridgeDB how they'd like their bridge + address to be given out. (Note that as of Oct 2017, BridgeDB does + not yet implement this feature.) As a side benefit, this feature + provides a way to distinguish bridge descriptors from non-bridge + descriptors. Implements tickets 18329. + + o Minor features (client, entry guards): + - Improve log messages when missing descriptors for primary guards. + Resolves ticket 23670. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (bridge): + - Overwrite the bridge address earlier in the process of retrieving + its descriptor, to make sure we reach it on the configured + address. Fixes bug 20532; bugfix on 0.2.0.10-alpha. + + o Minor bugfixes (documentation): + - Document better how to read gcov, and what our gcov postprocessing + scripts do. Fixes bug 23739; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (entry guards): + - Tor now updates its guard state when it reads a consensus + regardless of whether it's missing descriptors. That makes tor use + its primary guards to fetch descriptors in some edge cases where + it would previously have used fallback directories. Fixes bug + 23862; bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (hidden service client): + - When handling multiple SOCKS request for the same .onion address, + only fetch the service descriptor once. + - When a descriptor fetch fails with a non-recoverable error, close + all pending SOCKS requests for that .onion. Fixes bug 23653; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (hidden service): + - Always regenerate missing hidden service public key files. Prior + to this, if the public key was deleted from disk, it wouldn't get + recreated. Fixes bug 23748; bugfix on 0.3.2.2-alpha. Patch + from "cathugger". + - Make sure that we have a usable ed25519 key when the intro point + relay supports ed25519 link authentication. Fixes bug 24002; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (hidden service, v2): + - When reloading configured hidden services, copy all information + from the old service object. Previously, some data was omitted, + causing delays in descriptor upload, and other bugs. Fixes bug + 23790; bugfix on 0.2.1.9-alpha. + + o Minor bugfixes (memory safety, defensive programming): + - Clear the target address when node_get_prim_orport() returns + early. Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (relay): + - Avoid a BUG warning when receiving a dubious CREATE cell while an + option transition is in progress. Fixes bug 23952; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (testing): + - Adjust the GitLab CI configuration to more closely match that of + Travis CI. Fixes bug 23757; bugfix on 0.3.2.2-alpha. + - Prevent scripts/test/coverage from attempting to move gcov output + to the root directory. Fixes bug 23741; bugfix on 0.2.5.1-alpha. + - When running unit tests as root, skip a test that would fail + because it expects a permissions error. This affects some + continuous integration setups. Fixes bug 23758; bugfix + on 0.3.2.2-alpha. + - Stop unconditionally mirroring the tor repository in GitLab CI. + This prevented developers from enabling GitLab CI on master. Fixes + bug 23755; bugfix on 0.3.2.2-alpha. + - Fix the hidden service v3 descriptor decoding fuzzing to use the + latest decoding API correctly. Fixes bug 21509; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (warnings): + - When we get an HTTP request on a SOCKS port, tell the user about + the new HTTPTunnelPort option. Previously, we would give a "Tor is + not an HTTP Proxy" message, which stopped being true when + HTTPTunnelPort was introduced. Fixes bug 23678; bugfix + on 0.3.2.1-alpha. + + +Changes in version 0.2.5.15 - 2017-10-25 + Tor 0.2.5.15 backports a collection of bugfixes from later Tor release + series. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (openbsd, denial-of-service, backport from 0.3.1.5-alpha): + - Avoid an assertion failure bug affecting our implementation of + inet_pton(AF_INET6) on certain OpenBSD systems whose strtol() + handling of "0xx" differs from what we had expected. Fixes bug + 22789; bugfix on 0.2.3.8-alpha. Also tracked as TROVE-2017-007. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (defensive programming, undefined behavior, backport from 0.3.1.4-alpha): + - Fix a memset() off the end of an array when packing cells. This + bug should be harmless in practice, since the corrupted bytes are + still in the same structure, and are always padding bytes, + ignored, or immediately overwritten, depending on compiler + behavior. Nevertheless, because the memset()'s purpose is to make + sure that any other cell-handling bugs can't expose bytes to the + network, we need to fix it. Fixes bug 22737; bugfix on + 0.2.4.11-alpha. Fixes CID 1401591. + + o Build features (backport from 0.3.1.5-alpha): + - Tor's repository now includes a Travis Continuous Integration (CI) + configuration file (.travis.yml). This is meant to help new + developers and contributors who fork Tor to a Github repository be + better able to test their changes, and understand what we expect + to pass. To use this new build feature, you must fork Tor to your + Github account, then go into the "Integrations" menu in the + repository settings for your fork and enable Travis, then push + your changes. Closes ticket 22636. + + +Changes in version 0.2.8.16 - 2017-10-25 + Tor 0.2.8.16 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.9.13 - 2017-10-25 + Tor 0.2.9.13 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.0.12 - 2017-10-25 + Tor 0.3.0.12 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.1.8 - 2017-10-25 + Tor 0.3.1.8 is the second stable release in the 0.3.1 series. + It includes several bugfixes, including a bugfix for a crash issue + that had affected relays under memory pressure. It also adds + a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (compilation, backport from 0.3.2.2-alpha): + - Fix a compilation warning when building with zstd support on + 32-bit platforms. Fixes bug 23568; bugfix on 0.3.1.1-alpha. Found + and fixed by Andreas Stieger. + + o Minor bugfixes (compression, backport from 0.3.2.2-alpha): + - Handle a pathological case when decompressing Zstandard data when + the output buffer size is zero. Fixes bug 23551; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (directory authority, backport from 0.3.2.1-alpha): + - Remove the length limit on HTTP status lines that authorities can + send in their replies. Fixes bug 23499; bugfix on 0.3.1.6-rc. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (unit tests, backport from 0.3.2.2-alpha): + - Fix additional channelpadding unit test failures by using mocked + time instead of actual time for all tests. Fixes bug 23608; bugfix + on 0.3.1.1-alpha. + + Changes in version 0.3.2.2-alpha - 2017-09-29 Tor 0.3.2.2-alpha is the second release in the 0.3.2 series. This release fixes several minor bugs in the new scheduler and next- @@ -3220,7 +4950,7 @@ Changes in version 0.3.0.1-alpha - 2016-12-19 initial code by Alec Heifetz. - Relays now support the HSDir version 3 protocol, so that they can can store and serve v3 descriptors. This is part of the next- - generation onion service work detailled in proposal 224. Closes + generation onion service work detailed in proposal 224. Closes ticket 17238. o Major features (protocol, ed25519 identity keys): @@ -4218,7 +5948,7 @@ Changes in version 0.2.9.2-alpha - 2016-08-24 o Deprecated features: - A number of DNS-cache-related sub-options for client ports are now deprecated for security reasons, and may be removed in a future - version of Tor. (We believe that client-side DNS cacheing is a bad + version of Tor. (We believe that client-side DNS caching is a bad idea for anonymity, and you should not turn it on.) The options are: CacheDNS, CacheIPv4DNS, CacheIPv6DNS, UseDNSCache, UseIPv4Cache, and UseIPv6Cache. @@ -6688,7 +8418,7 @@ Changes in version 0.2.6.5-rc - 2015-03-18 o Major bugfixes (pluggable transports): - Initialize the extended OR Port authentication cookie before launching pluggable transports. This prevents a race condition - that occured when server-side pluggable transports would cache the + that occurred when server-side pluggable transports would cache the authentication cookie before it has been (re)generated. Fixes bug 15240; bugfix on 0.2.5.1-alpha. @@ -7431,7 +9161,7 @@ Changes in version 0.2.6.2-alpha - 2014-12-31 some bugs where we would look at (but fortunately, not reveal) uninitialized memory on the stack. Fixes bug 14013; bugfix on all versions of Tor. - - Clear all memory targetted by tor_addr_{to,from}_sockaddr(), not + - Clear all memory targeted by tor_addr_{to,from}_sockaddr(), not just the part that's used. This makes it harder for data leak bugs to occur in the event of other programming failures. Resolves ticket 14041. @@ -8574,7 +10304,7 @@ Changes in version 0.2.5.5-alpha - 2014-06-18 directory authority options, remove the documentation for a V2-directory fetching option that no longer exists. Resolves ticket 11634. - - Correct the documenation so that it lists the correct directory + - Correct the documentation so that it lists the correct directory for the stats files. (They are in a subdirectory called "stats", not "status".) - In the manpage, move more authority-only options into the @@ -9859,7 +11589,7 @@ Changes in version 0.2.5.1-alpha - 2013-10-02 from Arlo Breault. - Remove instances of strcpy() from the unit tests. They weren't hurting anything, since they were only in the unit tests, but it's - embarassing to have strcpy() in the code at all, and some analysis + embarrassing to have strcpy() in the code at all, and some analysis tools don't like it. Fixes bug 8790; bugfix on 0.2.3.6-alpha and 0.2.3.8-alpha. Patch from Arlo Breault. @@ -9960,7 +11690,7 @@ Changes in version 0.2.4.16-rc - 2013-08-10 0.2.4.15-rc. Found by stem integration tests. o Minor bugfixes: - - Fix an invalid memory read that occured when a pluggable + - Fix an invalid memory read that occurred when a pluggable transport proxy failed its configuration protocol. Fixes bug 9288; bugfix on 0.2.4.1-alpha. - When evaluating whether to use a connection that we haven't @@ -10218,7 +11948,7 @@ Changes in version 0.2.4.12-alpha - 2013-04-18 0.2.0.10-alpha. Reported pseudonymously. - Make the format and order of STREAM events for DNS lookups consistent among the various ways to launch DNS lookups. Fixes - bug 8203; bugfix on 0.2.0.24-rc. Patch by "Desoxy." + bug 8203; bugfix on 0.2.0.24-rc. Patch by "Desoxy". - Correct our check for which versions of Tor support the EXTEND2 cell. We had been willing to send it to Tor 0.2.4.7-alpha and later, when support was really added in version 0.2.4.8-alpha. @@ -12751,7 +14481,7 @@ Changes in version 0.2.3.11-alpha - 2012-01-22 CloseHSServiceRendCircuitsImmediatelyOnTimeout option. Fixes the remaining part of bug 1297; bugfix on 0.2.2.2-alpha. - Make sure we never mark the wrong rendezvous circuit as having - had its introduction cell acknowleged by the introduction-point + had its introduction cell acknowledged by the introduction-point relay. Previously, when we received an INTRODUCE_ACK cell on a client-side hidden-service introduction circuit, we might have marked a rendezvous circuit other than the one we specified in @@ -14058,7 +15788,7 @@ Changes in version 0.2.3.3-alpha - 2011-09-01 raised by bug 3898. - The "--quiet" and "--hush" options now apply not only to Tor's behavior before logs are configured, but also to Tor's behavior in - the absense of configured logs. Fixes bug 3550; bugfix on + the absence of configured logs. Fixes bug 3550; bugfix on 0.2.0.10-alpha. o Minor bugfixes (also part of 0.2.2.31-rc): @@ -16559,7 +18289,7 @@ Changes in version 0.2.2.8-alpha - 2010-01-26 please upgrade. o Major bugfixes: - - Fix a memory corruption bug on bridges that occured during the + - Fix a memory corruption bug on bridges that occurred during the inclusion of stats data in extra-info descriptors. Also fix the interface for geoip_get_bridge_stats* to prevent similar bugs in the future. Diagnosis by Tas, patch by Karsten and Sebastian. @@ -16973,7 +18703,7 @@ Changes in version 0.2.2.2-alpha - 2009-09-21 to EDGE and find out if the build-time data in the .tor/state gets reset without loss of Tor usability. You should also see a notice log message telling you that Tor has reset its timeout. - - Directory authorities can now vote on arbitary integer values as + - Directory authorities can now vote on arbitrary integer values as part of the consensus process. This is designed to help set network-wide parameters. Implements proposal 167. - Tor now reads the "circwindow" parameter out of the consensus, @@ -20001,7 +21731,7 @@ Changes in version 0.2.0.9-alpha - 2007-10-24 - Distinguish between detached signatures for the wrong period, and detached signatures for a divergent vote. - Fix a small memory leak when computing a consensus. - - When there's no concensus, we were forming a vote every 30 + - When there's no consensus, we were forming a vote every 30 minutes, but writing the "valid-after" line in our vote based on our configured V3AuthVotingInterval: so unless the intervals matched up, we immediately rejected our own vote because it didn't @@ -23388,7 +25118,7 @@ Changes in version 0.1.0.9-rc - 2005-06-09 KeepalivePeriod, ClientOnly, NoPublish, HttpProxy, HttpsProxy, HttpProxyAuthenticator - Stop warning about sigpipes in the logs. We're going to - pretend that getting these occassionally is normal and fine. + pretend that getting these occasionally is normal and fine. - Resolve OS X installer bugs: stop claiming to be 0.0.9.2 in certain installer screens; and don't put stuff into StartupItems unless @@ -23853,7 +25583,7 @@ Changes in version 0.1.0.1-rc - 2005-03-28 Changes in version 0.0.9.6 - 2005-03-24 o Bugfixes on 0.0.9.x (crashes and asserts): - - Add new end stream reasons to maintainance branch. Fix bug where + - Add new end stream reasons to maintenance branch. Fix bug where reason (8) could trigger an assert. Prevent bug from recurring. - Apparently win32 stat wants paths to not end with a slash. - Fix assert triggers in assert_cpath_layer_ok(), where we were diff --git a/Makefile.am b/Makefile.am index a126986f3a..04ca88a236 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT) endif if USE_RUST -rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@ \ +rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_STATIC_NAME@ \ @TOR_RUST_EXTRA_LIBS@ else rust_ldadd= @@ -118,17 +118,19 @@ test-network: need-chutney-path $(TESTING_TOR_BINARY) src/tools/tor-gencert # 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 both the BSD and the Linux ping6 syntax, because they're incompatible +# Try the syntax for BSD ping6, Linux ping6, and Linux ping -6, +# because they're incompatible test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert mkdir -p $(TEST_NETWORK_ALL_LOG_DIR) @flavors="$(TEST_CHUTNEY_FLAVORS)"; \ - if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1; then \ - echo "ping6 ::1 succeeded, running IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ + if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1 || ping -6 -c 1 -W 1 ::1 >/dev/null 2>&1; then \ + echo "ping6 ::1 or ping ::1 succeeded, running IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ flavors="$$flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \ else \ - echo "ping6 ::1 failed, skipping IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ + echo "ping6 ::1 and ping ::1 failed, skipping IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ skip_flavors="$$skip_flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \ fi; \ if command -v tor-stable >/dev/null 2>&1; then \ @@ -247,3 +249,8 @@ mostlyclean-local: clean-local: rm -rf $(top_builddir)/src/rust/target rm -rf $(top_builddir)/src/rust/.cargo/registry + +if USE_RUST +distclean-local: distclean-rust +endif + diff --git a/ReleaseNotes b/ReleaseNotes index 07a3881acb..6c31d8ab0c 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -2,6 +2,1379 @@ This document summarizes new features and bugfixes in each stable release of Tor. If you want to see more detailed descriptions of the changes in each development snapshot, see the ChangeLog file. +Changes in version 0.3.2.9 - 2018-01-09 + Tor 0.3.2.9 is the first stable release in the 0.3.2 series. + + The 0.3.2 series includes our long-anticipated new onion service + design, with numerous security features. (For more information, see + our blog post at https://blog.torproject.org/fall-harvest.) We also + have a new circuit scheduler algorithm for improved performance on + relays everywhere (see https://blog.torproject.org/kist-and-tell), + along with many smaller features and bugfixes. + + Per our stable release policy, we plan to support each stable release + series for at least the next nine months, or for three months after + the first stable release of the next series: whichever is longer. If + you need a release with long-term support, we recommend that you stay + with the 0.2.9 series. + + Below is a list of the changes since 0.3.1.7. For a list of all + changes since 0.3.2.8-rc, see the ChangeLog file. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Major features (next-generation onion services): + - Tor now supports the next-generation onion services protocol for + clients and services! As part of this release, the core of + proposal 224 has been implemented and is available for + experimentation and testing by our users. This newer version of + onion services ("v3") features many improvements over the legacy + system, including: + + a) Better crypto (replaced SHA1/DH/RSA1024 + with SHA3/ed25519/curve25519) + + b) Improved directory protocol, leaking much less information to + directory servers. + + c) Improved directory protocol, with smaller surface for + targeted attacks. + + d) Better onion address security against impersonation. + + e) More extensible introduction/rendezvous protocol. + + f) A cleaner and more modular codebase. + + You can identify a next-generation onion address by its length: + they are 56 characters long, as in + "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion". + + In the future, we will release more options and features for v3 + onion services, but we first need a testing period, so that the + current codebase matures and becomes more robust. Planned features + include: offline keys, advanced client authorization, improved + guard algorithms, and statistics. For full details, see + proposal 224. + + Legacy ("v2") onion services will still work for the foreseeable + future, and will remain the default until this new codebase gets + tested and hardened. Service operators who want to experiment with + the new system can use the 'HiddenServiceVersion 3' torrc + directive along with the regular onion service configuration + options. For more information, see our blog post at + "https://blog.torproject.org/fall-harvest". Enjoy! + + o Major feature (scheduler, channel): + - Tor now uses new schedulers to decide which circuits should + deliver cells first, in order to improve congestion at relays. The + first type is called "KIST" ("Kernel Informed Socket Transport"), + and is only available on Linux-like systems: it uses feedback from + the kernel to prevent the kernel's TCP buffers from growing too + full. The second new scheduler type is called "KISTLite": it + behaves the same as KIST, but runs on systems without kernel + support for inspecting TCP implementation details. The old + scheduler is still available, under the name "Vanilla". To change + the default scheduler preference order, use the new "Schedulers" + option. (The default preference order is "KIST,KISTLite,Vanilla".) + + Matt Traudt implemented KIST, based on research by Rob Jansen, + John Geddes, Christ Wacek, Micah Sherr, and Paul Syverson. For + more information, see the design paper at + http://www.robgjansen.com/publications/kist-sec2014.pdf and the + followup implementation paper at https://arxiv.org/abs/1709.01044. + Closes ticket 12541. For more information, see our blog post at + "https://blog.torproject.org/kist-and-tell". + + o Major bugfixes (security, general): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + + o Major bugfixes (security, directory authority): + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + + o Major bugfixes (security, onion service v2): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, relay): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (bootstrapping): + - Fetch descriptors aggressively whenever we lack enough to build + circuits, regardless of how many descriptors we are missing. + Previously, we would delay launching the fetch when we had fewer + than 15 missing descriptors, even if some of those descriptors + were blocking circuits from building. Fixes bug 23985; bugfix on + 0.1.1.11-alpha. The effects of this bug became worse in + 0.3.0.3-alpha, when we began treating missing descriptors from our + primary guards as a reason to delay circuits. + - Don't try fetching microdescriptors from relays that have failed + to deliver them in the past. Fixes bug 23817; bugfix + on 0.3.0.1-alpha. + + o Major bugfixes (circuit prediction): + - Fix circuit prediction logic so that a client doesn't treat a port + as being "handled" by a circuit if that circuit already has + isolation settings on it. This change should make Tor clients more + responsive by improving their chances of having a pre-created + circuit ready for use when a request arrives. Fixes bug 18859; + bugfix on 0.2.3.3-alpha. + + o Major bugfixes (exit relays, DNS): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (relay, crash, assertion failure): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Major bugfixes (usability, control port): + - Report trusted clock skew indications as bootstrap errors, so + controllers can more easily alert users when their clocks are + wrong. Fixes bug 23506; bugfix on 0.1.2.6-alpha. + + o Minor features (bridge): + - Bridge relays can now set the BridgeDistribution config option to + add a "bridge-distribution-request" line to their bridge + descriptor, which tells BridgeDB how they'd like their bridge + address to be given out. (Note that as of Oct 2017, BridgeDB does + not yet implement this feature.) As a side benefit, this feature + provides a way to distinguish bridge descriptors from non-bridge + descriptors. Implements tickets 18329. + - When handling the USERADDR command on an ExtOrPort, warn when the + transports provides a USERADDR with no port. In a future version, + USERADDR commands of this format may be rejected. Detects problems + related to ticket 23080. + + o Minor features (bug detection): + - Log a warning message with a stack trace for any attempt to call + get_options() during option validation. This pattern has caused + subtle bugs in the past. Closes ticket 22281. + + o Minor features (build, compilation): + - The "check-changes" feature is now part of the "make check" tests; + we'll use it to try to prevent misformed changes files from + accumulating. Closes ticket 23564. + - Tor builds should now fail if there are any mismatches between the + C type representing a configuration variable and the C type the + data-driven parser uses to store a value there. Previously, we + needed to check these by hand, which sometimes led to mistakes. + Closes ticket 23643. + + o Minor features (client): + - You can now use Tor as a tunneled HTTP proxy: use the new + HTTPTunnelPort option to open a port that accepts HTTP CONNECT + requests. Closes ticket 22407. + - Add an extra check to make sure that we always use the newer guard + selection code for picking our guards. Closes ticket 22779. + - When downloading (micro)descriptors, don't split the list into + multiple requests unless we want at least 32 descriptors. + Previously, we split at 4, not 32, which led to significant + overhead in HTTP request size and degradation in compression + performance. Closes ticket 23220. + - Improve log messages when missing descriptors for primary guards. + Resolves ticket 23670. + + o Minor features (command line): + - Add a new commandline option, --key-expiration, which prints when + the current signing key is going to expire. Implements ticket + 17639; patch by Isis Lovecruft. + + o Minor features (control port): + - If an application tries to use the control port as an HTTP proxy, + respond with a meaningful "This is the Tor control port" message, + and log the event. Closes ticket 1667. Patch from Ravi + Chandra Padmala. + - Provide better error message for GETINFO desc/(id|name) when not + fetching router descriptors. Closes ticket 5847. Patch by + Kevin Butler. + - Add GETINFO "{desc,md}/download-enabled", to inform the controller + whether Tor will try to download router descriptors and + microdescriptors respectively. Closes ticket 22684. + - Added new GETINFO targets "ip-to-country/{ipv4,ipv6}-available", + so controllers can tell whether the geoip databases are loaded. + Closes ticket 23237. + - Adds a timestamp field to the CIRC_BW and STREAM_BW bandwidth + events. Closes ticket 19254. Patch by "DonnchaC". + + o Minor features (development support): + - Developers can now generate a call-graph for Tor using the + "calltool" python program, which post-processes object dumps. It + should work okay on many Linux and OSX platforms, and might work + elsewhere too. To run it, install calltool from + https://gitweb.torproject.org/user/nickm/calltool.git and run + "make callgraph". Closes ticket 19307. + + o Minor features (directory authority): + - Make the "Exit" flag assignment only depend on whether the exit + policy allows connections to ports 80 and 443. Previously relays + would get the Exit flag if they allowed connections to one of + these ports and also port 6667. Resolves ticket 23637. + + o Minor features (ed25519): + - Add validation function to checks for torsion components in + ed25519 public keys, used by prop224 client-side code. Closes + ticket 22006. Math help by Ian Goldberg. + + o Minor features (exit relay, DNS): + - Improve the clarity and safety of the log message from evdns when + receiving an apparently spoofed DNS reply. Closes ticket 3056. + + o Minor features (fallback directory mirrors): + - The fallback directory list has been re-generated based on the + current status of the network. Tor uses fallback directories to + bootstrap when it doesn't yet have up-to-date directory + information. Closes ticket 24801. + - Make the default DirAuthorityFallbackRate 0.1, so that clients + prefer to bootstrap from fallback directory mirrors. This is a + follow-up to 24679, which removed weights from the default + fallbacks. Implements ticket 24681. + + o Minor features (geoip): + - Update geoip and geoip6 to the January 5 2018 Maxmind GeoLite2 + Country database. + + o Minor features (integration, hardening): + - Add a new NoExec option to prevent Tor from running other + programs. When this option is set to 1, Tor will never try to run + another program, regardless of the settings of + PortForwardingHelper, ClientTransportPlugin, or + ServerTransportPlugin. Once NoExec is set, it cannot be disabled + without restarting Tor. Closes ticket 22976. + + o Minor features (linux seccomp2 sandbox): + - Update the sandbox rules so that they should now work correctly + with Glibc 2.26. Closes ticket 24315. + + o Minor features (logging): + - Provide better warnings when the getrandom() syscall fails. Closes + ticket 24500. + - Downgrade a pair of log messages that could occur when an exit's + resolver gave us an unusual (but not forbidden) response. Closes + ticket 24097. + - Improve the message we log when re-enabling circuit build timeouts + after having received a consensus. Closes ticket 20963. + - Log more circuit information whenever we are about to try to + package a relay cell on a circuit with a nonexistent n_chan. + Attempt to diagnose ticket 8185. + - Improve info-level log identification of particular circuits, to + help with debugging. Closes ticket 23645. + - Improve the warning message for specifying a relay by nickname. + The previous message implied that nickname registration was still + part of the Tor network design, which it isn't. Closes + ticket 20488. + - If the sandbox filter fails to load, suggest to the user that + their kernel might not support seccomp2. Closes ticket 23090. + + o Minor features (onion service, circuit, logging): + - Improve logging of many callsite in the circuit subsystem to print + the circuit identifier(s). + - Log when we cleanup an intro point from a service so we know when + and for what reason it happened. Closes ticket 23604. + + o Minor features (portability): + - Tor now compiles correctly on arm64 with libseccomp-dev installed. + (It doesn't yet work with the sandbox enabled.) Closes + ticket 24424. + - Check at configure time whether uint8_t is the same type as + unsigned char. Lots of existing code already makes this + assumption, and there could be strict aliasing issues if the + assumption is violated. Closes ticket 22410. + + o Minor features (relay): + - When choosing which circuits can be expired as unused, consider + circuits from clients even if those clients used regular CREATE + cells to make them; and do not consider circuits from relays even + if they were made with CREATE_FAST. Part of ticket 22805. + - Reject attempts to use relative file paths when RunAsDaemon is + set. Previously, Tor would accept these, but the directory- + changing step of RunAsDaemon would give strange and/or confusing + results. Closes ticket 22731. + + o Minor features (relay statistics): + - Change relay bandwidth reporting stats interval from 4 hours to 24 + hours in order to reduce the efficiency of guard discovery + attacks. Fixes ticket 23856. + + o Minor features (reverted deprecations): + - The ClientDNSRejectInternalAddresses flag can once again be set in + non-testing Tor networks, so long as they do not use the default + directory authorities. This change also removes the deprecation of + this flag from 0.2.9.2-alpha. Closes ticket 21031. + + o Minor features (robustness): + - Change several fatal assertions when flushing buffers into non- + fatal assertions, to prevent any recurrence of 23690. + + o Minor features (startup, safety): + - When configured to write a PID file, Tor now exits if it is unable + to do so. Previously, it would warn and continue. Closes + ticket 20119. + + o Minor features (static analysis): + - The BUG() macro has been changed slightly so that Coverity no + longer complains about dead code if the bug is impossible. Closes + ticket 23054. + + o Minor features (testing): + - Our fuzzing tests now test the encrypted portions of v3 onion + service descriptors. Implements more of 21509. + - Add a unit test to make sure that our own generated platform + string will be accepted by directory authorities. Closes + ticket 22109. + - The default chutney network tests now include tests for the v3 + onion service design. Make sure you have the latest version of + chutney if you want to run these. Closes ticket 22437. + - Add a unit test to verify that we can parse a hardcoded v2 onion + service descriptor. Closes ticket 15554. + + o Minor bugfixes (address selection): + - When the fascist_firewall_choose_address_ functions don't find a + reachable address, set the returned address to the null address + and port. This is a precautionary measure, because some callers do + not check the return value. Fixes bug 24736; bugfix + on 0.2.8.2-alpha. + + o Minor bugfixes (bootstrapping): + - When warning about state file clock skew, report the correct + direction for the detected skew. Fixes bug 23606; bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (bridge clients, bootstrap): + - Retry directory downloads when we get our first bridge descriptor + during bootstrap or while reconnecting to the network. Keep + retrying every time we get a bridge descriptor, until we have a + reachable bridge. Fixes part of bug 24367; bugfix on 0.2.0.3-alpha. + - Stop delaying bridge descriptor fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when we + have at least one reachable bridge. Fixes part of bug 24367; + bugfix on 0.2.0.3-alpha. + - Stop delaying directory fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when + all our bridges are definitely unreachable. Fixes part of bug + 24367; bugfix on 0.2.0.3-alpha. + + o Minor bugfixes (bridge): + - Overwrite the bridge address earlier in the process of retrieving + its descriptor, to make sure we reach it on the configured + address. Fixes bug 20532; bugfix on 0.2.0.10-alpha. + + o Minor bugfixes (build, compilation): + - Fix a compilation warning when building with zstd support on + 32-bit platforms. Fixes bug 23568; bugfix on 0.3.1.1-alpha. Found + and fixed by Andreas Stieger. + - When searching for OpenSSL, don't accept any OpenSSL library that + lacks TLSv1_1_method(): Tor doesn't build with those versions. + Additionally, look in /usr/local/opt/openssl, if it's present. + These changes together repair the default build on OSX systems + with Homebrew installed. Fixes bug 23602; bugfix on 0.2.7.2-alpha. + - Fix a signed/unsigned comparison warning introduced by our fix to + TROVE-2017-009. Fixes bug 24480; bugfix on 0.2.5.16. + - Fix a memory leak warning in one of the libevent-related + configuration tests that could occur when manually specifying + -fsanitize=address. Fixes bug 24279; bugfix on 0.3.0.2-alpha. + Found and patched by Alex Xu. + - Fix unused-variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (certificate handling): + - Fix a time handling bug in Tor certificates set to expire after + the year 2106. Fixes bug 23055; bugfix on 0.3.0.1-alpha. Found by + Coverity as CID 1415728. + + o Minor bugfixes (client): + - By default, do not enable storage of client-side DNS values. These + values were unused by default previously, but they should not have + been cached at all. Fixes bug 24050; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (client, usability): + - Refrain from needlessly rejecting SOCKS5-with-hostnames and + SOCKS4a requests that contain IP address strings, even when + SafeSocks in enabled, as this prevents user from connecting to + known IP addresses without relying on DNS for resolving. SafeSocks + still rejects SOCKS connections that connect to IP addresses when + those addresses are _not_ encoded as hostnames. Fixes bug 22461; + bugfix on Tor 0.2.6.2-alpha. + + o Minor bugfixes (code correctness): + - Call htons() in extend_cell_format() for encoding a 16-bit value. + Previously we used ntohs(), which happens to behave the same on + all the platforms we support, but which isn't really correct. + Fixes bug 23106; bugfix on 0.2.4.8-alpha. + - For defense-in-depth, make the controller's write_escaped_data() + function robust to extremely long inputs. Fixes bug 19281; bugfix + on 0.1.1.1-alpha. Reported by Guido Vranken. + - Fix several places in our codebase where a C compiler would be + likely to eliminate a check, based on assuming that undefined + behavior had not happened elsewhere in the code. These cases are + usually a sign of redundant checking or dubious arithmetic. Found + by Georg Koppen using the "STACK" tool from Wang, Zeldovich, + Kaashoek, and Solar-Lezama. Fixes bug 24423; bugfix on various + Tor versions. + + o Minor bugfixes (compression): + - Handle a pathological case when decompressing Zstandard data when + the output buffer size is zero. Fixes bug 23551; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (consensus expiry): + - Check for adequate directory information correctly. Previously, Tor + would reconsider whether it had sufficient directory information + every 2 minutes. Fixes bug 23091; bugfix on 0.2.0.19-alpha. + + o Minor bugfixes (control port, linux seccomp2 sandbox): + - Avoid a crash when attempting to use the seccomp2 sandbox together + with the OwningControllerProcess feature. Fixes bug 24198; bugfix + on 0.2.5.1-alpha. + + o Minor bugfixes (control port, onion services): + - Report "FAILED" instead of "UPLOAD_FAILED" "FAILED" for the + HS_DESC event when a service is not able to upload a descriptor. + Fixes bug 24230; bugfix on 0.2.7.1-alpha. + + o Minor bugfixes (directory cache): + - Recover better from empty or corrupt files in the consensus cache + directory. Fixes bug 24099; bugfix on 0.3.1.1-alpha. + - When a consensus diff calculation is only partially successful, + only record the successful parts as having succeeded. Partial + success can happen if (for example) one compression method fails + but the others succeed. Previously we misrecorded all the + calculations as having succeeded, which would later cause a + nonfatal assertion failure. Fixes bug 24086; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (directory client): + - On failure to download directory information, delay retry attempts + by a random amount based on the "decorrelated jitter" algorithm. + Our previous delay algorithm tended to produce extra-long delays + too easily. Fixes bug 23816; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (directory protocol): + - Directory servers now include a "Date:" http header for response + codes other than 200. Clients starting with a skewed clock and a + recent consensus were getting "304 Not modified" responses from + directory authorities, so without the Date header, the client + would never hear about a wrong clock. Fixes bug 23499; bugfix + on 0.0.8rc1. + - Make clients wait for 6 seconds before trying to download a + consensus from an authority. Fixes bug 17750; bugfix + on 0.2.8.1-alpha. + + o Minor bugfixes (documentation): + - Document better how to read gcov, and what our gcov postprocessing + scripts do. Fixes bug 23739; bugfix on 0.2.9.1-alpha. + - Fix manpage to not refer to the obsolete (and misspelled) + UseEntryGuardsAsDirectoryGuards parameter in the description of + NumDirectoryGuards. Fixes bug 23611; bugfix on 0.2.4.8-alpha. + + o Minor bugfixes (DoS-resistance): + - If future code asks if there are any running bridges, without + checking if bridges are enabled, log a BUG warning rather than + crashing. Fixes bug 23524; bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (entry guards): + - Tor now updates its guard state when it reads a consensus + regardless of whether it's missing descriptors. That makes tor use + its primary guards to fetch descriptors in some edge cases where + it would previously have used fallback directories. Fixes bug + 23862; bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (format strictness): + - Restrict several data formats to decimal. Previously, the + BuildTimeHistogram entries in the state file, the "bw=" entries in + the bandwidth authority file, and the process IDs passed to the + __OwningControllerProcess option could all be specified in hex or + octal as well as in decimal. This was not an intentional feature. + Fixes bug 22802; bugfixes on 0.2.2.1-alpha, 0.2.2.2-alpha, + and 0.2.2.28-beta. + + o Minor bugfixes (heartbeat): + - If we fail to write a heartbeat message, schedule a retry for the + minimum heartbeat interval number of seconds in the future. Fixes + bug 19476; bugfix on 0.2.3.1-alpha. + + o Minor bugfixes (logging): + - Suppress a log notice when relay descriptors arrive. We already + have a bootstrap progress for this so no need to log notice + everytime tor receives relay descriptors. Microdescriptors behave + the same. Fixes bug 23861; bugfix on 0.2.8.2-alpha. + - Remove duplicate log messages regarding opening non-local + SocksPorts upon parsing config and opening listeners at startup. + Fixes bug 4019; bugfix on 0.2.3.3-alpha. + - Use a more comprehensible log message when telling the user + they've excluded every running exit node. Fixes bug 7890; bugfix + on 0.2.2.25-alpha. + - When logging the number of descriptors we intend to download per + directory request, do not log a number higher than then the number + of descriptors we're fetching in total. Fixes bug 19648; bugfix + on 0.1.1.8-alpha. + - When warning about a directory owned by the wrong user, log the + actual name of the user owning the directory. Previously, we'd log + the name of the process owner twice. Fixes bug 23487; bugfix + on 0.2.9.1-alpha. + - Fix some messages on unexpected errors from the seccomp2 library. + Fixes bug 22750; bugfix on 0.2.5.1-alpha. Patch from "cypherpunks". + - The tor specification says hop counts are 1-based, so fix two log + messages that mistakenly logged 0-based hop counts. Fixes bug + 18982; bugfix on 0.2.6.2-alpha and 0.2.4.5-alpha. Patch by teor. + Credit to Xiaofan Li for reporting this issue. + + o Minor bugfixes (logging, relay shutdown, annoyance): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (memory safety, defensive programming): + - Clear the target address when node_get_prim_orport() returns + early. Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (memory usage): + - When queuing DESTROY cells on a channel, only queue the circuit-id + and reason fields: not the entire 514-byte cell. This fix should + help mitigate any bugs or attacks that fill up these queues, and + free more RAM for other uses. Fixes bug 24666; bugfix + on 0.2.5.1-alpha. + + o Minor bugfixes (network layer): + - When closing a connection via close_connection_immediately(), we + mark it as "not blocked on bandwidth", to prevent later calls from + trying to unblock it, and give it permission to read. This fixes a + backtrace warning that can happen on relays under various + circumstances. Fixes bug 24167; bugfix on 0.1.0.1-rc. + + o Minor bugfixes (onion services): + - The introduction circuit was being timed out too quickly while + waiting for the rendezvous circuit to complete. Keep the intro + circuit around longer instead of timing out and reopening new ones + constantly. Fixes bug 23681; bugfix on 0.2.4.8-alpha. + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + - When handling multiple SOCKS request for the same .onion address, + only fetch the service descriptor once. + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + - When reloading configured onion services, copy all information + from the old service object. Previously, some data was omitted, + causing delays in descriptor upload, and other bugs. Fixes bug + 23790; bugfix on 0.2.1.9-alpha. + + o Minor bugfixes (path selection): + - When selecting relays by bandwidth, avoid a rounding error that + could sometimes cause load to be imbalanced incorrectly. + Previously, we would always round upwards; now, we round towards + the nearest integer. This had the biggest effect when a relay's + weight adjustments should have given it weight 0, but it got + weight 1 instead. Fixes bug 23318; bugfix on 0.2.4.3-alpha. + - When calculating the fraction of nodes that have descriptors, and + all nodes in the network have zero bandwidths, count the number of + nodes instead. Fixes bug 23318; bugfix on 0.2.4.10-alpha. + - Actually log the total bandwidth in compute_weighted_bandwidths(). + Fixes bug 24170; bugfix on 0.2.4.3-alpha. + + o Minor bugfixes (portability): + - Stop using the PATH_MAX variable, which is not defined on GNU + Hurd. Fixes bug 23098; bugfix on 0.3.1.1-alpha. + - Fix a bug in the bit-counting parts of our timing-wheel code on + MSVC. (Note that MSVC is still not a supported build platform, due + to cryptographic timing channel risks.) Fixes bug 24633; bugfix + on 0.2.9.1-alpha. + + o Minor bugfixes (relay): + - When uploading our descriptor for the first time after startup, + report the reason for uploading as "Tor just started" rather than + leaving it blank. Fixes bug 22885; bugfix on 0.2.3.4-alpha. + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing): + - Fix a spurious fuzzing-only use of an uninitialized value. Found + by Brian Carpenter. Fixes bug 24082; bugfix on 0.3.0.3-alpha. + - Test that IPv6-only clients can use microdescriptors when running + "make test-network-all". Requires chutney master 61c28b9 or later. + Closes ticket 24109. + - Prevent scripts/test/coverage from attempting to move gcov output + to the root directory. Fixes bug 23741; bugfix on 0.2.5.1-alpha. + - Capture and detect several "Result does not fit" warnings in unit + tests on platforms with 32-bit time_t. Fixes bug 21800; bugfix + on 0.2.9.3-alpha. + - Fix additional channelpadding unit test failures by using mocked + time instead of actual time for all tests. Fixes bug 23608; bugfix + on 0.3.1.1-alpha. + - Fix a bug in our fuzzing mock replacement for crypto_pk_checksig(), + to correctly handle cases where a caller gives it an RSA key of + under 160 bits. (This is not actually a bug in Tor itself, but + rather in our fuzzing code.) Fixes bug 24247; bugfix on + 0.3.0.3-alpha. Found by OSS-Fuzz as issue 4177. + - Fix a broken unit test for the OutboundAddress option: the parsing + function was never returning an error on failure. Fixes bug 23366; + bugfix on 0.3.0.3-alpha. + - Fix a signed-integer overflow in the unit tests for + dir/download_status_random_backoff, which was untriggered until we + fixed bug 17750. Fixes bug 22924; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (usability, control port): + - Stop making an unnecessary routerlist check in NETINFO clock skew + detection; this was preventing clients from reporting NETINFO clock + skew to controllers. Fixes bug 23532; bugfix on 0.2.4.4-alpha. + + o Code simplification and refactoring: + - Remove various ways of testing circuits and connections for + "clientness"; instead, favor channel_is_client(). Part of + ticket 22805. + - Extract the code for handling newly-open channels into a separate + function from the general code to handle channel state + transitions. This change simplifies our callgraph, reducing the + size of the largest strongly connected component by roughly a + factor of two. Closes ticket 22608. + - Remove dead code for largely unused statistics on the number of + times we've attempted various public key operations. Fixes bug + 19871; bugfix on 0.1.2.4-alpha. Fix by Isis Lovecruft. + - Remove several now-obsolete functions for asking about old + variants directory authority status. Closes ticket 22311; patch + from "huyvq". + - Remove some of the code that once supported "Named" and "Unnamed" + routers. Authorities no longer vote for these flags. Closes + ticket 22215. + - Rename the obsolete malleable hybrid_encrypt functions used in TAP + and old hidden services, to indicate that they aren't suitable for + new protocols or formats. Closes ticket 23026. + - Replace our STRUCT_OFFSET() macro with offsetof(). Closes ticket + 22521. Patch from Neel Chauhan. + - Split the enormous circuit_send_next_onion_skin() function into + multiple subfunctions. Closes ticket 22804. + - Split the portions of the buffer.c module that handle particular + protocols into separate modules. Part of ticket 23149. + - Use our test macros more consistently, to produce more useful + error messages when our unit tests fail. Add coccinelle patches to + allow us to re-check for test macro uses. Closes ticket 22497. + + o Deprecated features: + - The ReachableDirAddresses and ClientPreferIPv6DirPort options are + now deprecated; they do not apply to relays, and they have had no + effect on clients since 0.2.8.x. Closes ticket 19704. + - Deprecate HTTPProxy/HTTPProxyAuthenticator config options. They + only applies to direct unencrypted HTTP connections to your + directory server, which your Tor probably isn't using. Closes + ticket 20575. + + o Documentation: + - Add notes in man page regarding OS support for the various + scheduler types. Attempt to use less jargon in the scheduler + section. Closes ticket 24254. + - Clarify that the Address option is entirely about setting an + advertised IPv4 address. Closes ticket 18891. + - Clarify the manpage's use of the term "address" to clarify what + kind of address is intended. Closes ticket 21405. + - Document that onion service subdomains are allowed, and ignored. + Closes ticket 18736. + - Clarify in the manual that "Sandbox 1" is only supported on Linux + kernels. Closes ticket 22677. + - Document all values of PublishServerDescriptor in the manpage. + Closes ticket 15645. + - Improve the documentation for the directory port part of the + DirAuthority line. Closes ticket 20152. + - Restore documentation for the authorities' "approved-routers" + file. Closes ticket 21148. + + o Removed features: + - The AllowDotExit option has been removed as unsafe. It has been + deprecated since 0.2.9.2-alpha. Closes ticket 23426. + - The ClientDNSRejectInternalAddresses flag can no longer be set on + non-testing networks. It has been deprecated since 0.2.9.2-alpha. + Closes ticket 21031. + - The controller API no longer includes an AUTHDIR_NEWDESCS event: + nobody was using it any longer. Closes ticket 22377. + + +Changes in version 0.3.1.9 - 2017-12-01: + Tor 0.3.1.9 backports important security and stability fixes from the + 0.3.2 development series. All Tor users should upgrade to this + release, or to another of the releases coming out today. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (bridge): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (onion service, backport from 0.3.2.5-alpha): + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.0.13 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.9.14 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.8.17 - 2017-12-01 + Tor 0.2.8.17 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please upgrade with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path through + ourselves, even in the case where we have somehow lost the version of + our descriptor appearing in the consensus. Fixes part of bug 21534; + bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 + and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.5.16 - 2017-12-01 + Tor 0.2.5.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.5.15 - 2017-10-25 + Tor 0.2.5.15 backports a collection of bugfixes from later Tor release + series. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (openbsd, denial-of-service, backport from 0.3.1.5-alpha): + - Avoid an assertion failure bug affecting our implementation of + inet_pton(AF_INET6) on certain OpenBSD systems whose strtol() + handling of "0xx" differs from what we had expected. Fixes bug + 22789; bugfix on 0.2.3.8-alpha. Also tracked as TROVE-2017-007. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (defensive programming, undefined behavior, backport from 0.3.1.4-alpha): + - Fix a memset() off the end of an array when packing cells. This + bug should be harmless in practice, since the corrupted bytes are + still in the same structure, and are always padding bytes, + ignored, or immediately overwritten, depending on compiler + behavior. Nevertheless, because the memset()'s purpose is to make + sure that any other cell-handling bugs can't expose bytes to the + network, we need to fix it. Fixes bug 22737; bugfix on + 0.2.4.11-alpha. Fixes CID 1401591. + + o Build features (backport from 0.3.1.5-alpha): + - Tor's repository now includes a Travis Continuous Integration (CI) + configuration file (.travis.yml). This is meant to help new + developers and contributors who fork Tor to a Github repository be + better able to test their changes, and understand what we expect + to pass. To use this new build feature, you must fork Tor to your + Github account, then go into the "Integrations" menu in the + repository settings for your fork and enable Travis, then push + your changes. Closes ticket 22636. + + +Changes in version 0.2.8.16 - 2017-10-25 + Tor 0.2.8.16 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.9.13 - 2017-10-25 + Tor 0.2.9.13 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.0.12 - 2017-10-25 + Tor 0.3.0.12 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.1.8 - 2017-10-25 + Tor 0.3.1.8 is the second stable release in the 0.3.1 series. + It includes several bugfixes, including a bugfix for a crash issue + that had affected relays under memory pressure. It also adds + a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (compilation, backport from 0.3.2.2-alpha): + - Fix a compilation warning when building with zstd support on + 32-bit platforms. Fixes bug 23568; bugfix on 0.3.1.1-alpha. Found + and fixed by Andreas Stieger. + + o Minor bugfixes (compression, backport from 0.3.2.2-alpha): + - Handle a pathological case when decompressing Zstandard data when + the output buffer size is zero. Fixes bug 23551; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (directory authority, backport from 0.3.2.1-alpha): + - Remove the length limit on HTTP status lines that authorities can + send in their replies. Fixes bug 23499; bugfix on 0.3.1.6-rc. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (unit tests, backport from 0.3.2.2-alpha): + - Fix additional channelpadding unit test failures by using mocked + time instead of actual time for all tests. Fixes bug 23608; bugfix + on 0.3.1.1-alpha. + + Changes in version 0.2.8.15 - 2017-09-18 Tor 0.2.8.15 backports a collection of bugfixes from later Tor series. @@ -1345,7 +2718,7 @@ Changes in version 0.3.0.6 - 2017-04-26 initial code by Alec Heifetz. - Relays now support the HSDir version 3 protocol, so that they can can store and serve v3 descriptors. This is part of the next- - generation onion service work detailled in proposal 224. Closes + generation onion service work detailed in proposal 224. Closes ticket 17238. o Major features (protocol, ed25519 identity keys): @@ -5103,7 +6476,7 @@ Changes in version 0.2.6.6 - 2015-03-24 o Major bugfixes (pluggable transports): - Initialize the extended OR Port authentication cookie before launching pluggable transports. This prevents a race condition - that occured when server-side pluggable transports would cache the + that occurred when server-side pluggable transports would cache the authentication cookie before it has been (re)generated. Fixes bug 15240; bugfix on 0.2.5.1-alpha. @@ -5576,7 +6949,7 @@ Changes in version 0.2.6.6 - 2015-03-24 some bugs where we would look at (but fortunately, not reveal) uninitialized memory on the stack. Fixes bug 14013; bugfix on all versions of Tor. - - Clear all memory targetted by tor_addr_{to,from}_sockaddr(), not + - Clear all memory targeted by tor_addr_{to,from}_sockaddr(), not just the part that's used. This makes it harder for data leak bugs to occur in the event of other programming failures. Resolves ticket 14041. @@ -6753,7 +8126,7 @@ Changes in version 0.2.5.10 - 2014-10-24 from Arlo Breault. - Remove instances of strcpy() from the unit tests. They weren't hurting anything, since they were only in the unit tests, but it's - embarassing to have strcpy() in the code at all, and some analysis + embarrassing to have strcpy() in the code at all, and some analysis tools don't like it. Fixes bug 8790; bugfix on 0.2.3.6-alpha and 0.2.3.8-alpha. Patch from Arlo Breault. - Remove is_internal_IP() function. Resolves ticket 4645. @@ -6777,7 +8150,7 @@ Changes in version 0.2.5.10 - 2014-10-24 directory authority options, remove the documentation for a V2-directory fetching option that no longer exists. Resolves ticket 11634. - - Correct the documenation so that it lists the correct directory + - Correct the documentation so that it lists the correct directory for the stats files. (They are in a subdirectory called "stats", not "status".) - In the manpage, move more authority-only options into the @@ -8920,7 +10293,7 @@ Changes in version 0.2.3.25 - 2012-11-19 bugfix on 0.2.0.3-alpha. - The "--quiet" and "--hush" options now apply not only to Tor's behavior before logs are configured, but also to Tor's behavior in - the absense of configured logs. Fixes bug 3550; bugfix on + the absence of configured logs. Fixes bug 3550; bugfix on 0.2.0.10-alpha. - Change the AllowDotExit rules so they should actually work. We now enforce AllowDotExit only immediately after receiving an @@ -10033,7 +11406,7 @@ Changes in version 0.2.2.32 - 2011-08-27 algorithms for signatures and resource selection. Newer formats are signed with SHA256, with a possibility for moving to a better hash algorithm in the future. - - Directory authorities can now vote on arbitary integer values as + - Directory authorities can now vote on arbitrary integer values as part of the consensus process. This is designed to help set network-wide parameters. Implements proposal 167. @@ -15222,7 +16595,7 @@ Changes in version 0.1.0.10 - 2005-06-14 - Use correct errno on win32 if libevent fails. - Check and warn about known-bad/slow libevent versions. - Stop warning about sigpipes in the logs. We're going to - pretend that getting these occassionally is normal and fine. + pretend that getting these occasionally is normal and fine. o New contrib scripts: - New experimental script tor/contrib/exitlist: a simple python @@ -15341,7 +16714,7 @@ Changes in version 0.0.9.7 - 2005-04-01 Changes in version 0.0.9.6 - 2005-03-24 o Bugfixes on 0.0.9.x (crashes and asserts): - - Add new end stream reasons to maintainance branch. Fix bug where + - Add new end stream reasons to maintenance branch. Fix bug where reason (8) could trigger an assert. Prevent bug from recurring. - Apparently win32 stat wants paths to not end with a slash. - Fix assert triggers in assert_cpath_layer_ok(), where we were diff --git a/changes/bastet_v6 b/changes/bastet_v6 deleted file mode 100644 index ee4e2c8094..0000000000 --- a/changes/bastet_v6 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (directory authority): - - Add an IPv6 address for the "bastet" directory authority. - Closes ticket 24394. - diff --git a/changes/bug18859 b/changes/bug18859 deleted file mode 100644 index 1fe5bc2107..0000000000 --- a/changes/bug18859 +++ /dev/null @@ -1,7 +0,0 @@ - o Major bugfixes (circuit prediction): - - Fix circuit prediction logic so that a client doesn't treat a stream as - being "handled" by a circuit if that circuit already has isolation - settings on it that might make it incompatible with the stream. This - change should make Tor clients more responsive by improving their - chances of having a pre-created circuit ready for use when a new client - request arrives. Fixes bug 18859; bugfix on 0.2.3.3-alpha. diff --git a/changes/bug20532 b/changes/bug20532 deleted file mode 100644 index 7c190ea032..0000000000 --- a/changes/bug20532 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (bridges): - - Overwrite the bridge address earlier in the process of directly - retrieving its descriptor, to make sure we reach it on the configured - address. Fixes bug 20532; bugfix on 0.2.0.10-alpha. diff --git a/changes/bug20963 b/changes/bug20963 deleted file mode 100644 index a65c58399c..0000000000 --- a/changes/bug20963 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging): - - Improve the message we log when re-enabling circuit build timeouts - after having received a consensus. Closes ticket 20963. - diff --git a/changes/bug21074_downgrade b/changes/bug21074_downgrade deleted file mode 100644 index 1bc1f8523a..0000000000 --- a/changes/bug21074_downgrade +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (portability): - - Don't exit the Tor process if setrlimit() fails to change the file - limit (which can happen sometimes on some versions of OSX). Fixes - bug 21074; bugfix on 0.0.9pre5. diff --git a/changes/bug21394 b/changes/bug21394 deleted file mode 100644 index e5452e20ba..0000000000 --- a/changes/bug21394 +++ /dev/null @@ -1,9 +0,0 @@ - o Major bugfixes (Exit nodes): - - Fix an issue causing high-bandwidth exit nodes to fail a majority - or all of their DNS requests, making them basically unsuitable for - regular usage in Tor circuits. The problem is related to - libevent's DNS handling, but we can work around it in Tor. Fixes - bugs 21394 and 18580; bugfix on 0.1.2.2-alpha which introduced - eventdns. Credit goes to Dhalgren for identifying and finding a - workaround to this bug and to gamambel, arthuredelstein and - arma in helping to track it down and analyze it. diff --git a/changes/bug21509 b/changes/bug21509 deleted file mode 100644 index 593a01ef20..0000000000 --- a/changes/bug21509 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (hidden service v3, fuzzing): - - Fix the hidden service v3 descriptor decoding fuzzing to use the latest - decoding API correctly. Fixes bug 21509; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23318 b/changes/bug23318 deleted file mode 100644 index 7fcb8d4487..0000000000 --- a/changes/bug23318 +++ /dev/null @@ -1,11 +0,0 @@ - o Minor bugfixes (path selection): - - When selecting relays by bandwidth, avoid a rounding error that - could sometimes cause load to be imbalanced incorrectly. Previously, - we would always round upwards; now, we round towards the nearest - integer. This had the biggest effect when a relay's weight adjustments - should have given it weight 0, but it got weight 1 instead. - Fixes bug 23318; bugfix on 0.2.4.3-alpha. - - When calculating the fraction of nodes that have descriptors, and all - all nodes in the network have zero bandwidths, count the number of nodes - instead. - Fixes bug 23318; bugfix on 0.2.4.10-alpha. diff --git a/changes/bug23603 b/changes/bug23603 deleted file mode 100644 index dfb2052c9a..0000000000 --- a/changes/bug23603 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Fix a race between the circuit close and free where the service would - launch a new intro circuit after the close, and then fail to register it - before the free of the previously closed circuit. This was making the - service unable to find the established intro circuit and thus not upload - its descriptor. It can make a service unavailable for up to 24 hours. - Fixes bug 23603; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23623 b/changes/bug23623 deleted file mode 100644 index 1e2e5c2ac0..0000000000 --- a/changes/bug23623 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (onion services): - - Cache some needed onion service client information instead of - continuously computing it over and over again. Fixes bug 23623; bugfix - on 0.3.2.1-alpha. diff --git a/changes/bug23653 b/changes/bug23653 deleted file mode 100644 index 81760cbb82..0000000000 --- a/changes/bug23653 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (hidden service client): - - When getting multiple SOCKS request for the same .onion address, don't - trigger multiple descriptor fetches. - - When the descriptor fetch fails with an internal error, no more HSDir to - query or we aren't allowed to fetch (FetchHidServDescriptors 0), close - all pending SOCKS request for that .onion. Fixes bug 23653; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23662 b/changes/bug23662 deleted file mode 100644 index 1000bde3d6..0000000000 --- a/changes/bug23662 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (onion services): - - Silence a warning about failed v3 onion descriptor uploads since it can - happen naturally under certain edge-cases. Fixes part of bug 23662; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23670 b/changes/bug23670 deleted file mode 100644 index 039bc39478..0000000000 --- a/changes/bug23670 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (entry guards): - - Improve logs issued when we are missing descriptors of primary guards. - Resolves ticket 23670. diff --git a/changes/bug23678 b/changes/bug23678 deleted file mode 100644 index 8138ea71ea..0000000000 --- a/changes/bug23678 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (warnings): - - When we get an HTTP request on a SOCKS port, tell the user about - the new HTTPTunnelPort option. Previously, we would give a - "Tor is not an HTTP Proxy" message, which stopped being true when - HTTPTunnelPort was introduced. Fixes bug 23678; bugfix on - 0.3.2.1-alpha. - diff --git a/changes/bug23681 b/changes/bug23681 deleted file mode 100644 index e317f36d50..0000000000 --- a/changes/bug23681 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service client): - - The introduction circuit was being timed out too quickly while waiting - for the rendezvous circuit to complete. Keep the intro circuit around - longer instead of timing out and reopening new ones constantly. Fixes - bug 23681; bugfix on 0.2.4.8-alpha. diff --git a/changes/bug23693 b/changes/bug23693 deleted file mode 100644 index 796398be51..0000000000 --- a/changes/bug23693 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (relay, crash): - - Avoid a crash when transitioning from client mode to bridge mode. - Previously, we would launch the worker threads whenever our "public - server" mode changed, but not when our "server" mode changed. - Fixes bug 23693; bugfix on 0.2.6.3-alpha. - diff --git a/changes/bug23696 b/changes/bug23696 deleted file mode 100644 index c5d18583d4..0000000000 --- a/changes/bug23696 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfix (KIST scheduler): - - Downgrade a warning to log info when the monotonic time diff is - negative. This can happen on platform not supporting monotonic time. The - scheduler recovers from this without any problem. Fixes bug 23696; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23739 b/changes/bug23739 deleted file mode 100644 index 3207b5eaf3..0000000000 --- a/changes/bug23739 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (documentation): - - Document better how to read gcov and what our postprocessing scripts do. - Fixes bug 23739; bugfix on 0.2.9.1-alpha. diff --git a/changes/bug23741 b/changes/bug23741 deleted file mode 100644 index 92f06f5270..0000000000 --- a/changes/bug23741 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Prevent scripts/test/coverage from attempting to move gcov - output to the root directory. Fixes bug 23741; bugfix on - 0.2.5.1-alpha. diff --git a/changes/bug23748 b/changes/bug23748 deleted file mode 100644 index 0bd3f3f8ff..0000000000 --- a/changes/bug23748 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service): - - Always make sure the hidden service generate the public key file if it - is missing. Prior to this, if the public key was deleted from disk, it - wouldn't get recreated. Fixes bug 23748; bugfix on 0.3.2.2-alpha. - Patch from "cathugger". diff --git a/changes/bug23751 b/changes/bug23751 deleted file mode 100644 index 2fd7021664..0000000000 --- a/changes/bug23751 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (scheduler, channel): - - Ignore channels that have been closed while flushing cells. This can - happen if the write on the connection fails leading to the channel being - closed while in the scheduler loop. This is not a complete fix, it is a - bandaid until we are able to refactor those interactions. Fixes bug - 23751; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23753 b/changes/bug23753 deleted file mode 100644 index 8782a8e2d0..0000000000 --- a/changes/bug23753 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging, scheduler): - - Introduce a SCHED_BUG() function to log extra information about the - scheduler state if we ever catch a bug in the scheduler. Closes ticket - 23753. diff --git a/changes/bug23755 b/changes/bug23755 deleted file mode 100644 index 98f0970344..0000000000 --- a/changes/bug23755 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Stop unconditionally mirroring the tor repository in GitLab CI. - This prevented developers from enabling GitLab CI on master. - Fixes bug 23755; bugfix on 0.3.2.2-alpha. diff --git a/changes/bug23757 b/changes/bug23757 deleted file mode 100644 index 02507a0b4d..0000000000 --- a/changes/bug23757 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Adjust the GitLab CI configuration to more closely match that of Travis - CI. Fixes bug 23757; bugfix on 0.3.2.2-alpha. - diff --git a/changes/bug23758 b/changes/bug23758 deleted file mode 100644 index 565791e8f4..0000000000 --- a/changes/bug23758 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Skip a test that would fail if run as root (because it expects a - permissions error). This affects some continuous integration setups. - Fixes bug 23758; bugfix on 0.3.2.2-alpha. diff --git a/changes/bug23762 b/changes/bug23762 deleted file mode 100644 index 741a88e21f..0000000000 --- a/changes/bug23762 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Properly retry HSv3 descriptor fetches in the case where we were initially - missing required directory information. Fixes bug 23762; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23774 b/changes/bug23774 deleted file mode 100644 index 2ea5c0122a..0000000000 --- a/changes/bug23774 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (memory leak): - - Fix a minor memory-leak-at-exit in the KIST scheduler. This - bug should have no user-visible impact. Fixes bug 23774; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23783 b/changes/bug23783 deleted file mode 100644 index 98c583a12b..0000000000 --- a/changes/bug23783 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (compilation, windows): - - When detecting OpenSSL on Windows from our configure script, make sure - to try linking with the ws2_32 library. Fixes bug 23783; bugfix on - 0.3.2.2-alpha. - diff --git a/changes/bug23790 b/changes/bug23790 deleted file mode 100644 index 5ebe77f806..0000000000 --- a/changes/bug23790 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (hidden service v2): - - When reloading tor (HUP) configured with hidden service(s), some - information weren't copy to the new service object. One problem with - this was that tor would wait at least the RendPostPeriod time before - uploading the descriptor if the reload happened before the descriptor - needed to be published. Fixes bug 23790; bugfix on 0.2.1.9-alpha. diff --git a/changes/bug23816 b/changes/bug23816 deleted file mode 100644 index 6139dec9e8..0000000000 --- a/changes/bug23816 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (directory client): - - On failure to download directory information, delay retry attempts - by a random amount based on the "decorrelated jitter" algorithm. - Our previous delay algorithm tended to produce extra-long delays too - easily. Fixes bug 23816; bugfix on 0.2.9.1-alpha. - diff --git a/changes/bug23817 b/changes/bug23817 deleted file mode 100644 index 4740942799..0000000000 --- a/changes/bug23817 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (descriptors): - - Don't try fetching microdescriptors from relays that have failed to - deliver them in the past. Fixes bug 23817; bugfix on 0.3.0.1-alpha. diff --git a/changes/bug23820 b/changes/bug23820 deleted file mode 100644 index 4e920d0498..0000000000 --- a/changes/bug23820 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (IPv6, v3 single onion services): - - Remove buggy code for IPv6-only v3 single onion services, and reject - attempts to configure them. This release supports IPv4, dual-stack, and - IPv6-only v3 hidden services; and IPv4 and dual-stack v3 single onion - services. Fixes bug 23820; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23861 b/changes/bug23861 deleted file mode 100644 index c6f017640d..0000000000 --- a/changes/bug23861 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (logging, relay): - - Suppress a log notice when relay descriptors arrive. We already have a - bootstrap progress for this so no need to log notice everytime tor - receives relay descriptors. Microdescriptors behave the same. Fixes bug - 23861; bugfix on 0.2.8.2-alpha. diff --git a/changes/bug23862 b/changes/bug23862 deleted file mode 100644 index 301ce73672..0000000000 --- a/changes/bug23862 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (entry guards): - - Tor now updates its guard state when it reads a consensus regardless of - whether it's missing descriptors. That makes tor use its primary guards - to fetch descriptors in some edge cases where it would have used fallback - directories in the past. Fixes bug 23862; bugfix on 0.3.0.1-alpha.
\ No newline at end of file diff --git a/changes/bug23874 b/changes/bug23874 deleted file mode 100644 index bf6620553d..0000000000 --- a/changes/bug23874 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (memory safety): - - Clear the address when node_get_prim_orport() returns early. - Fixes bug 23874; bugfix on 0.2.8.2-alpha. diff --git a/changes/bug23952 b/changes/bug23952 deleted file mode 100644 index ab1462e522..0000000000 --- a/changes/bug23952 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (relay): - - Avoid a BUG warning when receiving a dubious CREATE cell while - an option transition is in progress. Fixes bug 23952; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23985 b/changes/bug23985 deleted file mode 100644 index 9cb5937962..0000000000 --- a/changes/bug23985 +++ /dev/null @@ -1,9 +0,0 @@ - o Minor bugfixes (bootstrapping): - - Fetch descriptors aggressively whenever we lack enough - to build circuits, regardless of how many descriptors we are missing. - Previously, we would delay launching the fetch when we had fewer than - 15 missing descriptors, even if some of those descriptors were - blocking circuits from building. Fixes bug 23985; bugfix on - 0.1.1.11-alpha. The effects of this bug became worse in 0.3.0.3-alpha, - when we began treating missing descriptors from our primary guards - as a reason to delay circuits. diff --git a/changes/bug24002 b/changes/bug24002 deleted file mode 100644 index cdb6081110..0000000000 --- a/changes/bug24002 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service): - - Make sure that we have a usable ed25519 key when the intro point relay - does support ed25519 link authentication. We do check for an empty key - when the relay does not support it so this makes it nice and symmetric. - Fixes bug 24002; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24025 b/changes/bug24025 deleted file mode 100644 index 1d7841af53..0000000000 --- a/changes/bug24025 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (logging, relay): - - Downgrade a warning to a protocol warning in the case the ed25519 key is - not consistent between the descriptor and micro descriptor of a relay. - This can happen for instance if the relay has been flagged - NoEdConsensus. Fixes bug 24025; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24050 b/changes/bug24050 deleted file mode 100644 index d184a77ac0..0000000000 --- a/changes/bug24050 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (client): - - By default, do not enable storage of client-side DNS values. - These values were unused by default previously, but they should - not have been cached at all. Fixes bug 24050; bugfix on - 0.2.6.3-alpha. diff --git a/changes/bug24082 b/changes/bug24082 deleted file mode 100644 index 1523239351..0000000000 --- a/changes/bug24082 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (testing): - - Fix a spurious fuzzing-only use of an uninitialized value. - Found by Brian Carpenter. Fixes bug 24082; bugfix on 0.3.0.3-alpha. diff --git a/changes/bug24086 b/changes/bug24086 deleted file mode 100644 index 2ae0b37e65..0000000000 --- a/changes/bug24086 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (directory cache): - - When a consensus diff calculation is only partially successful, only - record the successful parts as having succeeded. Partial success - can happen if (for example) one compression method fails but - the others succeed. Previously we misrecorded all the calculations as - having succeeded, which would later cause a nonfatal assertion failure. - Fixes bug 24086; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24099 b/changes/bug24099 deleted file mode 100644 index dca3992664..0000000000 --- a/changes/bug24099 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (directory cache): - - Recover better from empty or corrupt files in the consensus cache - directory. Fixes bug 24099; bugfix on 0.3.1.1-alpha. - diff --git a/changes/bug24115 b/changes/bug24115 deleted file mode 100644 index 767f13840b..0000000000 --- a/changes/bug24115 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (manpage, hidden service): - - Mention that the HiddenServiceNumIntroductionPoints option is 0-10 for - v2 service and 0-20 for v3 service. Fixes bug 24115; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug24150 b/changes/bug24150 deleted file mode 100644 index cfda7c40da..0000000000 --- a/changes/bug24150 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (v3 onion services): - - Fix a memory leak when decrypting a badly formatted v3 onion - service descriptor. Fixes bug 24150; bugfix on 0.3.2.1-alpha. - Found by OSS-Fuzz; this is OSS-Fuzz issue 3994. diff --git a/changes/bug24167 b/changes/bug24167 deleted file mode 100644 index fd0d87efff..0000000000 --- a/changes/bug24167 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (network layer): - - When closing a connection via close_connection_immediately(), we - mark it as "not blocked on bandwidth", to prevent later calls - from trying to unblock it, and give it permission to read. This - fixes a backtrace warning that can happen on relays under various - circumstances. Fixes bug 24167; bugfix on 0.1.0.1-rc. - diff --git a/changes/bug24170 b/changes/bug24170 deleted file mode 100644 index d3d7347693..0000000000 --- a/changes/bug24170 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (path selection): - - Actually log the total bandwidth in compute_weighted_bandwidths(). - Fixes bug 24170; bugfix on 0.2.4.3-alpha. diff --git a/changes/bug24198 b/changes/bug24198 deleted file mode 100644 index 6790706872..0000000000 --- a/changes/bug24198 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (controller, linux seccomp2 sandbox): - - Avoid a crash when attempting to use the seccomp2 sandbox - together with the OwningControllerProcess feature. - Fixes bug 24198; bugfix on 0.2.5.1-alpha. diff --git a/changes/bug24230 b/changes/bug24230 deleted file mode 100644 index b08c4cde24..0000000000 --- a/changes/bug24230 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (control port, hidden service): - - Control port was reporting the action "UPLOAD_FAILED" instead of - "FAILED" for the HS_DESC event when a service was not able to upload a - descriptor. Fixes bug 24230; bugfix on 0.2.7.1-alpha. diff --git a/changes/bug24247 b/changes/bug24247 deleted file mode 100644 index 1f4ddcdde2..0000000000 --- a/changes/bug24247 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (fuzzing): - - Fix a bug in our fuzzing mock replacement for crypto_pk_checksig(), to - correctly handle cases where a caller gives it an RSA key of under 160 - bits. (This is not actually a bug in Tor itself, but wrather in our - fuzzing code.) Fixes bug 24247; bugfix on 0.3.0.3-alpha. - Found by OSS-Fuzz as issue 4177. diff --git a/changes/bug24262 b/changes/bug24262 deleted file mode 100644 index eee69512e4..0000000000 --- a/changes/bug24262 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (hidden service): - - Fix the consensus parameter "hsdir-interval" to "hsdir_interval" so it - matches the dir-spec.txt. Fixes bug 24262; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24279 b/changes/bug24279 deleted file mode 100644 index ab2932b341..0000000000 --- a/changes/bug24279 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (compilation, hardening): - - Fix a memory leak warning in one of the libevent-related - configuration tests that could occur when manually specifying - -fsanitize=address. Fixes bug 24279; bugfix on 0.3.0.2-alpha. - Found and patched by Alex Xu. diff --git a/changes/bug24313 b/changes/bug24313 deleted file mode 100644 index b927ec3ba6..0000000000 --- a/changes/bug24313 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (security, hidden service v2): - - Fix a use-after-free error that could crash v2 Tor hidden services - when it failed to open circuits while expiring introductions - points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This - issue is also tracked as TROVE-2017-013 and CVE-2017-8823. diff --git a/changes/bug24345 b/changes/bug24345 deleted file mode 100644 index 22eb412514..0000000000 --- a/changes/bug24345 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (tests): - - Fix a unit test in one of the bridge-distribution test cases. - Fixes bug 24345; bugfix on 0.3.2.3-alpha. diff --git a/changes/bug24367 b/changes/bug24367 deleted file mode 100644 index 09ef3bb877..0000000000 --- a/changes/bug24367 +++ /dev/null @@ -1,13 +0,0 @@ - o Minor bugfixes (bridge clients, bootstrap): - - Retry directory downloads when we get our first bridge descriptor - during bootstrap or while reconnecting to the network. Keep retrying - every time we get a bridge descriptor, until we have a reachable bridge. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. - - Stop delaying bridge descriptor fetches when we have cached bridge - descriptors. Instead, only delay bridge descriptor fetches when we - have at least one reachable bridge. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. - - Stop delaying directory fetches when we have cached bridge descriptors. - Instead, only delay bridge descriptor fetches when all our bridges are - definitely unreachable. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. diff --git a/changes/bug24424 b/changes/bug24424 deleted file mode 100644 index 63c2d39ba1..0000000000 --- a/changes/bug24424 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (portability): - - Tor now compiles correctly on arm64 with libseccomp-dev installed. - (It doesn't yet work with the sandbox enabled.) Closes ticket 24424. diff --git a/changes/bug24480 b/changes/bug24480 deleted file mode 100644 index 94e5b91a0c..0000000000 --- a/changes/bug24480 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (compilation): - - Fix a signed/unsigned comparison warning introduced by our - fix to TROVE-2017-009. Fixes bug 24480; bugfix on 0.2.5.16. diff --git a/changes/bug24502 b/changes/bug24502 deleted file mode 100644 index 3fa6fb58dd..0000000000 --- a/changes/bug24502 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (scheduler): - - Properly set the scheduler state of an unopened channel in the KIST - scheduler main loop. This prevents a harmless but annoying log warning. - Fixes bug 24502; bugfix on 0.3.2.4-alpha. diff --git a/changes/bug24526 b/changes/bug24526 deleted file mode 100644 index 4d69defa9b..0000000000 --- a/changes/bug24526 +++ /dev/null @@ -1,4 +0,0 @@ - o Documentation: - - Document that operators who run more than one relay or bridge are - expected to set MyFamily and ContactInfo correctly. Closes ticket - 24526. diff --git a/changes/bug24590 b/changes/bug24590 deleted file mode 100644 index 77e039f8d2..0000000000 --- a/changes/bug24590 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (scheduler, KIST): - - Avoid a possible integer overflow when computing the available space on - the TCP buffer of a channel. This has no security implications but can - make KIST not behave properly by allowing more cells on a already - saturated connection. Fixes bug 24590; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24633 b/changes/bug24633 deleted file mode 100644 index 028c7cc143..0000000000 --- a/changes/bug24633 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (portability, msvc): - - Fix a bug in the bit-counting parts of our timing-wheel code on - MSVC. (Note that MSVC is still not a supported build platform, - due to cyptographic timing channel risks.) Fixes bug 24633; - bugfix on 0.2.9.1-alpha. diff --git a/changes/bug24634 b/changes/bug24634 deleted file mode 100644 index ac82b94fbb..0000000000 --- a/changes/bug24634 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (compilation): - - Resolve a few shadowed-variable warnings in the onion service code. - Fixes bug 24634; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24652 b/changes/bug24652 deleted file mode 100644 index 6e35e259e9..0000000000 --- a/changes/bug24652 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (build, compatibility, rust, OSX): - - - When building with Rust on OSX, link against libresolv, to - work around the issue at - https://github.com/rust-lang/rust/issues/46797. Fixes bug - 24652; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24665 b/changes/bug24665 deleted file mode 100644 index f950d9dd01..0000000000 --- a/changes/bug24665 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (KIST, scheduler): - - The KIST scheduler did not correctly account for data already enqueued - in each connection's send socket buffer, particularly in cases when the - TCP/IP congestion window was reduced between scheduler calls. This - situation lead to excessive per-connection buffering in the kernel, and - a potential memory DoS. Fixes bug 24665; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24666 b/changes/bug24666 deleted file mode 100644 index 830775f5f6..0000000000 --- a/changes/bug24666 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (memory usage): - - - When queuing DESTROY cells on a channel, only queue the - circuit-id and reason fields: not the entire 514-byte - cell. This fix should help mitigate any bugs or attacks that - fill up these queues, and free more RAM for other uses. Fixes - bug 24666; bugfix on 0.2.5.1-alpha. diff --git a/changes/bug24671 b/changes/bug24671 deleted file mode 100644 index 34d09e704d..0000000000 --- a/changes/bug24671 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (scheduler, KIST): - - Use a sane write limit for KISTLite when writing onto a connection - buffer instead of using INT_MAX and shoving as much as it can. Because - the OOM handler cleans up circuit queues, we are better off at keeping - them in that queue instead of the connection's buffer. Fixes bug 24671; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24700 b/changes/bug24700 deleted file mode 100644 index 74dc581a0b..0000000000 --- a/changes/bug24700 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (scheduler, KIST): - - Avoid adding the same channel twice in the KIST scheduler pending list - wasting CPU cycles at handling the same channel twice. Fixes bug 24700; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24736 b/changes/bug24736 deleted file mode 100644 index 632560932a..0000000000 --- a/changes/bug24736 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (address selection): - - When the fascist_firewall_choose_address_ functions don't find a - reachable address, set the returned address to the null address and port. - This is a precautionary measure, because some callers do not check the - return value. - Fixes bug 24736; bugfix on 0.2.8.2-alpha. diff --git a/changes/bug24769 b/changes/bug24769 new file mode 100644 index 0000000000..2893e0ff07 --- /dev/null +++ b/changes/bug24769 @@ -0,0 +1,7 @@ + o Minor bugfixes (performance): + - Reduce the number of circuits that can be opened at once during the + circuit build timeout phase. This is done by increasing the idle timeout + to 3 minutes, and lowering the maximum number of concurrent learning + circuits to 10. Fixes bug 24769; bugfix on 0.3.1.1-alpha. + + diff --git a/changes/bug24826_031 b/changes/bug24826_031 deleted file mode 100644 index 3d4a66184a..0000000000 --- a/changes/bug24826_031 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (performance, fragile-hardening): - - Improve the performance of our consensus-diff application code when Tor - is built with the --enable-fragile-hardening option set. Fixes bug - 24826; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24859 b/changes/bug24859 deleted file mode 100644 index 122109d650..0000000000 --- a/changes/bug24859 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (logging): - - Don't treat inability to store a cached consensus object as a - bug: it can happen normally when we are out of disk space. - Fixes bug 24859; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24894 b/changes/bug24894 deleted file mode 100644 index b08cdce1f0..0000000000 --- a/changes/bug24894 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (v3 onion services): - - New-style (v3) onion services now obey the "max rendezvous circuit - attempts" logic. Previously they would make as many rendezvous - circuit attempts as they could fit in the MAX_REND_TIMEOUT second - window before giving up. Fixes bug 24894; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24895 b/changes/bug24895 deleted file mode 100644 index 7edde94a0b..0000000000 --- a/changes/bug24895 +++ /dev/null @@ -1,8 +0,0 @@ - o Major bugfixes (onion services): - - Fix an "off by 2" error in counting rendezvous failures on the onion - service side. While we thought we would stop the rendezvous attempt - after one failed circuit, we were actually making three circuit attempts - before giving up. Now switch to a default of 2, and allow the consensus - parameter "hs_service_max_rdv_failures" to override. Fixes bug 24895; - bugfix on 0.0.6. - diff --git a/changes/bug24898 b/changes/bug24898 deleted file mode 100644 index f64340d71b..0000000000 --- a/changes/bug24898 +++ /dev/null @@ -1,8 +0,0 @@ - o Major bugfixes (relays): - - Fix a set of false positives where relays would consider connections - to other relays as being client-only connections (and thus e.g. - deserving different link padding schemes) if those relays fell out - of the consensus briefly. Now we look only at the initial handshake - and whether the connection authenticated as a relay. Fixes bug - 24898; bugfix on 0.3.1.1-alpha. - diff --git a/changes/bug24952 b/changes/bug24952 deleted file mode 100644 index 93174c04f5..0000000000 --- a/changes/bug24952 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfix (channel connection): - - The accurate address of a connection is real_addr, not the addr member. - TLS Channel remote address is now real_addr content instead of addr - member. Fixes bug 24952; bugfix on 707c1e2e26 in 0.2.4.11-alpha. - Patch by "ffmancera". diff --git a/changes/bug24972 b/changes/bug24972 deleted file mode 100644 index 5adf970abf..0000000000 --- a/changes/bug24972 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging, diagnostic): - - When logging a failure to check a hidden service's certificate, - also log what the problem with the certificate was. Diagnostic - for ticket 24972. diff --git a/changes/bug24975 b/changes/bug24975 deleted file mode 100644 index 32a5dfc929..0000000000 --- a/changes/bug24975 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (scheduler, consensus): - - A logic in the code was preventing the scheduler subystem to properly - make a decision based on the latest consensus when it arrives. This lead - to the scheduler failing to notice any consensus parameters that might - have changed between consensuses. Fixes bug 24975; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug24976 b/changes/bug24976 deleted file mode 100644 index 9c3be86eab..0000000000 --- a/changes/bug24976 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service v3 client): - - Remove a BUG() statement which can be triggered in normal circumstances - where a client fetches a descriptor that has a lower revision counter - than the one in its cache. This can happen due to HSDir desync. Fixes - bug 24976; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug25005 b/changes/bug25005 deleted file mode 100644 index dedf283aa9..0000000000 --- a/changes/bug25005 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (unit tests): - - Fix a memory leak in the scheduler/loop_kist unit test. Fixes bug - 25005; bugfix on 0.3.2.7-rc. - diff --git a/changes/bug25070 b/changes/bug25070 deleted file mode 100644 index c2f4e58c45..0000000000 --- a/changes/bug25070 +++ /dev/null @@ -1,3 +0,0 @@ - o Major bugfixes (protocol versions): - - Add Link protocol version 5 to the supported protocols list. - Fixes bug 25070; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug25105 b/changes/bug25105 deleted file mode 100644 index 36d1a5f16f..0000000000 --- a/changes/bug25105 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (v3 onion services): - - Look at the "HSRend" protocol version, not the "HSDir" protocol - version, when deciding whether a consensus entry can support - the v3 onion service protocol as a rendezvous point. - Fixes bug 25105; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug25120 b/changes/bug25120 new file mode 100644 index 0000000000..7215756ef3 --- /dev/null +++ b/changes/bug25120 @@ -0,0 +1,4 @@ + o Minor features (logging): + - Clarify the log messages produced when getrandom() or a related + entropy-generation mechanism gives an error. Closes ticket + 25120. diff --git a/changes/feature18329 b/changes/feature18329 deleted file mode 100644 index 1dabf50244..0000000000 --- a/changes/feature18329 +++ /dev/null @@ -1,9 +0,0 @@ - o Minor features (bridge): - - Bridge relays can now set the BridgeDistribution config option to - add a "bridge-distribution-request" line to their bridge descriptor, - which tells BridgeDB how they'd like their bridge address to be - given out. (Note that as of Oct 2017, BridgeDB does not yet implement - this feature.) As a side benefit, this feature provides a way - to distinguish bridge descriptors from non-bridge descriptors. - Implements tickets 18329. - diff --git a/changes/geoip-2017-11-06 b/changes/geoip-2017-11-06 deleted file mode 100644 index f034be9006..0000000000 --- a/changes/geoip-2017-11-06 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-2017-12-06 b/changes/geoip-2017-12-06 deleted file mode 100644 index ae4fb1149f..0000000000 --- a/changes/geoip-2017-12-06 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the December 6 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-2018-01-05 b/changes/geoip-2018-01-05 deleted file mode 100644 index 59aba02d09..0000000000 --- a/changes/geoip-2018-01-05 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the January 5 2018 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-2018-02-07 b/changes/geoip-2018-02-07 deleted file mode 100644 index f45228fd76..0000000000 --- a/changes/geoip-2018-02-07 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the February 7 2018 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-october2017 b/changes/geoip-october2017 deleted file mode 100644 index 11f623e85f..0000000000 --- a/changes/geoip-october2017 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/hsdescv3_fuzz_more b/changes/hsdescv3_fuzz_more deleted file mode 100644 index 25626bb9a4..0000000000 --- a/changes/hsdescv3_fuzz_more +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (testing): - - Our fuzzing tests now test the encrypted portions of the - v3 hidden service descriptors. Implements more of 21509. diff --git a/changes/longclaw_23592 b/changes/longclaw_23592 deleted file mode 100644 index 91e2da8972..0000000000 --- a/changes/longclaw_23592 +++ /dev/null @@ -1,3 +0,0 @@ - o Directory authority changes: - - The directory authority "Longclaw" has changed its IP address. - Closes ticket 23592. diff --git a/changes/stack b/changes/stack deleted file mode 100644 index ffdf536cb9..0000000000 --- a/changes/stack +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (correctness): - - Fix several places in our codebase where a C compiler would be likely - to eliminate a check, based on assuming that undefined behavior had not - happened elsewhere in the code. These cases are usually a sign of - redundant checking, or dubious arithmetic. Found by Georg Koppen using - the "STACK" tool from Wang, Zeldovich, Kaashoek, and - Solar-Lezama. Fixes bug 24423; bugfix on various Tor versions. diff --git a/changes/ticket21031 b/changes/ticket21031 deleted file mode 100644 index b081fb018f..0000000000 --- a/changes/ticket21031 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor features (removed deprecations): - - The ClientDNSRejectInternalAddresses flag can once again be set in - non-testing Tor networks, so long as they do not use the default - directory authorities. - This change also removes the deprecation of this - flag in 0.2.9.2-alpha. Closes ticket 21031. - diff --git a/changes/ticket23637 b/changes/ticket23637 deleted file mode 100644 index 0c524f34c3..0000000000 --- a/changes/ticket23637 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor features (directory authority): - - Make the "Exit" flag assignment only depend on whether the exit - policy allows connections to ports 80 and 443. Previously relays - would get the Exit flag if they allowed connections to one of - these ports and also port 6667. Resolves ticket 23637. diff --git a/changes/ticket23856 b/changes/ticket23856 deleted file mode 100644 index 049da18d06..0000000000 --- a/changes/ticket23856 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor feature (relay statistics): - - Change relay bandwidth reporting stats interval from 4 hours to 24 hours - in order to reduce the efficiency of guard discovery attacks. Fixes - ticket 23856. diff --git a/changes/ticket23910 b/changes/ticket23910 deleted file mode 100644 index eb38fcf32f..0000000000 --- a/changes/ticket23910 +++ /dev/null @@ -1,3 +0,0 @@ - o Directory authority changes: - - Add bastet as a ninth directory authority to the default list. Closes - ticket 23910. diff --git a/changes/ticket24097 b/changes/ticket24097 deleted file mode 100644 index 36547a8ddb..0000000000 --- a/changes/ticket24097 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging): - - Downgrade a pair of log messages that could occur when an exit's - resolver gave us an unusual (but not forbidden) response. - Closes ticket 24097. diff --git a/changes/ticket24109 b/changes/ticket24109 deleted file mode 100644 index f66271817d..0000000000 --- a/changes/ticket24109 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (integration tests): - - Test that IPv6-only clients can use microdescriptors when running - "make test-network-all". Requires chutney master 61c28b9 or later. - Closes ticket 24109. diff --git a/changes/ticket24158 b/changes/ticket24158 deleted file mode 100644 index 3cdc06afae..0000000000 --- a/changes/ticket24158 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (logging): - Only log about no longer having KIST support once. Fixes bug 24158; bugfix - on 0.3.2 diff --git a/changes/ticket24254 b/changes/ticket24254 deleted file mode 100644 index 98d5d6bacd..0000000000 --- a/changes/ticket24254 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation: - Add notes in man page regarding OS support for the various scheduler types. - Attempt to use less jargon in the scheduler section. Closes ticket 24254. diff --git a/changes/ticket24315 b/changes/ticket24315 deleted file mode 100644 index df34dbf412..0000000000 --- a/changes/ticket24315 +++ /dev/null @@ -1,3 +0,0 @@ - o Major features (linux seccomp2 sandbox): - - Update the sandbox rules so that they should now work correctly with - Glibc 2.26. Closes ticket 24315. diff --git a/changes/ticket24343 b/changes/ticket24343 new file mode 100644 index 0000000000..e62d65eb54 --- /dev/null +++ b/changes/ticket24343 @@ -0,0 +1,6 @@ + o Minor bugfixes (man page, SocksPort): + - Remove dead code about the old "SocksSockets" option. To do so, the + SocksSocketsGroupWritable option has been renamed to + UnixSockssGroupWritable which does the same exact thing. The old option + is still usable but will warn that it is deprecated. Fixes bug 24343; + bugfix on 0.2.6.3. diff --git a/changes/ticket24425 b/changes/ticket24425 deleted file mode 100644 index aa6f082bcc..0000000000 --- a/changes/ticket24425 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Bump hsdir_spread_store parameter from 3 to 4 in order to increase the - probability of reaching a service for a client missing microdescriptors. - Fixes bug 24425; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket24500 b/changes/ticket24500 deleted file mode 100644 index b49b7a5551..0000000000 --- a/changes/ticket24500 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (logging): - - Provide better warnings when the getrandom() syscall fails. - Closes ticket 24500. diff --git a/changes/ticket24681 b/changes/ticket24681 deleted file mode 100644 index cc0a42b2e0..0000000000 --- a/changes/ticket24681 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor features (fallback directory mirrors): - - Make the default DirAuthorityFallbackRate 0.1, so that clients on the - public tor network prefer to bootstrap off fallback directory mirrors. - This is a follow-up to 24679, which removed weights from the default - fallbacks. - Implements ticket 24681. diff --git a/changes/ticket24902 b/changes/ticket24902 deleted file mode 100644 index 1a2ef95cc9..0000000000 --- a/changes/ticket24902 +++ /dev/null @@ -1,13 +0,0 @@ - o Major features (denial of service mitigation): - - Give relays some defenses against the recent network overload. We start - with three defenses (default parameters in parentheses). First: if a - single client address makes too many concurrent connections (>100), hang - up on further connections. Second: if a single client address makes - circuits too quickly (more than 3 per second, with an allowed burst of - 90) while also having too many connections open (3), refuse new create - cells for the next while (1-2 hours). Third: if a client asks to - establish a rendezvous point to you directly, ignore the request. These - defenses can be manually controlled by new torrc options, but relays - will also take guidance from consensus parameters, so there's no need to - configure anything manually. Implements ticket 24902. - diff --git a/changes/ticket25071 b/changes/ticket25071 new file mode 100644 index 0000000000..5e2917e10b --- /dev/null +++ b/changes/ticket25071 @@ -0,0 +1,4 @@ + o Minor features (testing): + - Add a "make test-rust" target to run the rust tests only. + Closes ticket 25071. + diff --git a/changes/ticket25122 b/changes/ticket25122 deleted file mode 100644 index 2921811b22..0000000000 --- a/changes/ticket25122 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor feature (geoip cache): - - Make our OOM handler aware of the geoip client history cache so it - doesn't fill up the memory which is especially important for IPv6 and - our DoS mitigation subsystem. Closes ticket 25122. diff --git a/changes/ticket25170 b/changes/ticket25170 deleted file mode 100644 index 0652139400..0000000000 --- a/changes/ticket25170 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfix (directory authority, documentation): - - When a fingerprint or network address is marked as rejected, the - returned message by the authority now explicitly mention to set a valid - ContactInfo address and contact the bad-relays@ mailing list. Fixes bug - 25170; bugfix on 0.2.9.1. diff --git a/changes/ticket_24801 b/changes/ticket_24801 deleted file mode 100644 index f5f6c831af..0000000000 --- a/changes/ticket_24801 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor features (new fallback directories): - - The fallback directory list has been re-generated based on the - current status of the network. Tor uses fallback directories to - bootstrap it doesn't yet have up-to-date directory - information. Closes ticket 24801. diff --git a/changes/trove-2017-009 b/changes/trove-2017-009 deleted file mode 100644 index 166a5faec6..0000000000 --- a/changes/trove-2017-009 +++ /dev/null @@ -1,10 +0,0 @@ - o Major bugfixes (security): - - When checking for replays in the INTRODUCE1 cell data for a (legacy) - hiddden service, correctly detect replays in the RSA-encrypted part of - the cell. We were previously checking for replays on the entire cell, - but those can be circumvented due to the malleability of Tor's legacy - hybrid encryption. This fix helps prevent a traffic confirmation - attack. Fixes bug 24244; bugfix on 0.2.4.1-alpha. This issue is also - tracked as TROVE-2017-009 and CVE-2017-8819. - - diff --git a/changes/trove-2017-010 b/changes/trove-2017-010 deleted file mode 100644 index d5bf9333da..0000000000 --- a/changes/trove-2017-010 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (security): - - Fix a denial-of-service issue where an attacker could crash - a directory authority using a malformed router descriptor. - Fixes bug 24245; bugfix on 0.2.9.4-alpha. Also tracked - as TROVE-2017-010 and CVE-2017-8820. - diff --git a/changes/trove-2017-011 b/changes/trove-2017-011 deleted file mode 100644 index 82d20d9e78..0000000000 --- a/changes/trove-2017-011 +++ /dev/null @@ -1,8 +0,0 @@ - o Major bugfixes (security): - - Fix a denial of service bug where an attacker could use a malformed - directory object to cause a Tor instance to pause while OpenSSL would - try to read a passphrase from the terminal. (If the terminal was not - available, tor would continue running.) Fixes bug 24246; bugfix on - every version of Tor. Also tracked as TROVE-2017-011 and - CVE-2017-8821. Found by OSS-Fuzz as testcase 6360145429790720. - diff --git a/changes/trove-2017-012-part1 b/changes/trove-2017-012-part1 deleted file mode 100644 index 9fccc2cf65..0000000000 --- a/changes/trove-2017-012-part1 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (security, relay): - - When running as a relay, make sure that we never build a path through - ourselves, even in the case where we have somehow lost the version of - our descriptor appearing in the consensus. Fixes part of bug 21534; - bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 - and CVE-2017-8822. diff --git a/changes/trove-2017-012-part2 b/changes/trove-2017-012-part2 deleted file mode 100644 index ed994c5b02..0000000000 --- a/changes/trove-2017-012-part2 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (security, relay): - - When running as a relay, make sure that we never ever choose ourselves - as a guard. Previously, this was possible. Fixes part of bug 21534; - bugfix on 0.3.0.1-alpha. This issue is also tracked as TROVE-2017-012 - and CVE-2017-8822. diff --git a/configure.ac b/configure.ac index 0fdb84c131..1e27dfa563 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2017, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.3.2.9-dev]) +AC_INIT([tor],[0.3.3.2-alpha-dev]) AC_CONFIG_SRCDIR([src/or/main.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -59,6 +59,8 @@ AC_ARG_ENABLE(rust, AS_HELP_STRING(--enable-rust, [enable rust integration])) AC_ARG_ENABLE(cargo-online-mode, AS_HELP_STRING(--enable-cargo-online-mode, [Allow cargo to make network requests to fetch crates. For builds with rust only.])) +AC_ARG_ENABLE(restart-debugging, + AS_HELP_STRING(--enable-restart-debugging, [Build Tor with support for debugging in-process restart. Developers only.])) if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then AC_MSG_ERROR([Can't disable assertions outside of coverage build]) @@ -107,6 +109,10 @@ AC_ARG_ENABLE(systemd, * ) AC_MSG_ERROR(bad value for --enable-systemd) ;; esac], [systemd=auto]) +if test "$enable_restart_debugging" = "yes"; then + AC_DEFINE(ENABLE_RESTART_DEBUGGING, 1, + [Defined if we're building with support for in-process restart debugging.]) +fi # systemd support @@ -204,6 +210,20 @@ if test x$enable_event_tracing_debug = xyes; then AC_DEFINE([TOR_EVENT_TRACING_ENABLED], [1], [Compile the event tracing instrumentation]) fi +dnl Enable Android only features. +AC_ARG_ENABLE(android, + AS_HELP_STRING(--enable-android, [build with Android features enabled])) +AM_CONDITIONAL([USE_ANDROID], [test "x$enable_android" = "xyes"]) + +if test "x$enable_android" = "xyes"; then + AC_DEFINE([USE_ANDROID], [1], [Compile with Android specific features enabled]) + + dnl Check if the Android log library is available. + AC_CHECK_HEADERS([android/log.h]) + AC_SEARCH_LIBS(__android_log_write, [log]) + +fi + dnl check for the correct "ar" when cross-compiling. dnl (AM_PROG_AR was new in automake 1.11.2, which we do not yet require, dnl so kludge up a replacement for the case where it isn't there yet.) @@ -375,6 +395,7 @@ AH_BOTTOM([ AM_CONDITIONAL(BUILD_NT_SERVICES, test "x$bwin32" = "xtrue") +AM_CONDITIONAL(BUILD_LIBTORRUNNER, test "x$bwin32" != "xtrue") dnl Enable C99 when compiling with MIPSpro AC_MSG_CHECKING([for MIPSpro compiler]) @@ -417,22 +438,27 @@ if test "x$enable_rust" = "xyes"; then dnl When we're not allowed to touch the network, we need crate dependencies dnl locally available. AC_MSG_CHECKING([rust crate dependencies]) - AC_ARG_VAR([RUST_DEPENDENCIES], [path to directory with local crate mirror]) - if test "x$RUST_DEPENDENCIES" = "x"; then - RUST_DEPENDENCIES="$srcdir/src/ext/rust/" + AC_ARG_VAR([TOR_RUST_DEPENDENCIES], [path to directory with local crate mirror]) + if test "x$TOR_RUST_DEPENDENCIES" = "x"; then + TOR_RUST_DEPENDENCIES="$srcdir/src/ext/rust/" NEED_MOD=1 fi - if test ! -d "$RUST_DEPENDENCIES"; then - AC_MSG_ERROR([Rust dependency directory $RUST_DEPENDENCIES does not exist. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + if test ! -d "$TOR_RUST_DEPENDENCIES"; then + AC_MSG_ERROR([Rust dependency directory $TOR_RUST_DEPENDENCIES does not exist. Specify a dependency directory using the TOR_RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + ERRORED=1 fi for dep in $rust_crates; do - if test ! -d "$RUST_DEPENDENCIES"/"$dep"; then - AC_MSG_ERROR([Failure to find rust dependency $RUST_DEPENDENCIES/$dep. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + if test ! -d "$TOR_RUST_DEPENDENCIES"/"$dep"; then + AC_MSG_ERROR([Failure to find rust dependency $TOR_RUST_DEPENDENCIES/$dep. Specify a dependency directory using the TOR_RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + ERRORED=1 fi done if test "x$NEED_MOD" = "x1"; then dnl When looking for dependencies from cargo, pick right directory - RUST_DEPENDENCIES="../../src/ext/rust" + TOR_RUST_DEPENDENCIES="../../src/ext/rust" + fi + if test "x$ERRORED" = "x"; then + AC_MSG_RESULT([yes]) fi fi @@ -448,17 +474,18 @@ if test "x$enable_rust" = "xyes"; then dnl For now both MSVC and MinGW rust libraries will output static libs with dnl the MSVC naming convention. if test "$bwin32" = "true"; then - TOR_RUST_UTIL_STATIC_NAME=tor_util.lib + TOR_RUST_STATIC_NAME=tor_rust.lib else - TOR_RUST_UTIL_STATIC_NAME=libtor_util.a + TOR_RUST_STATIC_NAME=libtor_rust.a fi - AC_SUBST(TOR_RUST_UTIL_STATIC_NAME) + AC_SUBST(TOR_RUST_STATIC_NAME) AC_SUBST(CARGO_ONLINE) AC_SUBST(RUST_DL) dnl Let's check the rustc version, too AC_MSG_CHECKING([rust version]) + RUSTC_VERSION=`$RUSTC --version` RUSTC_VERSION_MAJOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 1` RUSTC_VERSION_MINOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 2` if test "x$RUSTC_VERSION_MAJOR" = "x" -o "x$RUSTC_VERSION_MINOR" = "x"; then @@ -467,6 +494,7 @@ if test "x$enable_rust" = "xyes"; then if test "$RUSTC_VERSION_MAJOR" -lt 2 -a "$RUSTC_VERSION_MINOR" -lt 14; then AC_MSG_ERROR([rustc must be at least version 1.14]) fi + AC_MSG_RESULT([$RUSTC_VERSION]) fi AC_SUBST(TOR_RUST_EXTRA_LIBS) @@ -516,6 +544,7 @@ AC_CHECK_FUNCS( llround \ localtime_r \ lround \ + mach_approximate_time \ memmem \ memset_s \ pipe \ @@ -544,7 +573,7 @@ AC_CHECK_FUNCS( # Apple messed up when they added two functions functions in Sierra: they # forgot to decorate them with appropriate AVAILABLE_MAC_OS_VERSION # checks. So we should only probe for those functions if we are sure that we -# are not targetting OSX 10.11 or earlier. +# are not targeting OSX 10.11 or earlier. AC_MSG_CHECKING([for a pre-Sierra OSX build target]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #ifdef __APPLE__ @@ -739,7 +768,7 @@ TOR_SEARCH_LIBRARY(openssl, $tryssldir, [-lssl -lcrypto $TOR_LIB_GDI $TOR_LIB_WS [#include <openssl/ssl.h>], [struct ssl_method_st; const struct ssl_method_st *TLSv1_1_method(void);], [TLSv1_1_method();], [], - [/usr/local/opt/openssl /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/athena /opt/openssl]) + [/usr/local/opt/openssl /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /opt/openssl]) dnl XXXX check for OPENSSL_VERSION_NUMBER == SSLeay() @@ -997,12 +1026,12 @@ if test "$fragile_hardening" = "yes"; then TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=address], also_link, CFLAGS_ASAN="-fsanitize=address", true) if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then - AC_MSG_ERROR([The compiler supports -fsanitize=address, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libubsan.so, and with Clang you need libclang_rt.ubsan*]) + AC_MSG_ERROR([The compiler supports -fsanitize=address, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libubsan.*, and with Clang you need libclang_rt.ubsan*]) fi TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=undefined], also_link, CFLAGS_UBSAN="-fsanitize=undefined", true) if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then - AC_MSG_ERROR([The compiler supports -fsanitize=undefined, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libasan.so, and with Clang you need libclang_rt.ubsan*]) + AC_MSG_ERROR([The compiler supports -fsanitize=undefined, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libasan.*, and with Clang you need libclang_rt.ubsan*]) fi TOR_CHECK_CFLAGS([-fno-omit-frame-pointer]) @@ -1243,6 +1272,7 @@ AC_CHECK_HEADERS([assert.h \ pwd.h \ readpassphrase.h \ stdint.h \ + stdatomic.h \ sys/eventfd.h \ sys/file.h \ sys/ioctl.h \ @@ -2000,7 +2030,6 @@ if test "x$enable_gcc_warnings_advisory" != "xno"; then -Winvalid-source-encoding -Winvalid-token-paste -Wknr-promoted-parameter - -Wlanguage-extension-token -Wlarge-by-value-copy -Wliteral-conversion -Wliteral-range @@ -2026,7 +2055,7 @@ if test "x$enable_gcc_warnings_advisory" != "xno"; then -Wnon-literal-null-conversion -Wnon-pod-varargs -Wnonportable-cfstrings - -Wnormalized=id + -Wnormalized=nfkc -Wnull-arithmetic -Wnull-character -Wnull-conversion diff --git a/contrib/operator-tools/linux-tor-prio.sh b/contrib/operator-tools/linux-tor-prio.sh index ea9e0ddaa5..30ea5fc659 100644 --- a/contrib/operator-tools/linux-tor-prio.sh +++ b/contrib/operator-tools/linux-tor-prio.sh @@ -87,7 +87,7 @@ RATE_UP=5000 # machine does any other network activity. That is not very fun. RATE_UP_TOR=1500 -# RATE_UP_TOR_CEIL is the maximum rate allowed for all Tor trafic in +# RATE_UP_TOR_CEIL is the maximum rate allowed for all Tor traffic in # kbits/sec. RATE_UP_TOR_CEIL=5000 diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index 252424619c..77e75c4502 100644 --- a/contrib/win32build/tor-mingw.nsi.in +++ b/contrib/win32build/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.3.2.9-dev" +!define VERSION "0.3.3.2-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/doc/HACKING/CodingStandards.md b/doc/HACKING/CodingStandards.md index dd21d6fd2c..79a6a9f0ce 100644 --- a/doc/HACKING/CodingStandards.md +++ b/doc/HACKING/CodingStandards.md @@ -346,6 +346,46 @@ macro, as in: if (BUG(ptr == NULL)) return -1; +Allocator conventions +--------------------- + +By convention, any tor type with a name like `abc_t` should be allocated +by a function named `abc_new()`. This function should never return +NULL. + +Also, a type named `abc_t` should be freed by a function named `abc_free_()`. +Don't call this `abc_free_()` function directly -- instead, wrap it in a +macro called `abc_free()`, using the `FREE_AND_NULL` macro: + + void abc_free_(abc_t *obj); + #define abc_free(obj) FREE_AND_NULL(abc_t, abc_free_, (obj)) + +This macro will free the underlying `abc_t` object, and will also set +the object pointer to NULL. + +You should define all `abc_free_()` functions to accept NULL inputs: + + void + abc_free_(abc_t *obj) + { + if (!obj) + return; + tor_free(obj->name); + thing_free(obj->thing); + tor_free(obj); + } + +If you need a free function that takes a `void *` argument (for example, +to use it as a function callback), define it with a name like +`abc_free_void()`: + + static void + abc_free_void_(void *obj) + { + abc_free_(obj); + } + + Doxygen comment conventions --------------------------- diff --git a/doc/HACKING/GettingStartedRust.md b/doc/HACKING/GettingStartedRust.md index a5253b46a6..f5914bc87a 100644 --- a/doc/HACKING/GettingStartedRust.md +++ b/doc/HACKING/GettingStartedRust.md @@ -9,17 +9,19 @@ Please read or review our documentation on Rust coding standards (`.../doc/HACKING/CodingStandardsRust.md`) before doing anything. Please also read -[the Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). We aim -to follow the good example set by the Rust community and be excellent to one -another. Let's be careful with each other, so we can be memory-safe together! - -Next, please contact us before rewriting anything! Rust in Tor is still an -experiment. It is an experiment that we very much want to see succeed, so we're -going slowly and carefully. For the moment, it's also a completely -volunteer-driven effort: while many, if not most, of us are paid to work on Tor, -we are not yet funded to write Rust code for Tor. Please be patient with the -other people who are working on getting more Rust code into Tor, because they -are graciously donating their free time to contribute to this effort. +[the Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). We +aim to follow the good example set by the Rust community and be +excellent to one another. Let's be careful with each other, so we can +be memory-safe together! + +Next, please contact us before rewriting anything! Rust in Tor is still +an experiment. It is an experiment that we very much want to see +succeed, so we're going slowly and carefully. For the moment, it's also +a completely volunteer-driven effort: while many, if not most, of us are +paid to work on Tor, we are not yet funded to write Rust code for Tor. +Please be patient with the other people who are working on getting more +Rust code into Tor, because they are graciously donating their free time +to contribute to this effort. Resources for learning Rust ----------------------------- @@ -33,14 +35,15 @@ Rust immediately, without waiting for anything to install, there is **Advanced resources** -If you're interested in playing with various Rust compilers and viewing a very -nicely displayed output of the generated assembly, there is +If you're interested in playing with various Rust compilers and viewing +a very nicely displayed output of the generated assembly, there is [the Godbolt compiler explorer](https://rust.godbolt.org/) For learning how to write unsafe Rust, read [The Rustonomicon](https://doc.rust-lang.org/nomicon/). -For learning everything you ever wanted to know about Rust macros, there is +For learning everything you ever wanted to know about Rust macros, there +is [The Little Book of Rust Macros](https://danielkeep.github.io/tlborm/book/index.html). For learning more about FFI and Rust, see Jake Goulding's @@ -49,10 +52,10 @@ For learning more about FFI and Rust, see Jake Goulding's Compiling Tor with Rust enabled --------------------------------- -You will need to run the `configure` script with the `--enable-rust` flag to -explicitly build with Rust. Additionally, you will need to specify where to -fetch Rust dependencies, as we allow for either fetching dependencies from Cargo -or specifying a local directory. +You will need to run the `configure` script with the `--enable-rust` +flag to explicitly build with Rust. Additionally, you will need to +specify where to fetch Rust dependencies, as we allow for either +fetching dependencies from Cargo or specifying a local directory. **Fetch dependencies from Cargo** @@ -60,48 +63,48 @@ or specifying a local directory. **Using a local dependency cache** -**NOTE**: local dependency caches which were not *originally* created via - `--enable-cargo-online-mode` are broken. See https://bugs.torproject.org/22907 +You'll need the following Rust dependencies (as of this writing): -To specify a local directory: + libc==0.2.22 - RUST_DEPENDENCIES='path_to_dependencies_directory' ./configure --enable-rust +We vendor our Rust dependencies in a separate repo using +[cargo-vendor](https://github.com/alexcrichton/cargo-vendor). To use +them, do: -(Note that RUST_DEPENDENCIES must be the full path to the directory; it cannot -be relative.) + git submodule init + git submodule update -You'll need the following Rust dependencies (as of this writing): +To specify the local directory containing the dependencies, (assuming +you are in the top level of the repository) configure tor with: - libc==0.2.22 + TOR_RUST_DEPENDENCIES='path_to_dependencies_directory' ./configure --enable-rust + +(Note that TOR_RUST_DEPENDENCIES must be the full path to the directory; it +cannot be relative.) -To get them, do: +Assuming you used the above `git submodule` commands and you're in the +topmost directory of the repository, this would be: - mkdir path_to_dependencies_directory - cd path_to_dependencies_directory - git clone https://github.com/rust-lang/libc - cd libc - git checkout 0.2.22 - cargo package - cd .. - ln -s libc/target/package/libc-0.2.22 libc-0.2.22 + TOR_RUST_DEPENDENCIES=`pwd`/src/ext/rust/crates ./configure --enable-rust Identifying which modules to rewrite ====================================== -The places in the Tor codebase that are good candidates for porting to Rust are: +The places in the Tor codebase that are good candidates for porting to +Rust are: 1. loosely coupled to other Tor submodules, 2. have high test coverage, and 3. would benefit from being implemented in a memory safe language. -Help in either identifying places such as this, or working to improve existing -areas of the C codebase by adding regression tests and simplifying dependencies, -would be really helpful. +Help in either identifying places such as this, or working to improve +existing areas of the C codebase by adding regression tests and +simplifying dependencies, would be really helpful. Furthermore, as submodules in C are implemented in Rust, this is a good -opportunity to refactor, add more tests, and split modules into smaller areas of -responsibility. +opportunity to refactor, add more tests, and split modules into smaller +areas of responsibility. A good first step is to build a module-level callgraph to understand how interconnected your target module is. @@ -119,11 +122,22 @@ the module calls. Modules which call fewer other modules are better targets. Strive to change the C API as little as possible. -We are currently targetting Rust nightly, *for now*. We expect this to change -moving forward, as we understand more about which nightly features we need. It -is on our TODO list to try to cultivate good standing with various distro -maintainers of `rustc` and `cargo`, in order to ensure that whatever version we -solidify on is readily available. +We are currently targeting Rust nightly, *for now*. We expect this to +change moving forward, as we understand more about which nightly +features we need. It is on our TODO list to try to cultivate good +standing with various distro maintainers of `rustc` and `cargo`, in +order to ensure that whatever version we solidify on is readily +available. + +If parts of your Rust code needs to stay in sync with C code (such as +handling enums across the FFI boundary), annonotate these places in a +comment structured as follows: + + /// C_RUST_COUPLED: <path_to_file> `<name_of_c_object>` + +Where <name_of_c_object> can be an enum, struct, constant, etc. Then, +do the same in the C code, to note that rust will need to be changed +when the C does. Adding your Rust module to Tor's build system ----------------------------------------------- @@ -132,16 +146,22 @@ solidify on is readily available. in the `.../tor/src/rust/` directory. 1. Add your crate to `.../tor/src/rust/Cargo.toml`, in the `[workspace.members]` section. -2. Append your crate's static library to the `rust_ldadd` definition - (underneath `if USE_RUST`) in `.../tor/Makefile.am`. +2. Add your crate's files to src/rust/include.am + +If your crate should be available to C (rather than just being included as a +dependency of other Rust modules): +0. Declare the crate as a dependency of tor_rust in + `src/rust/tor_util/Cargo.toml` and include it in + `src/rust/tor_rust/lib.rs` How to test your Rust code ---------------------------- Everything should be tested full stop. Even non-public functionality. -Be sure to edit `.../tor/src/test/test_rust.sh` to add the name of your crate to -the `crates` variable! This will ensure that `cargo test` is run on your crate. +Be sure to edit `.../tor/src/test/test_rust.sh` to add the name of your +crate to the `crates` variable! This will ensure that `cargo test` is +run on your crate. Configure Tor's build system to build with Rust enabled: diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md index 62029b44f0..6c8fa1331f 100644 --- a/doc/HACKING/ReleasingTor.md +++ b/doc/HACKING/ReleasingTor.md @@ -14,6 +14,9 @@ new Tor release: 2. If this is going to be an important security release, give the packagers some advance warning: See this list of packagers in IV.3 below. +3. Given the release date for Tor, ask the TB team about the likely release + date of a TB that contains it. See note below in "commit, upload, + announce". === I. Make sure it works @@ -22,6 +25,7 @@ new Tor release: resolve those. As applicable, merge the `maint-X` branch into the `release-X` branch. + But you've been doing that all along, right? 2. Are all of the jenkins builders happy? See jenkins.torproject.org. @@ -134,6 +138,9 @@ new Tor release: either `make`, or `perl scripts/maint/updateVersions.pl`, depending on your version.) + When you merge the maint branch forward to the next maint branch, or into + master, merge it with "-s ours" to avoid a needless version bump. + 2. Make distcheck, put the tarball up in somewhere (how about your homedir on your homedir on people.torproject.org?) , and tell `#tor` about it. Wait a while to see if anybody has problems building it. @@ -147,6 +154,9 @@ new Tor release: git tag -u <keyid> tor-0.3.x.y-status git push origin tag tor-0.3.x.y-status + (You must do this before you update the website: it relies on finding + the version by tag.) + 2. scp the tarball and its sig to the dist website, i.e. `/srv/dist-master.torproject.org/htdocs/` on dist-master. When you want it to go live, you run "static-update-component dist.torproject.org" @@ -171,7 +181,10 @@ new Tor release: - {mike} at tig dot as - {tails-rm} at boum dot org - {simon} at sdeziel.info - - {yuri} at rawbw.com + - {yuri} at freebsd.org + - {mh+tor} at scrit.ch + + Also, email tor-packagers@lists.torproject.org. 4. Add the version number to Trac. To do this, go to Trac, log in, select "Admin" near the top of the screen, then select "Versions" from @@ -180,7 +193,11 @@ new Tor release: 0.2.2.23-alpha" (or whatever the version is), and we select the date as the date in the ChangeLog. -5. Mail the release blurb and ChangeLog to tor-talk (development release) or +5. Double-check: did the version get recommended in the consensus yet? Is + the website updated? If not, don't announce until they have the + up-to-date versions, or people will get confused. + +6. Mail the release blurb and ChangeLog to tor-talk (development release) or tor-announce (stable). Post the changelog on the blog as well. You can generate a diff --git a/doc/HACKING/Tracing.md b/doc/HACKING/Tracing.md index a5fb5165e2..349aade23a 100644 --- a/doc/HACKING/Tracing.md +++ b/doc/HACKING/Tracing.md @@ -6,7 +6,7 @@ tracing framework. ## Basics ### -Event tracing is seperated in two concepts, trace events and a tracer. The +Event tracing is separated in two concepts, trace events and a tracer. The tracing subsystem can be found in `src/trace`. The `events.h` header file is the main file that maps the different tracers to trace events. diff --git a/doc/HACKING/android/Simpleperf.md b/doc/HACKING/android/Simpleperf.md new file mode 100644 index 0000000000..25f39a3d23 --- /dev/null +++ b/doc/HACKING/android/Simpleperf.md @@ -0,0 +1,98 @@ +# Using `simpleperf` to collect CPU profiling on Android + +This document describes how you can use Android's `simpleperf` +command-line tool to get CPU profiling information from Tor via the +Orbot application. The tool is particularly useful for Tor development +because it is able to profile native applications on the platform +whereas a lot of the normal tooling for the Android platform is only +able to collect information from Java-based applications. + +## Prerequisites + +Before using `simpleperf` there is a couple of steps that must be +followed. You should make sure you have both a recent installation of +the Android Software Development Kit (SDK) and Native Development Kit +(NDK) installed. These can be found on the Android Developers website. + +1. Follow the build instructions from the `BUILD` file in the Orbot + repository and build an Orbot APK (Android Package) file with + debugging enabled. Make sure that when you build the native content of + the Orbot application that you run the `make -C external` command with + an additional `DEBUG=1` as parameter to ensure that the Orbot build + process does not strip the debug symbols from the Tor binary. + +2. (Optional) Uninstall and clean-up your old Orbot installation that + is most likely downloaded from Google's Play Store or via fdroid: + + $ adb shell pm clear org.torproject.android + $ adb uninstall org.torproject.android + +3. Install the Android Package you generated in step 1: + + $ adb install /path/to/your/app-fullperm-debug.apk + +4. Check on your device that the newly installed Orbot actually works + and behaves in the way you expect it to. + +## Profiling using `simpleperf` + +The `simpleperf` tool can be found in the `simpleperf/` directory in +the directory where you installed the Android NDK to. In this +directory there is a set of Python files that will help you deploy the +tool to a device and collect the measurement data such that you can +analyze the results on your computer rather than on your phone. + +1. Change directory to the location of the `simpleperf` directory. +2. Open the `app_profiler.config` file and change + `app_package_name` to `org.torproject.android`, `apk_file_path` to + the path of your Orbot Android Package (APK file). +3. Optionally change the duration parameter in the `record_options` + variable in `app_profiler.config` to the duration which you would like + to collect samples in. The value is specified in seconds. +4. Run the app profiler using `python app_profiler.py`. This helper + script will push the `simpleperf` tool to your device, start the + profiler, and once it has completed copy the generated `perf.data` + file over to your computer with the results. + +### Analyzing the results + +You can inspect your resulting `perf.data` file via a simple GUI +program `python report.py` or via the command-line tool `simpleperf +report`. I've found the GUI tool to be easier to navigate around with +than the command-line tool. + +The `-g` option can be passed to the command line `simpleperf report` +tool allows you to see the call graph of functions and how much time +was spend on the call. + +## Tips & Tricks + +- When you have installed Orbot the first time, you will notice that + if you get a shell on the Android device that there is no Tor binary + available. This is because Orbot unpacks the Tor binary first time it + is executed and places it under the `app_bin/` directory on the + device. + + To access binaries, `torrc` files, and other useful information on + the device do the following: + + $ adb shell + (device):/ $ run-as org.torproject.android + (device):/data/data/org.torproject.android $ ls + app_bin app_data cache databases files lib shared_prefs + + Descriptors, control authentication cookie, state, and other files can be + found in the `app_data` directory. The `torrc` can be found in the `app_bin/` + directory. + +- You can enable logging in Tor via the syslog (or android) log + mechanism with: + + $ adb shell + (device):/ $ run-as org.torproject.android + (device):/data/data/org.torproject.android $ echo -e "\nLog info syslog" >> app_bin/torrc + + Start Tor the normal way via Orbot and collect the logs from your computer using + + $ adb logcat + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index aad8440a2c..ad6d941587 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -231,12 +231,15 @@ GENERAL OPTIONS usage for \_relayed traffic_ on this node to the specified number of bytes per second, and the average outgoing bandwidth usage to that same value. Relayed traffic currently is calculated to include answers to directory - requests, but that may change in future versions. (Default: 0) + requests, but that may change in future versions. They do not include directory + fetches by the relay (from authority or other relays), because that is considered + "client" activity. (Default: 0) [[RelayBandwidthBurst]] **RelayBandwidthBurst** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: If not 0, limit the maximum token bucket size (also known as the burst) for \_relayed traffic_ to the given number of bytes in each direction. - (Default: 0) + They do not include directory fetches by the relay (from authority + or other relays), because that is considered "client" activity. (Default: 0) [[PerConnBWRate]] **PerConnBWRate** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: If set, do separate rate limiting for each connection from a non-relay. @@ -415,6 +418,16 @@ GENERAL OPTIONS DataDirectory. If the option is set to 1, make the DataDirectory readable by the default GID. (Default: 0) +[[CacheDirectory]] **CacheDirectory** __DIR__:: + Store cached directory data in DIR. Can not be changed while tor is + running. + (Default: uses the value of DataDirectory.) + +[[CacheDirectoryGroupReadable]] **CacheDirectoryGroupReadable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read the + CacheDirectory. If the option is set to 1, make the CacheDirectory readable + by the default GID. (Default: 0) + [[FallbackDir]] **FallbackDir** __ipv4address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__] [ipv6=**[**__ipv6address__**]**:__orport__]:: When we're unable to connect to any directory cache for directory info (usually because we don't know about any yet) we try a directory authority. @@ -595,10 +608,10 @@ GENERAL OPTIONS in accordance to RFC 1929. Both username and password must be between 1 and 255 characters. -[[SocksSocketsGroupWritable]] **SocksSocketsGroupWritable** **0**|**1**:: +[[UnixSocksGroupWritable]] **UnixSocksGroupWritable** **0**|**1**:: If this option is set to 0, don't allow the filesystem group to read and - write unix sockets (e.g. SocksSocket). If the option is set to 1, make - the SocksSocket socket readable and writable by the default GID. (Default: 0) + write unix sockets (e.g. SocksPort unix:). If the option is set to 1, make + the Unix socket readable and writable by the default GID. (Default: 0) [[KeepalivePeriod]] **KeepalivePeriod** __NUM__:: To keep firewalls from expiring connections, send a padding keepalive cell @@ -663,7 +676,7 @@ GENERAL OPTIONS be used twice, once with an IPv4 address and once with an IPv6 address. IPv6 addresses should be wrapped in square brackets. This setting will be ignored for connections to the loopback addresses - (127.0.0.0/8 and ::1). + (127.0.0.0/8 and ::1), and is not used for DNS requests as well. [[OutboundBindAddressOR]] **OutboundBindAddressOR** __IP__:: Make all outbound non-exit (relay and other) connections @@ -715,6 +728,11 @@ GENERAL OPTIONS log entries are marked with "Tor-__tag__". Can not be changed while tor is running. (Default: none) +[[AndroidIdentityTag]] **AndroidIdentityTag** __tag__:: + When logging to Android's logging subsystem, adds a tag to the log identity + such that log entries are marked with "Tor-__tag__". Can not be changed while + tor is running. (Default: none) + [[SafeLogging]] **SafeLogging** **0**|**1**|**relay**:: Tor can scrub potentially sensitive strings from log messages (e.g. addresses) by replacing them with the string [scrubbed]. This way logs can @@ -1224,8 +1242,8 @@ The following options are useful only for clients (that is, if nodes via this connection. **UseIPv4Cache**;; Tells the client to use any cached IPv4 DNS answers we have when making - requests via this connection. (NOTE: This option, along UseIPv6Cache - and UseDNSCache, can harm your anonymity, and probably + requests via this connection. (NOTE: This option, or UseIPv6Cache + or UseDNSCache, can harm your anonymity, and probably won't help performance as much as you might expect. Use with care!) **UseIPv6Cache**;; Tells the client to use any cached IPv6 DNS answers we have when making @@ -1513,6 +1531,100 @@ The following options are useful only for clients (that is, if If no nodes in Tor2webRendezvousPoints are currently available for use, Tor will choose a random node when building HS circuits. +[[_HSLayer2Nodes]] **_HSLayer2Nodes** __node__,__node__,__...__:: + A list of identity fingerprints, nicknames, country codes, and + address patterns of nodes that are allowed to be used as the + second hop in all client or service-side Onion Service circuits. + This option mitigates attacks where the adversary runs middle nodes + and induces your client or service to create many circuits, in order + to discover your primary guard node. + (Default: Any node in the network may be used in the second hop.) + + + (Example: + _HSLayer2Nodes ABCD1234CDEF5678ABCD1234CDEF5678ABCD1234, \{cc}, 255.254.0.0/8) + + + + When this is set, the resulting hidden service paths will + look like: + + + C - G - L2 - M - Rend + + C - G - L2 - M - HSDir + + C - G - L2 - M - Intro + + S - G - L2 - M - Rend + + S - G - L2 - M - HSDir + + S - G - L2 - M - Intro + + + + where C is this client, S is the service, G is the Guard node, + L2 is a node from this option, and M is a random middle node. + Rend, HSDir, and Intro point selection is not affected by this + option. + + + This option may be combined with _HSLayer3Nodes to create + paths of the form: + + + C - G - L2 - L3 - Rend + + C - G - L2 - L3 - M - HSDir + + C - G - L2 - L3 - M - Intro + + S - G - L2 - L3 - M - Rend + + S - G - L2 - L3 - HSDir + + S - G - L2 - L3 - Intro + + + + ExcludeNodes have higher priority than _HSLayer2Nodes, + which means that nodes specified in ExcludeNodes will not be + picked. + + + This option is meant to be managed by a Tor controller such as + https://github.com/mikeperry-tor/vanguards that selects and + updates this set of nodes for you. Hence it does not do load + balancing if fewer than 20 nodes are selected, and if no nodes in + _HSLayer2Nodes are currently available for use, Tor will not work. + Please use extreme care if you are setting this option manually. + +[[_HSLayer3Nodes]] **_HSLayer3Nodes** __node__,__node__,__...__:: + A list of identity fingerprints, nicknames, country codes, and + address patterns of nodes that are allowed to be used as the + third hop in all client and service-side Onion Service circuits. + This option mitigates attacks where the adversary runs middle nodes + and induces your client or service to create many circuits, in order + to discover your primary or Layer2 guard nodes. + (Default: Any node in the network may be used in the third hop.) + + + (Example: + _HSLayer3Nodes ABCD1234CDEF5678ABCD1234CDEF5678ABCD1234, \{cc}, 255.254.0.0/8) + + + + When this is set by itself, the resulting hidden service paths + will look like: + + C - G - M - L3 - Rend + + C - G - M - L3 - M - HSDir + + C - G - M - L3 - M - Intro + + S - G - M - L3 - M - Rend + + S - G - M - L3 - HSDir + + S - G - M - L3 - Intro + + where C is this client, S is the service, G is the Guard node, + L2 is a node from this option, and M is a random middle node. + Rend, HSDir, and Intro point selection is not affected by this + option. + + + While it is possible to use this option by itself, it should be + combined with _HSLayer2Nodes to create paths of the form: + + + C - G - L2 - L3 - Rend + + C - G - L2 - L3 - M - HSDir + + C - G - L2 - L3 - M - Intro + + S - G - L2 - L3 - M - Rend + + S - G - L2 - L3 - HSDir + + S - G - L2 - L3 - Intro + + + + ExcludeNodes have higher priority than _HSLayer3Nodes, + which means that nodes specified in ExcludeNodes will not be + picked. + + + This option is meant to be managed by a Tor controller such as + https://github.com/mikeperry-tor/vanguards that selects and + updates this set of nodes for you. Hence it does not do load + balancing if fewer than 20 nodes are selected, and if no nodes in + _HSLayer3Nodes are currently available for use, Tor will not work. + Please use extreme care if you are setting this option manually. + [[UseMicrodescriptors]] **UseMicrodescriptors** **0**|**1**|**auto**:: Microdescriptors are a smaller version of the information that Tor needs in order to build its circuits. Using microdescriptors makes Tor clients @@ -1777,8 +1889,15 @@ is non-zero): write your IPv6 rules using accept6/reject6 \*6, and your IPv4 rules using accept/reject \*4. If you want to \_replace_ the default exit policy, end your exit policy with either a reject \*:* or an accept \*:*. Otherwise, - you're \_augmenting_ (prepending to) the default exit policy. The default - exit policy is: + + you're \_augmenting_ (prepending to) the default exit policy. + + + + If you want to use a reduced exit policy rather than the default exit + policy, set "ReducedExitPolicy 1". If you want to _replace_ the default + exit policy with your custom exit policy, end your exit policy with either + a reject *:* or an accept *:*. Otherwise, you’re _augmenting_ (prepending + to) the default or reduced exit policy. + + + + The default exit policy is: reject *:25 reject *:119 @@ -1792,7 +1911,7 @@ is non-zero): reject *:6881-6999 accept *:* -[[ExitPolicyDefault]]:: +[[ExitPolicyDefault]] **ExitPolicyDefault**:: Since the default exit policy uses accept/reject *, it applies to both IPv4 and IPv6 addresses. @@ -1814,6 +1933,99 @@ is non-zero): to disclose. (Default: 0) +[[ReducedExitPolicy]] **ReducedExitPolicy** **0**|**1**:: + If set, use a reduced exit policy rather than the default one. + + + + The reduced exit policy is an alternative to the default exit policy. It + allows as many Internet services as possible while still blocking the + majority of TCP ports. Currently, the policy allows approximately 65 ports. + This reduces the odds that your node will be used for peer-to-peer + applications. + + + + The reduced exit policy is: + + accept *:20-21 + accept *:22 + accept *:23 + accept *:43 + accept *:53 + accept *:79 + accept *:80-81 + accept *:88 + accept *:110 + accept *:143 + accept *:194 + accept *:220 + accept *:389 + accept *:443 + accept *:464 + accept *:465 + accept *:531 + accept *:543-544 + accept *:554 + accept *:563 + accept *:587 + accept *:636 + accept *:706 + accept *:749 + accept *:873 + accept *:902-904 + accept *:981 + accept *:989-990 + accept *:991 + accept *:992 + accept *:993 + accept *:994 + accept *:995 + accept *:1194 + accept *:1220 + accept *:1293 + accept *:1500 + accept *:1533 + accept *:1677 + accept *:1723 + accept *:1755 + accept *:1863 + accept *:2082 + accept *:2083 + accept *:2086-2087 + accept *:2095-2096 + accept *:2102-2104 + accept *:3128 + accept *:3389 + accept *:3690 + accept *:4321 + accept *:4643 + accept *:5050 + accept *:5190 + accept *:5222-5223 + accept *:5228 + accept *:5900 + accept *:6660-6669 + accept *:6679 + accept *:6697 + accept *:8000 + accept *:8008 + accept *:8074 + accept *:8080 + accept *:8082 + accept *:8087-8088 + accept *:8232-8233 + accept *:8332-8333 + accept *:8443 + accept *:8888 + accept *:9418 + accept *:9999 + accept *:10000 + accept *:11371 + accept *:19294 + accept *:19638 + accept *:50002 + accept *:64738 + reject *:* + + (Default: 0) + [[IPv6Exit]] **IPv6Exit** **0**|**1**:: If set, and we are an exit node, allow clients to use us for IPv6 traffic. (Default: 0) @@ -1923,6 +2135,11 @@ is non-zero): to 0 will disable the heartbeat. Otherwise, it must be at least 30 minutes. (Default: 6 hours) +[[MainloopStats]] **MainloopStats** **0**|**1**:: + Log main loop statistics every **HeartbeatPeriod** seconds. This is a log + level __notice__ message designed to help developers instrumenting Tor's + main event loop. (Default: 0) + [[AccountingMax]] **AccountingMax** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: Limits the max number of bytes sent and received within a set time period using a given calculation rule (see: AccountingStart, AccountingRule). @@ -2136,6 +2353,17 @@ is non-zero): ed25519 master identity key, as well as the corresponding temporary signing keys and certificates. (Default: 0) +[[KeyDirectory]] **KeyDirectory** __DIR__:: + Store secret keys in DIR. Can not be changed while tor is + running. + (Default: the "keys" subdirectory of DataDirectory.) + +[[KeyDirectoryGroupReadable]] **KeyDirectoryGroupReadable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read the + KeywDirectory. If the option is set to 1, make the KeyDirectory readable + by the default GID. (Default: 0) + + DIRECTORY SERVER OPTIONS ------------------------ @@ -2379,9 +2607,29 @@ on the public Tor network. [[AuthDirHasIPv6Connectivity]] **AuthDirHasIPv6Connectivity** **0**|**1**:: Authoritative directories only. When set to 0, OR ports with an - IPv6 address are being accepted without reachability testing. - When set to 1, IPv6 OR ports are being tested just like IPv4 OR - ports. (Default: 0) + IPv6 address are not included in the authority's votes. When set to 1, + IPv6 OR ports are tested for reachability like IPv4 OR ports. If the + reachability test succeeds, the authority votes for the IPv6 ORPort, and + votes Running for the relay. If the reachability test fails, the authority + does not vote for the IPv6 ORPort, and does not vote Running (Default: 0) + ++ + The content of the consensus depends on the number of voting authorities + that set AuthDirHasIPv6Connectivity: + + If no authorities set AuthDirHasIPv6Connectivity 1, there will be no + IPv6 ORPorts in the consensus. + + If a minority of authorities set AuthDirHasIPv6Connectivity 1, + unreachable IPv6 ORPorts will be removed from the consensus. But the + majority of IPv4-only authorities will still vote the relay as Running. + Reachable IPv6 ORPort lines will be included in the consensus + + If a majority of voting authorities set AuthDirHasIPv6Connectivity 1, + relays with unreachable IPv6 ORPorts will not be listed as Running. + Reachable IPv6 ORPort lines will be included in the consensus + (To ensure that any valid majority will vote relays with unreachable + IPv6 ORPorts not Running, 75% of authorities must set + AuthDirHasIPv6Connectivity 1.) [[MinMeasuredBWsForAuthToIgnoreAdvertised]] **MinMeasuredBWsForAuthToIgnoreAdvertised** __N__:: A total value, in abstract bandwidth units, describing how much @@ -2422,7 +2670,7 @@ The following options are used to configure a hidden service. you're using a Tor controller that handles hidserv publishing for you. (Default: 1) -[[HiddenServiceVersion]] **HiddenServiceVersion** __version__,__version__,__...__:: +[[HiddenServiceVersion]] **HiddenServiceVersion** **2**|**3**:: A list of rendezvous service descriptor versions to publish for the hidden service. Currently, versions 2 and 3 are supported. (Default: 2) @@ -2447,7 +2695,7 @@ The following options are used to configure a hidden service. [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous circuit. The maximum value allowed is 65535. (Setting this to 0 will allow - an unlimited number of simultanous streams.) (Default: 0) + an unlimited number of simultaneous streams.) (Default: 0) [[HiddenServiceMaxStreamsCloseCircuit]] **HiddenServiceMaxStreamsCloseCircuit** **0**|**1**:: If set to 1, then exceeding **HiddenServiceMaxStreams** will cause the @@ -2895,40 +3143,35 @@ FILES **@LOCALSTATEDIR@/lib/tor/**:: The tor process stores keys and other data here. -__DataDirectory__**/cached-status/**:: - The most recently downloaded network status document for each authority. - Each file holds one such document; the filenames are the hexadecimal - identity key fingerprints of the directory authorities. Obsolete; - no longer in use. -__DataDirectory__**/cached-certs**:: +__CacheDirectory__**/cached-certs**:: This file holds downloaded directory key certificates that are used to verify authenticity of documents generated by Tor directory authorities. -__DataDirectory__**/cached-consensus** and/or **cached-microdesc-consensus**:: +__CacheDirectory__**/cached-consensus** and/or **cached-microdesc-consensus**:: The most recent consensus network status document we've downloaded. -__DataDirectory__**/cached-descriptors** and **cached-descriptors.new**:: +__CacheDirectory__**/cached-descriptors** and **cached-descriptors.new**:: These files hold downloaded router statuses. Some routers may appear more than once; if so, the most recently published descriptor is used. Lines beginning with @-signs are annotations that contain more information about a given router. The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-descriptors file. -__DataDirectory__**/cached-extrainfo** and **cached-extrainfo.new**:: +__CacheDirectory__**/cached-extrainfo** and **cached-extrainfo.new**:: As "cached-descriptors", but holds optionally-downloaded "extra-info" documents. Relays use these documents to send inessential information about statistics, bandwidth history, and network health to the authorities. They aren't fetched by default; see the DownloadExtraInfo option for more info. -__DataDirectory__**/cached-microdescs** and **cached-microdescs.new**:: +__CacheDirectory__**/cached-microdescs** and **cached-microdescs.new**:: These files hold downloaded microdescriptors. Lines beginning with @-signs are annotations that contain more information about a given router. The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-microdescs file. -__DataDirectory__**/cached-routers** and **cached-routers.new**:: +__CacheDirectory__**/cached-routers** and **cached-routers.new**:: Obsolete versions of cached-descriptors and cached-descriptors.new. When Tor can't find the newer files, it looks here instead. @@ -2946,11 +3189,11 @@ __DataDirectory__**/sr-state**:: Authority only. State file used to record information about the current status of the shared-random-value voting state. -__DataDirectory__**/diff-cache**:: +__CacheDirectory__**/diff-cache**:: Directory cache only. Holds older consensuses, and diffs from older consensuses to the most recent consensus of each type, compressed in various ways. Each file contains a set of key-value arguments - decribing its contents, followed by a single NUL byte, followed by the + describing its contents, followed by a single NUL byte, followed by the main file contents. __DataDirectory__**/bw_accounting**:: @@ -2976,63 +3219,60 @@ __DataDirectory__**/key-pinning-journal**:: or factoring the RSA1024 key will no longer let an attacker impersonate the relay. -__DataDirectory__**/keys/***:: - Only used by servers. Holds identity keys and onion keys. - -__DataDirectory__**/keys/authority_identity_key**:: +__KeyDirectory__**/authority_identity_key**:: A v3 directory authority's master identity key, used to authenticate its signing key. Tor doesn't use this while it's running. The tor-gencert program uses this. If you're running an authority, you should keep this key offline, and not actually put it here. -__DataDirectory__**/keys/authority_certificate**:: +__KeyDirectory__**/authority_certificate**:: A v3 directory authority's certificate, which authenticates the authority's current vote- and consensus-signing key using its master identity key. Only directory authorities use this file. -__DataDirectory__**/keys/authority_signing_key**:: +__KeyDirectory__**/authority_signing_key**:: A v3 directory authority's signing key, used to sign votes and consensuses. Only directory authorities use this file. Corresponds to the **authority_certificate** cert. -__DataDirectory__**/keys/legacy_certificate**:: +__KeyDirectory__**/legacy_certificate**:: As authority_certificate: used only when V3AuthUseLegacyKey is set. See documentation for V3AuthUseLegacyKey. -__DataDirectory__**/keys/legacy_signing_key**:: +__KeyDirectory__**/legacy_signing_key**:: As authority_signing_key: used only when V3AuthUseLegacyKey is set. See documentation for V3AuthUseLegacyKey. -__DataDirectory__**/keys/secret_id_key**:: +__KeyDirectory__**/secret_id_key**:: A relay's RSA1024 permanent identity key, including private and public components. Used to sign router descriptors, and to sign other keys. -__DataDirectory__**/keys/ed25519_master_id_public_key**:: +__KeyDirectory__**/ed25519_master_id_public_key**:: The public part of a relay's Ed25519 permanent identity key. -__DataDirectory__**/keys/ed25519_master_id_secret_key**:: +__KeyDirectory__**/ed25519_master_id_secret_key**:: The private part of a relay's Ed25519 permanent identity key. This key is used to sign the medium-term ed25519 signing key. This file can be kept offline, or kept encrypted. If so, Tor will not be able to generate new signing keys itself; you'll need to use tor --keygen yourself to do so. -__DataDirectory__**/keys/ed25519_signing_secret_key**:: +__KeyDirectory__**/ed25519_signing_secret_key**:: The private and public components of a relay's medium-term Ed25519 signing key. This key is authenticated by the Ed25519 master key, in turn authenticates other keys (and router descriptors). -__DataDirectory__**/keys/ed25519_signing_cert**:: +__KeyDirectory__**/ed25519_signing_cert**:: The certificate which authenticates "ed25519_signing_secret_key" as having been signed by the Ed25519 master key. -__DataDirectory__**/keys/secret_onion_key** and **secret_onion_key.old**:: +__KeyDirectory__**/secret_onion_key** and **secret_onion_key.old**:: A relay's RSA1024 short-term onion key. Used to decrypt old-style ("TAP") circuit extension requests. The ".old" file holds the previously generated key, which the relay uses to handle any requests that were made by clients that didn't have the new one. -__DataDirectory__**/keys/secret_onion_key_ntor** and **secret_onion_key_ntor.old**:: +__KeyDirectory__**/secret_onion_key_ntor** and **secret_onion_key_ntor.old**:: A relay's Curve25519 short-term onion key. Used to handle modern ("ntor") circuit extension requests. The ".old" file holds the previously generated key, which the relay uses to handle any requests that were @@ -3059,11 +3299,11 @@ __DataDirectory__**/v3-status-votes**:: Only for v3 authoritative directory servers. This file contains status votes from all the authoritative directory servers. -__DataDirectory__**/unverified-consensus**:: +__CacheDirectory__**/unverified-consensus**:: This file contains a network consensus document that has been downloaded, but which we didn't have the right certificates to check yet. -__DataDirectory__**/unverified-microdesc-consensus**:: +__CacheDirectory__**/unverified-microdesc-consensus**:: This file contains a microdescriptor-flavored network consensus document that has been downloaded, but which we didn't have the right certificates to check yet. diff --git a/scripts/codegen/fuzzing_include_am.py b/scripts/codegen/fuzzing_include_am.py index fee6d4494a..fda57d2ae8 100755 --- a/scripts/codegen/fuzzing_include_am.py +++ b/scripts/codegen/fuzzing_include_am.py @@ -9,7 +9,7 @@ FUZZERS = """ hsdescv2 hsdescv3 http - http-connect + http-connect iptsv2 microdesc vrs diff --git a/scripts/maint/fallback.blacklist b/scripts/maint/fallback.blacklist index 1417a13a98..a118cb5919 100644 --- a/scripts/maint/fallback.blacklist +++ b/scripts/maint/fallback.blacklist @@ -3,6 +3,8 @@ # Format: # [ IPv4[:DirPort] ] [ orport=<ORPort> ] [ id=<ID> ] ... # [ ipv6=<IPv6>[:<IPv6 ORPort>] ] +# or use: +# scripts/maint/generateFallbackDirLine.py fingerprint ... # # If a sufficiently specific group of attributes matches, the directory mirror # will be excluded: (each group is listed on its own line) @@ -35,14 +37,6 @@ 62.210.207.124:9030 orport=9001 id=58938B1A5C4029B4415D38A4F36B7724273F4755 ipv6=[2001:bc8:31eb:100::1]:9001 62.210.207.124:9130 orport=9101 id=338D0AB6DBAB7B529B9C91B2FD770658000693C4 ipv6=[2001:bc8:31eb:100::1]:9101 -# these fallback candidates fail the consensus download test in a way that -# causes stem to hang (and not respond to ^C, at least on OS X) -# (Is something sending weird responses to DirPort traffic?) -#217.23.14.190:1194 -#151.80.164.147:80 -#148.251.255.92:80 -#78.142.19.59:80 - # Email sent directly to teor, verified using relay contact info 216.17.99.183:80 orport=443 id=D52CD431CEF28E01B11F545A84347EE45524BCA7 216.17.99.183:8080 orport=9001 id=EE21F83AB6F76E3B3FFCBA5C2496F789CB84E7C6 @@ -75,9 +69,6 @@ 185.21.216.140:9030 orport=9001 id=921DA852C95141F8964B359F774B35502E489869 # Email sent directly to teor, verified using relay contact info -62.210.82.44:143 orport=21 id=1C90D3AEADFF3BCD079810632C8B85637924A58E ipv6=[2001:bc8:3d7c::]:21 - -# Email sent directly to teor, verified using relay contact info 46.101.220.161:80 orport=443 id=7DDFE5B2C306B19A79832FBE581EAA245BAE90C6 ipv6=[2a03:b0c0:3:d0::8b:3001]:443 # Email sent directly to teor, verified using relay contact info @@ -200,45 +191,12 @@ # Email sent directly to teor, verified using relay contact info 88.190.208.4:30555 orport=30556 id=030A6EB24725C05D8E0FCE21923CBA5223E75E0E -# Fallback was on 0.2.8.2-alpha list, but changed fingerprint before 0.2.8.5 -46.101.102.71:80 orport=443 id=9504CB22EEB25D344DE63CB7A6F2C46F895C3686 ipv6=[2a03:b0c0:3:d0::2ed:7001]:9050 -# Also blacklist anything with the new fingerprint -id=9C8A123081EFBE022EF795630F447839DDFDDDEC - -# Fallbacks were on 0.2.8.2-alpha list, but downloads were slow before 0.2.8.5 -185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE -178.62.36.64:9030 orport=9001 id=B87C84E38DAECFFFFDE98E5AEE5786AFDC748F2C - -# Fallback was on 0.2.8.2-alpha list, but changed address before 0.2.8.5 -84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34 -# Also blacklist anything with the new address -84.216.235.55:9030 orport=443 - -# Fallbacks were on 0.2.8.2-alpha list, but disappeared before 0.2.8.5 -81.7.17.171:80 orport=443 id=CFECDDCA990E3EF7B7EC958B22441386B6B8D820 ipv6=[2a02:180:1:1::517:11ab]:443 -51.254.215.121:80 orport=443 id=262B66AD25C79588AD1FC8ED0E966395B47E5C1D -185.100.85.138:80 orport=46356 id=5C4DF16A0029CC4F67D3E127356E68F219269859 - # Fallback was on 0.2.8.2-alpha list, but opted-out before 0.2.8.6 37.187.1.149:9030 orport=9001 id=08DC0F3C6E3D9C527C1FC8745D35DD1B0DE1875D ipv6=[2001:41d0:a:195::1]:9001 # Email sent directly to teor, verified using relay contact info 195.154.15.227:9030 orport=9001 id=6C3E3AB2F5F03CD71B637D433BAD924A1ECC5796 -# Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 -195.154.8.111:80 orport=443 id=FCB6695F8F2DC240E974510A4B3A0F2B12AB5B64 -# Same operator, not on 0.2.8.6 list, also changed IPv4 -51.255.235.246:80 orport=443 id=9B99C72B02AF8E3E5BE3596964F9CACD0090D132 - -# Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 -5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33 - -# Fallbacks were on 0.2.8.6 list, but went down before 0.2.9 -194.150.168.79:11112 orport=11111 id=29F1020B94BE25E6BE1AD13E93CE19D2131B487C -94.126.23.174:9030 orport=9001 id=6FC6F08270D565BE89B7C819DD8E2D487397C073 -195.191.233.221:80 orport=443 id=DE134FC8E5CC4EC8A5DE66934E70AC9D70267197 -176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329 ipv6=[2001:41d0:8:eb9d::1]:22 - # Fallback was on 0.2.8.6 list, but opted-out before 0.2.9 144.76.73.140:9030 orport=9001 id=6A640018EABF3DA9BAD9321AA37C2C87BBE1F907 @@ -256,9 +214,6 @@ id=9C8A123081EFBE022EF795630F447839DDFDDDEC # Email sent directly to teor, verified using relay contact info 104.243.35.196:9030 orport=9001 id=FA3415659444AE006E7E9E5375E82F29700CFDFD -# Relay changed IPv4 address, operator uncontactable -138.201.130.32:9030 orport=9001 id=52AEA31188331F421B2EDB494DB65CD181E5B257 - # Emails sent directly to teor, verified using relay contact info 217.12.199.208:80 orport=443 id=DF3AED4322B1824BF5539AE54B2D1B38E080FF05 ipv6=[2a02:27a8:0:2::7e]:443 @@ -269,6 +224,9 @@ id=9C8A123081EFBE022EF795630F447839DDFDDDEC # Email sent directly to teor, verified using relay contact info 5.35.251.247:9030 orport=9001 id=9B1F5187DFBA89DC24B37EA7BF896C12B43A27AE -#​https://lists.torproject.org/pipermail/tor-relays/2017-May/012281.html +#https://lists.torproject.org/pipermail/tor-relays/2017-May/012281.html 62.210.124.124:9030 orport=9001 id=86E78DD3720C78DA8673182EF96C54B162CD660C ipv6=[2001:bc8:3f23:100::1]:9001 62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E ipv6=[2001:bc8:3f23:100::1]:9101 + +# Email sent directly to teor +212.51.156.193:995 orport=110 id=32E7AAF1F602814D699BEF6761AD03E387758D49 ipv6=[2a02:168:4a01::49]:110 diff --git a/scripts/maint/fallback.whitelist b/scripts/maint/fallback.whitelist index 0620d6b5fe..e9158e1280 100644 --- a/scripts/maint/fallback.whitelist +++ b/scripts/maint/fallback.whitelist @@ -2,6 +2,8 @@ # # Format: # IPv4:DirPort orport=<ORPort> id=<ID> [ ipv6=<IPv6>:<IPv6 ORPort> ] +# or use: +# scripts/maint/generateFallbackDirLine.py fingerprint ... # # All attributes must match for the directory mirror to be included. # If the fallback has an ipv6 key, the whitelist line must also have @@ -28,8 +30,9 @@ # <IPv4>:<DirPort> orport=<ORPort> id=<ID> [ ipv6=<IPv6>:<IPv6 ORPort> ] # https://lists.torproject.org/pipermail/tor-relays/2015-December/008362.html -78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE -131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8 +# https://trac.torproject.org/projects/tor/ticket/22321#comment:22 +78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE ipv6=[2a01:4f8:120:4023::110]:80 # fluxe3 +131.188.40.188:1443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8 ipv6=[2001:638:a000:4140::ffff:188]:80 # fluxe4 # https://lists.torproject.org/pipermail/tor-relays/2015-December/008366.html 5.39.88.19:9030 orport=9001 id=7CB8C31432A796731EA7B6BF4025548DFEB25E0C ipv6=[2001:41d0:8:9a13::1]:9050 @@ -52,17 +55,21 @@ # https://lists.torproject.org/pipermail/tor-relays/2015-December/008379.html # Email sent directly to teor, verified using relay contact info -91.121.84.137:4951 orport=4051 id=6DE61A6F72C1E5418A66BFED80DFB63E4C77668F ipv6=[2001:41d0:1:8989::1]:4051 -91.121.84.137:4952 orport=4052 id=9FBEB75E8BC142565F12CBBE078D63310236A334 ipv6=[2001:41d0:1:8989::1]:4052 +91.121.84.137:4951 orport=4051 id=6DE61A6F72C1E5418A66BFED80DFB63E4C77668F +91.121.84.137:4952 orport=4052 id=9FBEB75E8BC142565F12CBBE078D63310236A334 # https://lists.torproject.org/pipermail/tor-relays/2015-December/008381.html -# Sent additional email to teor with more relays -178.254.44.135:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422 -178.254.20.134:80 orport=443 id=9F5068310818ED7C70B0BC4087AB55CB12CB4377 -178.254.20.134:9030 orport=9001 id=2CE96A8A1DA032664C90F574AFFBECE18A6E8DFC -178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2 -178.254.13.126:80 orport=443 id=F9246DEF2B653807236DA134F2AEAB103D58ABFE -178.254.13.126:9030 orport=9001 id=0C475BA4D3AA3C289B716F95954CAD616E50C4E5 +# Sent additional emails to teor with updated relays +81.7.11.96:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422 # Doedel22 +# 9F5068310818ED7C70B0BC4087AB55CB12CB4377 not found in current consensus +178.254.19.101:80 orport=443 id=F9246DEF2B653807236DA134F2AEAB103D58ABFE # Freebird31 +178.254.19.101:9030 orport=9001 id=0C475BA4D3AA3C289B716F95954CAD616E50C4E5 # Freebird32 +81.7.14.253:9001 orport=443 id=1AE039EE0B11DB79E4B4B29CBA9F752864A0259E # Ichotolot60 +81.7.11.186:1080 orport=443 id=B86137AE9681701901C6720E55C16805B46BD8E3 # BeastieJoy60 +85.25.213.211:465 orport=80 id=CE47F0356D86CF0A1A2008D97623216D560FB0A8 # BeastieJoy61 +85.25.159.65:995 orport=80 id=52BFADA8BEAA01BA46C8F767F83C18E2FE50C1B9 # BeastieJoy63 +81.7.3.67:993 orport=443 id=A2E6BB5C391CD46B38C55B4329C35304540771F1 # BeastieJoy62 +81.7.14.31:9001 orport=443 id=7600680249A22080ECC6173FBBF64D6FCF330A61 # Ichotolot62 # https://lists.torproject.org/pipermail/tor-relays/2015-December/008382.html 51.255.33.237:9091 orport=9001 id=A360C21FA87FFA2046D92C17086A6B47E5C68109 @@ -97,12 +104,9 @@ # Email sent directly to teor, verified using relay contact info 171.25.193.77:80 orport=443 id=A10C4F666D27364036B562823E5830BC448E046A ipv6=[2001:67c:289c:3::77]:443 171.25.193.78:80 orport=443 id=A478E421F83194C114F41E94F95999672AED51FE ipv6=[2001:67c:289c:3::78]:443 -171.25.193.131:80 orport=443 id=79861CF8522FC637EF046F7688F5289E49D94576 171.25.193.20:80 orport=443 id=DD8BD7307017407FCC36F8D04A688F74A0774C02 ipv6=[2001:67c:289c::20]:443 -# OK, but same machine as 79861CF8522FC637EF046F7688F5289E49D94576 -#171.25.193.132:80 orport=443 id=01C67E0CA8F97111E652C7564CB3204361FFFAB8 -# OK, but same machine as DD8BD7307017407FCC36F8D04A688F74A0774C02 -#171.25.193.25:80 orport=443 id=185663B7C12777F052B2C2D23D7A239D8DA88A0F ipv6=[2001:67c:289c::25]:443 +# same machine as DD8BD7307017407FCC36F8D04A688F74A0774C02 +171.25.193.25:80 orport=443 id=185663B7C12777F052B2C2D23D7A239D8DA88A0F ipv6=[2001:67c:289c::25]:443 # Email sent directly to teor, verified using relay contact info 212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68 ipv6=[2001:bc8:4400:2100::f03]:9001 @@ -110,8 +114,12 @@ 46.28.109.231:9030 orport=9001 id=F70B7C5CD72D74C7F9F2DC84FA9D20D51BA13610 ipv6=[2a02:2b88:2:1::4205:1]:9001 # Email sent directly to teor, verified using relay contact info -85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE -185.96.180.29:80 orport=443 id=F93D8F37E35C390BCAD9F9069E13085B745EC216 +85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE # TykRelay01 +185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE # TykRelay051 +# This fallback opted-in in previous releases, then changed its details, +# and so we blacklisted it. Now we want to whitelist changes. +# Assume details update is permanent +185.96.180.29:80 orport=443 id=F93D8F37E35C390BCAD9F9069E13085B745EC216 # TykRelay06 # Email sent directly to teor, verified using relay contact info 185.11.180.67:80 orport=9001 id=794D8EA8343A4E820320265D05D4FA83AB6D1778 @@ -151,9 +159,10 @@ 109.163.234.8:80 orport=443 id=0818DAE0E2DDF795AEDEAC60B15E71901084F281 109.163.234.9:80 orport=443 id=ABF7FBF389C9A747938B639B20E80620B460B2A9 62.102.148.67:80 orport=443 id=4A0C3E177AF684581EF780981AEAF51A98A6B5CF -77.247.181.162:80 orport=443 id=7BB160A8F54BD74F3DA5F2CE701E8772B841859D -77.247.181.164:80 orport=443 id=10E13E340651D0EF66B4DEBF610B3C0981168107 -77.247.181.166:80 orport=443 id=06E123865C590189B3181114F23F0F13A7BC0E69 +# Assume details update is permanent +77.247.181.166:80 orport=443 id=77131D7E2EC1CA9B8D737502256DA9103599CE51 # CriticalMass +77.247.181.164:80 orport=443 id=204DFD2A2C6A0DC1FA0EACB495218E0B661704FD # HaveHeart +77.247.181.162:80 orport=443 id=7BFB908A3AA5B491DA4CA72CCBEE0E1F2A939B55 # sofia # https://twitter.com/biotimylated/status/718994247500718080 212.47.252.149:9030 orport=9001 id=2CAC39BAA996791CEFAADC9D4754D65AF5EB77C0 @@ -205,20 +214,19 @@ # Email sent directly to teor, verified using relay contact info 89.187.142.208:80 orport=443 id=64186650FFE4469EBBE52B644AE543864D32F43C -# Email sent directly to teor, verified using relay contact info -212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2 ipv6=[2a02:168:6e00:0:3a60:77ff:fe9c:8bd1]:9001 +# Email sent directly to teor +# Assume details update is permanent +212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2 # Jans # Email sent directly to teor, verified using relay contact info 46.101.143.173:80 orport=443 id=F960DF50F0FD4075AC9B505C1D4FFC8384C490FB # Email sent directly to teor, verified using relay contact info -217.79.190.25:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B - -# Email sent directly to teor, verified using relay contact info 193.171.202.146:9030 orport=9001 id=01A9258A46E97FF8B2CAC7910577862C14F2C524 # Email sent directly to teor, verified using relay contact info -197.231.221.211:9030 orport=9001 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626 +# Assume details update is permanent +197.231.221.211:9030 orport=443 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626 # IPredator # Email sent directly to teor, verified using relay contact info 185.61.138.18:8080 orport=4443 id=2541759BEC04D37811C2209A88E863320271EC9C @@ -229,7 +237,7 @@ 193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E # Email sent directly to teor, verified using relay contact info -144.76.26.175:9012 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510 +138.201.250.33:9012 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510 # Email sent directly to teor, verified using relay contact info 37.221.162.226:9030 orport=9001 id=D64366987CB39F61AD21DBCF8142FA0577B92811 @@ -247,7 +255,7 @@ 134.119.3.164:9030 orport=9001 id=D1B8AAA98C65F3DF7D8BB3AF881CAEB84A33D8EE # Email sent directly to teor, verified using relay contact info -81.7.10.93:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7 +173.212.254.192:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7 # Email sent directly to teor, verified using relay contact info 178.62.22.36:80 orport=443 id=A0766C0D3A667A3232C7D569DE94A28F9922FCB1 ipv6=[2a03:b0c0:1:d0::174:1]:9050 @@ -298,14 +306,15 @@ 46.148.18.74:8080 orport=443 id=6CACF0B5F03C779672F3C5C295F37C8D234CA3F7 # Email sent directly to teor, verified using relay contact info -37.187.102.108:9090 orport=5550 id=F4263275CF54A6836EE7BD527B1328836A6F06E1 -212.47.241.21:80 orport=443 id=892F941915F6A0C6E0958E52E0A9685C190CF45C +37.187.102.108:80 orport=443 id=F4263275CF54A6836EE7BD527B1328836A6F06E1 ipv6=[2001:41d0:a:266c::1]:443 # EvilMoe +212.47.241.21:80 orport=443 id=892F941915F6A0C6E0958E52E0A9685C190CF45C # EvilMoe # Email sent directly to teor, verified using relay contact info 212.129.38.254:9030 orport=9001 id=FDF845FC159C0020E2BDDA120C30C5C5038F74B4 -# Email sent directly to teor, verified using relay contact info -37.157.195.87:8030 orport=443 id=12FD624EE73CEF37137C90D38B2406A66F68FAA2 +# Email sent directly to teor +37.157.195.87:8030 orport=443 id=12FD624EE73CEF37137C90D38B2406A66F68FAA2 # thanatosCZ +5.189.169.190:8030 orport=8080 id=8D79F73DCD91FC4F5017422FAC70074D6DB8DD81 # thanatosDE # Email sent directly to teor, verified using relay contact info 37.187.7.74:80 orport=443 id=AEA43CB1E47BE5F8051711B2BF01683DB1568E05 ipv6=[2001:41d0:a:74a::1]:443 @@ -369,14 +378,6 @@ 91.219.237.229:80 orport=443 id=1ECD73B936CB6E6B3CD647CC204F108D9DF2C9F7 # Email sent directly to teor, verified using relay contact info -212.47.240.10:82 orport=443 id=2A4C448784F5A83AFE6C78DA357D5E31F7989DEB -# Ok, but on the same machine as 2A4C448784F5A83AFE6C78DA357D5E31F7989DEB -#212.47.240.10:81 orport=993 id=72527E3242CB15AADE28374AE0D35833FC083F60 -163.172.131.88:80 orport=443 id=AD253B49E303C6AB1E048B014392AC569E8A7DAE ipv6=[2001:bc8:4400:2100::2:1009]:443 -# Ok, but on the same machine as AD253B49E303C6AB1E048B014392AC569E8A7DAE -#163.172.131.88:81 orport=993 id=D5F3FB17504744FB7ECEF46F4B1D155258A6D942 ipv6=[2001:bc8:4400:2100::2:1009]:993 - -# Email sent directly to teor, verified using relay contact info 46.101.151.222:80 orport=443 id=1DBAED235E3957DE1ABD25B4206BE71406FB61F8 178.62.60.37:80 orport=443 id=175921396C7C426309AB03775A9930B6F611F794 @@ -404,8 +405,8 @@ # Email sent directly to teor, verified using relay contact info 5.199.142.236:9030 orport=9001 id=F4C0EDAA0BF0F7EC138746F8FEF1CE26C7860265 -# Email sent directly to teor, verified using relay contact info -188.166.133.133:9030 orport=9001 id=774555642FDC1E1D4FDF2E0C31B7CA9501C5C9C7 ipv6=[2a03:b0c0:2:d0::5:f001]:9001 +# Email sent directly to teor +188.166.133.133:9030 orport=9001 id=774555642FDC1E1D4FDF2E0C31B7CA9501C5C9C7 ipv6=[2a03:b0c0:2:d0::26c0:1]:9001 # dropsy # Email sent directly to teor, verified using relay contact info 46.8.249.10:80 orport=443 id=31670150090A7C3513CB7914B9610E786391A95D @@ -477,9 +478,6 @@ 95.130.11.147:9030 orport=443 id=6B697F3FF04C26123466A5C0E5D1F8D91925967A # Email sent directly to teor, verified using relay contact info -176.31.191.26:80 orport=443 id=7350AB9ED7568F22745198359373C04AC783C37C - -# Email sent directly to teor, verified using relay contact info 128.199.55.207:9030 orport=9001 id=BCEF908195805E03E92CCFE669C48738E556B9C5 ipv6=[2a03:b0c0:2:d0::158:3001]:9001 # Email sent directly to teor, verified using relay contact info @@ -497,8 +495,8 @@ # Email sent directly to teor, verified using relay contact info 185.97.32.18:9030 orport=9001 id=04250C3835019B26AA6764E85D836088BE441088 -# Email sent directly to teor, verified using relay contact info -149.56.45.200:9030 orport=9001 id=FE296180018833AF03A8EACD5894A614623D3F76 +# Email sent directly to teor +149.56.45.200:9030 orport=9001 id=FE296180018833AF03A8EACD5894A614623D3F76 ipv6=[2607:5300:201:3000::17d3]:9002 # PiotrTorpotkinOne # Email sent directly to teor, verified using relay contact info 81.2.209.10:443 orport=80 id=B6904ADD4C0D10CDA7179E051962350A69A63243 ipv6=[2001:15e8:201:1::d10a]:80 @@ -536,8 +534,8 @@ # Email sent directly to teor, verified using relay contact info 212.238.208.48:9030 orport=9001 id=F406219CDD339026D160E53FCA0EF6857C70F109 ipv6=[2001:984:a8fb:1:ba27:ebff:feac:c109]:9001 -# Email sent directly to teor, verified using relay contact info -176.158.132.12:9030 orport=9001 id=DC163DDEF4B6F0C6BC226F9F6656A5A30C5C5686 +# Email sent directly to teor +176.158.236.102:9030 orport=9001 id=DC163DDEF4B6F0C6BC226F9F6656A5A30C5C5686 # Underworld # Email sent directly to teor, verified using relay contact info 91.229.20.27:9030 orport=9001 id=9A0D54D3A6D2E0767596BF1515E6162A75B3293F @@ -545,8 +543,8 @@ # Email sent directly to teor, verified using relay contact info 80.127.137.19:80 orport=443 id=6EF897645B79B6CB35E853B32506375014DE3621 ipv6=[2001:981:47c1:1::6]:443 -# Email sent directly to teor, verified using relay contact info -163.172.138.22:80 orport=443 id=8664DC892540F3C789DB37008236C096C871734D ipv6=[2001:bc8:4400:2100::1:3]:443 +# Email sent directly to teor +163.172.138.22:80 orport=443 id=16102E458460349EE45C0901DAA6C30094A9BBEA ipv6=[2001:bc8:4400:2100::1:3]:443 # mkultra # Email sent directly to teor, verified using relay contact info 97.74.237.196:9030 orport=9001 id=2F0F32AB1E5B943CA7D062C03F18960C86E70D94 @@ -580,7 +578,8 @@ 167.114.113.48:9030 orport=403 id=2EC0C66EA700C44670444280AABAB1EC78B722A0 # Email sent directly to teor, verified using relay contact info -79.120.16.42:9030 orport=9001 id=BD552C165E2ED2887D3F1CCE9CFF155DDA2D86E6 +# Assume details update is permanent +213.141.138.174:9030 orport=9001 id=BD552C165E2ED2887D3F1CCE9CFF155DDA2D86E6 # Schakalium # Email sent directly to teor, verified using relay contact info 95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90 ipv6=[2a02:ec0:209:10::4]:443 @@ -609,10 +608,13 @@ # Email sent directly to teor, verified using relay contact info 31.31.78.49:80 orport=443 id=46791D156C9B6C255C2665D4D8393EC7DBAA7798 -# Email sent directly to teor, verified using relay contact info -96.47.231.214:9030 orport=8080 id=F843CB5729575D76FF1FFBB2179BDCF52C0C6387 -192.99.246.48:9030 orport=9001 id=CD6B149BED1BB254EF6DFF9D75DDB11E7F8A38A4 ipv6=[2607:5300:100:200::de3]:9002 -192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F ipv6=[2605:e200:d00c:c01d::1111]:9002 +# Email sent directly to teor +192.160.102.169:80 orport=9001 id=C0192FF43E777250084175F4E59AC1BA2290CE38 ipv6=[2620:132:300c:c01d::9]:9002 # manipogo +192.160.102.166:80 orport=9001 id=547DA56F6B88B6C596B3E3086803CDA4F0EF8F21 ipv6=[2620:132:300c:c01d::6]:9002 # chaucer +192.160.102.170:80 orport=9001 id=557ACEC850F54EEE65839F83CACE2B0825BE811E ipv6=[2620:132:300c:c01d::a]:9002 # ogopogo +192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F ipv6=[2620:132:300c:c01d::4]:9002 # snowfall +192.160.102.165:80 orport=9001 id=C90CA3B7FE01A146B8268D56977DC4A2C024B9EA ipv6=[2620:132:300c:c01d::5]:9002 # cowcat +192.160.102.168:80 orport=9001 id=F6A358DD367B3282D6EF5824C9D45E1A19C7E815 ipv6=[2620:132:300c:c01d::8]:9002 # prawksi # Email sent directly to teor, verified using relay contact info 136.243.214.137:80 orport=443 id=B291D30517D23299AD7CEE3E60DFE60D0E3A4664 @@ -623,8 +625,8 @@ # Email sent directly to teor, verified using relay contact info 192.87.28.28:9030 orport=9001 id=ED2338CAC2711B3E331392E1ED2831219B794024 -# OK, but same machine as ED2338CAC2711B3E331392E1ED2831219B794024 -#192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7 +# same machine as ED2338CAC2711B3E331392E1ED2831219B794024 +192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7 # https://twitter.com/kosjoli/status/719507270904758272 85.10.202.87:9030 orport=9001 id=971AFB23C168DCD8EDA17473C1C452B359DE3A5A @@ -680,7 +682,8 @@ 213.239.217.18:1338 orport=1337 id=C37BC191AC389179674578C3E6944E925FE186C2 ipv6=[2a01:4f8:a0:746a:101:1:1:1]:1337 # Email sent directly to teor, verified using relay contact info -188.40.128.246:9030 orport=9001 id=AD19490C7DBB26D3A68EFC824F67E69B0A96E601 +# Assume details update is permanent +188.40.128.246:9030 orport=9001 id=AD19490C7DBB26D3A68EFC824F67E69B0A96E601 ipv6=[2a01:4f8:221:1ac1:dead:beef:7005:9001]:9001 # sputnik # Email sent directly to teor, verified using relay contact info 88.198.253.13:9030 orport=9001 id=DF924196D69AAE3C00C115A9CCDF7BB62A175310 ipv6=[2a01:4f8:11a:b1f::2]:9001 @@ -698,19 +701,44 @@ # Email sent directly to teor, verified using relay contact info 107.170.101.39:9030 orport=443 id=30973217E70AF00EBE51797FF6D9AA720A902EAA -# Email sent directly to teor, verified using relay contact info -192.99.212.139:80 orport=443 id=F10BDE279AE71515DDCCCC61DC19AC8765F8A3CC - -# Email sent directly to teor, verified using relay contact info -163.172.35.249:80 orport=443 id=C08DE49658E5B3CFC6F2A952B453C4B608C9A16A -163.172.35.247:80 orport=443 id=71AB4726D830FAE776D74AEF790CF04D8E0151B4 +# Email sent directly to teor +193.70.112.165:80 orport=443 id=F10BDE279AE71515DDCCCC61DC19AC8765F8A3CC # ParkBenchInd001 + +# Email sent directly to teor +185.220.101.6:10006 orport=20006 id=C08DE49658E5B3CFC6F2A952B453C4B608C9A16A # niftyvolcanorabbit +185.220.101.13:10013 orport=20013 id=71AB4726D830FAE776D74AEF790CF04D8E0151B4 # niftycottontail +185.220.101.5:10005 orport=20005 id=1084200B44021D308EA4253F256794671B1D099A # niftyhedgehog +185.220.101.9:10009 orport=20009 id=14877C6384A9E793F422C8D1DDA447CACA4F7C4B # niftywoodmouse +185.220.101.8:10008 orport=20008 id=24E91955D969AEA1D80413C64FE106FAE7FD2EA9 # niftymouse +185.220.101.1:10001 orport=20001 id=28F4F392F8F19E3FBDE09616D9DB8143A1E2DDD3 # niftycottonmouse +185.220.101.21:10021 orport=20021 id=348B89013EDDD99E4755951D1EC284D9FED71226 # niftysquirrel +185.220.101.10:10010 orport=20010 id=4031460683AE9E0512D3620C2758D98758AC6C93 # niftyeuropeanrabbit +185.220.101.34:10034 orport=20034 id=47C42E2094EE482E7C9B586B10BABFB67557030B # niftyquokka +185.220.101.18:10018 orport=20018 id=5D5006E4992F2F97DF4F8B926C3688870EB52BD8 # niftyplagiodontia +185.220.101.28:10028 orport=20028 id=609E598FB6A00BCF7872906B602B705B64541C50 # niftychipmunk +185.220.101.20:10020 orport=20020 id=619349D82424C601CAEB94161A4CF778993DAEE7 # niftytucotuco +185.220.101.17:10017 orport=20017 id=644DECC5A1879C0FE23DE927DD7049F58BBDF349 # niftyhutia +185.220.101.0:10000 orport=20000 id=6E94866ED8CA098BACDFD36D4E8E2B459B8A734E # niftybeaver +185.220.101.30:10030 orport=20030 id=71CFDEB4D9E00CCC3E31EC4E8A29E109BBC1FB36 # niftypedetidae +185.220.101.29:10029 orport=20029 id=7DC52AE6667A30536BA2383CD102CFC24F20AD71 # niftyllipika +185.220.101.41:10041 orport=20041 id=7E281CD2C315C4F7A84BC7C8721C3BC974DDBFA3 # niftyporcupine +185.220.101.25:10025 orport=20025 id=8EE0534532EA31AA5172B1892F53B2F25C76EB02 # niftyjerboa +185.220.101.33:10033 orport=20033 id=906DCB390F2BA987AE258D745E60BAAABAD31DE8 # niftyquokka +185.220.101.26:10026 orport=20026 id=92A6085EABAADD928B6F8E871540A1A41CBC08BA # niftypedetes +185.220.101.40:10040 orport=20040 id=9A857254F379194D1CD76F4A79A20D2051BEDA3F # niftynutria +185.220.101.42:10042 orport=20042 id=9B816A5B3EB20B8E4E9B9D1FBA299BD3F40F0320 # niftypygmyjerboa +185.220.101.2:10002 orport=20002 id=B740BCECC4A9569232CDD45C0E1330BA0D030D33 # niftybunny +185.220.101.32:10032 orport=20032 id=B771AA877687F88E6F1CA5354756DF6C8A7B6B24 # niftypika +185.220.101.12:10012 orport=20012 id=BC82F2190DE2E97DE65F49B4A95572374BDC0789 # niftycapybara +185.220.101.22:10022 orport=20022 id=CA37CD46799449D83B6B98B8C22C649906307888 # niftyjackrabbit +185.220.101.4:10004 orport=20004 id=CDA2EA326E2272C57ACB26773D7252C211795B78 # niftygerbil +185.220.101.14:10014 orport=20014 id=E7EBA5D8A4E09684D11A1DF24F75362817333768 # niftyhare +185.220.101.16:10016 orport=20016 id=EC1997D51892E4607C68E800549A1E7E4694005A # niftyguineapig +185.220.101.24:10024 orport=20024 id=FDA70EC93DB01E3CB418CB6943B0C68464B18B4C # niftyrat # Email sent directly to teor, verified using relay contact info 64.113.32.29:9030 orport=9001 id=30C19B81981F450C402306E2E7CFB6C3F79CB6B2 -# Email sent directly to teor, verified using relay contact info -212.51.156.193:995 orport=110 id=32E7AAF1F602814D699BEF6761AD03E387758D49 ipv6=[2a02:168:4a01::49]:110 - # Emails sent directly to teor, verified using relay contact info 51.254.101.242:9002 orport=9001 id=4CC9CC9195EC38645B699A33307058624F660CCF @@ -767,17 +795,17 @@ 178.33.183.251:80 orport=443 id=DD823AFB415380A802DCAEB9461AE637604107FB ipv6=[2001:41d0:2:a683::251]:443 # Email sent directly to teor, verified using relay contact info -#31.185.104.19:80 orport=443 id=9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D -# OK, but on same machine as 9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D +31.185.104.19:80 orport=443 id=9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D +# same machine as 9EAD5B2D3DBD96DBC80DCE423B0C345E920A758D 31.185.104.20:80 orport=443 id=ADB2C26629643DBB9F8FE0096E7D16F9414B4F8D -#31.185.104.21:80 orport=443 id=C2AAB088555850FC434E68943F551072042B85F1 -#31.185.104.22:80 orport=443 id=5BA3A52760A0EABF7E7C3ED3048A77328FF0F148 +31.185.104.21:80 orport=443 id=C2AAB088555850FC434E68943F551072042B85F1 +31.185.104.22:80 orport=443 id=5BA3A52760A0EABF7E7C3ED3048A77328FF0F148 # Email sent directly to teor, verified using relay contact info 185.34.60.114:80 orport=443 id=7F7A695DF6F2B8640A70B6ADD01105BC2EBC5135 -# Email sent directly to teor, verified using relay contact info -94.142.242.84:80 orport=443 id=AA0D167E03E298F9A8CD50F448B81FBD7FA80D56 ipv6=[2a02:898:24:84::1]:443 +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013939.html +94.142.242.84:80 orport=443 id=AA0D167E03E298F9A8CD50F448B81FBD7FA80D56 ipv6=[2a02:898:24:84::1]:443 # rejozenger # Email sent directly to teor, verified using relay contact info 185.129.62.62:9030 orport=9001 id=ACDD9E85A05B127BA010466C13C8C47212E8A38F ipv6=[2a06:d380:0:3700::62]:9001 @@ -818,12 +846,151 @@ # Email sent directly to teor, verified using relay contact info 85.214.151.72:9030 orport=9001 id=722D365140C8C52DBB3C9FF6986E3CEFFE2BA812 -# Email sent directly to teor, verified using relay contact info -72.52.75.27:9030 orport=9001 id=1220F0F20E80D348244C5F3B6D126DAA0A446DFD +# email sent directly to teor +72.52.75.27:9030 orport=9001 id=8567AD0A6369ED08527A8A8533A5162AC00F7678 # piecoopdotnet # Email sent directly to teor, verified using relay contact info 5.9.146.203:80 orport=443 id=1F45542A24A61BF9408F1C05E0DCE4E29F2CBA11 5.9.159.14:9030 orport=9001 id=0F100F60C7A63BED90216052324D29B08CFCF797 # Email sent directly to teor, verified using relay contact info -5.9.147.226:9030 orport=9001 id=B0553175AADB0501E5A61FC61CEA3970BE130FF2 +# Assume details update is permanent +5.9.147.226:9030 orport=9001 id=B0553175AADB0501E5A61FC61CEA3970BE130FF2 ipv6=[2a01:4f8:190:30e1::2]:9001 # zwiubel + +# https://trac.torproject.org/projects/tor/ticket/22527#comment:1 +199.184.246.250:80 orport=443 id=1F6ABD086F40B890A33C93CC4606EE68B31C9556 ipv6=[2620:124:1009:1::171]:443 + +# https://trac.torproject.org/projects/tor/ticket/24695 +163.172.53.84:143 orport=21 id=1C90D3AEADFF3BCD079810632C8B85637924A58E ipv6=[2001:bc8:24f8::]:21 # Multivac + +# Email sent directly to teor +54.36.237.163:80 orport=443 id=DB2682153AC0CCAECD2BD1E9EBE99C6815807A1E # GermanCraft2 + +# Email sent directly to teor +62.138.7.171:9030 orport=9001 id=9844B981A80B3E4B50897098E2D65167E6AEF127 # 0x3d004 +62.138.7.171:8030 orport=8001 id=9285B22F7953D7874604EEE2B470609AD81C74E9 # 0x3d005 +91.121.23.100:9030 orport=9001 id=3711E80B5B04494C971FB0459D4209AB7F2EA799 # 0x3d002 +91.121.23.100:8030 orport=8001 id=CFBBA0D858F02E40B1432A65F6D13C9BDFE7A46B # 0x3d001 +51.15.13.245:9030 orport=9001 id=CED527EAC230E7B56E5B363F839671829C3BA01B # 0x3d006 +51.15.13.245:8030 orport=8001 id=8EBB8D1CF48FE2AB95C451DA8F10DB6235F40F8A # 0x3d007 + +# Email sent directly to teor +104.192.5.248:9030 orport=9001 id=BF735F669481EE1CCC348F0731551C933D1E2278 # Freeway11 + +# Email sent directly to teor +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013961.html +178.17.174.14:9030 orport=9001 id=B06F093A3D4DFAD3E923F4F28A74901BD4F74EB1 # TorExitMoldova +178.17.170.156:9030 orport=9001 id=41C59606AFE1D1AA6EC6EF6719690B856F0B6587 # TorExitMoldova2 + +# Email sent directly to teor +163.172.221.44:59030 orport=59001 id=164604F5C86FC8CC9C0288BD9C02311958427597 # altego + +# Email sent directly to teor +46.38.237.221:9030 orport=9001 id=D30E9D4D639068611D6D96861C95C2099140B805 # mine + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013911.html +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013912.html +199.249.223.62:80 orport=443 id=0077BCBA7244DB3E6A5ED2746E86170066684887 # Quintex13 +199.249.224.45:80 orport=443 id=041646640AB306EA74B001966E86169B04CC88D2 # QuintexAirVPN26 +199.249.223.67:80 orport=443 id=155D6F57425F16C0624D77777641E4EB1B47C6F0 # Quintex18 +199.249.223.45:80 orport=443 id=1AE949967F82BBE7534A3D6BA77A7EBE1CED4369 # Quintex36 +199.249.223.63:80 orport=443 id=1DB25DF59DAA01B5BE3D3CEB8AFED115940EBE8B # Quintex14 +199.249.224.63:80 orport=443 id=1E5136DDC52FAE1219208F0A6BADB0BA62587EE6 # Quintex43 +199.249.224.46:80 orport=443 id=2ED4D25766973713EB8C56A290BF07E06B85BF12 # QuintexAirVPN27 +199.249.223.42:80 orport=443 id=3687FEC7E73F61AC66F7AE251E7DEE6BBD8C0252 # Quintex33 +199.249.223.49:80 orport=443 id=36D68478366CB8627866757EBCE7FB3C17FC1CB8 # Quintex40 +199.249.224.49:80 orport=443 id=3CA0D15567024D2E0B557DC0CF3E962B37999A79 # QuintexAirVPN30 +199.249.223.61:80 orport=443 id=40E7D6CE5085E4CDDA31D51A29D1457EB53F12AD # Quintex12 +199.249.223.76:80 orport=443 id=43209F6D50C657A56FE79AF01CA69F9EF19BD338 # QuintexAirVPN5 +199.249.224.41:80 orport=443 id=54A4820B46E65509BF3E2B892E66930A41759DE9 # QuintexAirVPN22 +199.249.223.73:80 orport=443 id=5649CB2158DA94FB747415F26628BEC07FA57616 # QuintexAirVPN8 +199.249.223.74:80 orport=443 id=5F4CD12099AF20FAF9ADFDCEC65316A376D0201C # QuintexAirVPN7 +199.249.223.75:80 orport=443 id=60D3667F56AEC5C69CF7E8F557DB21DDF6C36060 # QuintexAirVPN6 +199.249.223.46:80 orport=443 id=66E19E8C4773086F669A1E06A3F8C23B6C079129 # Quintex37 +199.249.224.65:80 orport=443 id=764BF8A03868F84C8F323C1A676AA254B80DC3BF # Quintex45 +199.249.223.48:80 orport=443 id=7A3DD280EA4CD4DD16EF8C67B93D9BDE184D1A81 # Quintex39 +199.249.224.68:80 orport=443 id=7E6E9A6FDDB8DC7C92F0CFCC3CBE76C29F061799 # Quintex48 +199.249.223.69:80 orport=443 id=7FA8E7E44F1392A4E40FFC3B69DB3B00091B7FD3 # Quintex20 +199.249.223.44:80 orport=443 id=8B80169BEF71450FC4069A190853523B7AEA45E1 # Quintex35 +199.249.224.60:80 orport=443 id=9314BD9503B9014261A65C221D77E57389DBCCC1 # Quintex50 +199.249.224.40:80 orport=443 id=9C1E7D92115D431385B8CAEA6A7C15FB89CE236B # QuintexAirVPN21 +199.249.223.65:80 orport=443 id=9D21F034C3BFF4E7737D08CF775DC1745706801F # Quintex16 +199.249.224.67:80 orport=443 id=9E2D7C6981269404AA1970B53891701A20424EF8 # Quintex47 +199.249.223.64:80 orport=443 id=9F2856F6D2B89AD4EF6D5723FAB167DB5A53519A # Quintex15 +199.249.224.48:80 orport=443 id=A0DB820FEC87C0405F7BF05DEE5E4ADED2BB9904 # QuintexAirVPN29 +199.249.224.64:80 orport=443 id=A4A393FEF48640961AACE92D041934B55348CEF9 # Quintex44 +199.249.223.72:80 orport=443 id=B028707969D8ED84E6DEA597A884F78AAD471971 # QuintexAirVPN9 +199.249.223.40:80 orport=443 id=B0CD9F9B5B60651ADC5919C0F1EAA87DBA1D9249 # Quintex31 +199.249.224.61:80 orport=443 id=B2197C23A4FF5D1C49EE45BA7688BA8BCCD89A0B # Quintex41 +199.249.223.71:80 orport=443 id=B6320E44A230302C7BF9319E67597A9B87882241 # QuintexAirVPN10 +199.249.223.60:80 orport=443 id=B7047FBDE9C53C39011CA84E5CB2A8E3543066D0 # Quintex11 +199.249.224.66:80 orport=443 id=C78AFFEEE320EA0F860961763E613FD2FAC855F5 # Quintex46 +199.249.224.44:80 orport=443 id=CB7C0D841FE376EF43F7845FF201B0290C0A239E # QuintexAirVPN25 +199.249.223.47:80 orport=443 id=CC14C97F1D23EE97766828FC8ED8582E21E11665 # Quintex38 +199.249.223.77:80 orport=443 id=CC4A3AE960E3617F49BF9887B79186C14CBA6813 # QuintexAirVPN4 +199.249.223.41:80 orport=443 id=D25210CE07C49F2A4F2BC7A506EB0F5EA7F5E2C2 # Quintex32 +199.249.223.79:80 orport=443 id=D33292FEDE24DD40F2385283E55C87F85C0943B6 # QuintexAirVPN2 +199.249.224.47:80 orport=443 id=D6FF2697CEA5C0C7DA84797C2E71163814FC2466 # QuintexAirVPN28 +199.249.223.68:80 orport=443 id=DF20497E487A979995D851A5BCEC313DF7E5BC51 # Quintex19 +199.249.223.43:80 orport=443 id=E480D577F58E782A5BC4FA6F49A6650E9389302F # Quintex34 +199.249.224.69:80 orport=443 id=EABC2DD0D47B5DB11F2D37EB3C60C2A4D91C10F2 # Quintex49 +199.249.223.78:80 orport=443 id=EC15DB62D9101481F364DE52EB8313C838BDDC29 # QuintexAirVPN3 +199.249.224.42:80 orport=443 id=F21DE9C7DE31601D9716781E17E24380887883D1 # QuintexAirVPN23 +199.249.223.81:80 orport=443 id=F7447E99EB5CBD4D5EB913EE0E35AC642B5C1EF3 # QuintexAirVPN1 +199.249.224.43:80 orport=443 id=FDD700C791CC6BB0AC1C2099A82CBC367AD4B764 # QuintexAirVPN24 +199.249.224.62:80 orport=443 id=FE00A3A835680E67FBBC895A724E2657BB253E97 # Quintex42 +199.249.223.66:80 orport=443 id=C5A53BCC174EF8FD0DCB223E4AA929FA557DEDB2 # Quintex17 + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013914.html +5.196.23.64:9030 orport=9001 id=775B0FAFDE71AADC23FFC8782B7BEB1D5A92733E # Aerodynamik01 +217.182.75.181:9030 orport=9001 id=EFEACD781604EB80FBC025EDEDEA2D523AEAAA2F # Aerodynamik02 +193.70.43.76:9030 orport=9001 id=484A10BA2B8D48A5F0216674C8DD50EF27BC32F3 # Aerodynamik03 +149.56.141.138:9030 orport=9001 id=1938EBACBB1A7BFA888D9623C90061130E63BB3F # Aerodynamik04 + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013917.html +104.200.20.46:80 orport=9001 id=78E2BE744A53631B4AAB781468E94C52AB73968B # bynumlawtor + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013929.html +139.99.130.178:80 orport=443 id=867B95CACD64653FEEC4D2CEFC5C49B4620307A7 # coffswifi2 + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013946.html +172.98.193.43:80 orport=443 id=5E56738E7F97AA81DEEF59AF28494293DFBFCCDF # Backplane + +# Email sent directly to teor +62.210.254.132:80 orport=443 id=8456DFA94161CDD99E480C2A2992C366C6564410 # turingmachine + +# Email sent directly to teor +80.127.117.180:80 orport=443 id=328E54981C6DDD7D89B89E418724A4A7881E3192 ipv6=[2001:985:e77:10::4]:443 # sjc01 + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/013960.html +51.15.205.214:9030 orport=9001 id=8B6556601612F1E2AFCE2A12FFFAF8482A76DD1F ipv6=[2001:bc8:4400:2500::5:b07]:9001 # titania1 +51.15.205.214:9031 orport=9002 id=5E363D72488276160D062DDD2DFA25CFEBAF5EA9 ipv6=[2001:bc8:4400:2500::5:b07]:9002 # titania2 + +# Email sent directly to teor +185.129.249.124:9030 orport=9001 id=1FA8F638298645BE58AC905276680889CB795A94 # treadstone + +# https://lists.torproject.org/pipermail/tor-relays/2017-December/014000.html +24.117.231.229:34175 orport=45117 id=CE24412AD69444954B4015E293AE53DDDAFEA3D6 # Anosognosia + +# https://lists.torproject.org/pipermail/tor-relays/2018-January/014012.html +128.31.0.13:80 orport=443 id=A53C46F5B157DD83366D45A8E99A244934A14C46 # csailmitexit + +# Email sent directly to teor +82.247.103.117:110 orport=995 id=C9B3C1661A9577BA24C1C2C6123918921A495509 # Casper01 +109.238.2.79:110 orport=995 id=7520892E3DD133D0B0464D01A158B54B8E2A8B75 # Casper02 +51.15.179.153:110 orport=995 id=BB60F5BA113A0B8B44B7B37DE3567FE561E92F78 # Casper04 + +# Email sent directly to teor +80.127.107.179:80 orport=443 id=BC6B2E2F62ACC5EDECBABE64DA1E48F84DD98B78 ipv6=[2001:981:4a22:c::6]:443 # TVISION02 + +# https://lists.torproject.org/pipermail/tor-relays/2018-January/014020.html +37.120.174.249:80 orport=443 id=11DF0017A43AF1F08825CD5D973297F81AB00FF3 ipv6=[2a03:4000:6:724c:df98:15f9:b34d:443]:443 # gGDHjdcC6zAlM8k08lX + +# These fallbacks opted-in in previous releases, then changed their details, +# and so we blacklisted them. Now we want to whitelist changes. +# Assume details update is permanent +85.230.184.93:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34 # Logforme +176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329 ipv6=[2001:41d0:8:eb9d::1]:22 # armbrust + +# https://lists.torproject.org/pipermail/tor-relays/2018-January/014024.html +82.161.212.209:9030 orport=9001 id=4E8CE6F5651E7342C1E7E5ED031E82078134FB0D ipv6=[2001:980:d7ed:1:ff:b0ff:fe00:d0b]:9001 # ymkeo diff --git a/scripts/maint/generateFallbackDirLine.py b/scripts/maint/generateFallbackDirLine.py new file mode 100755 index 0000000000..b856c938bf --- /dev/null +++ b/scripts/maint/generateFallbackDirLine.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +# Generate a fallback directory whitelist/blacklist line for every fingerprint +# passed as an argument. +# +# Usage: +# generateFallbackDirLine.py fingerprint ... + +import sys +import urllib2 + +import stem.descriptor.remote +import stem.util.tor_tools + +if len(sys.argv) <= 1: + print('Usage: %s fingerprint ...' % sys.argv[0]) + sys.exit(1) + +for fingerprint in sys.argv[1:]: + if not stem.util.tor_tools.is_valid_fingerprint(fingerprint): + print("'%s' isn't a valid relay fingerprint" % fingerprint) + sys.exit(1) + + try: + desc = stem.descriptor.remote.get_server_descriptors(fingerprint).run()[0] + except urllib2.HTTPError as exc: + if exc.code == 404: + print('# %s not found in recent descriptors' % fingerprint) + continue + else: + raise + + if not desc.dir_port: + print("# %s needs a DirPort" % fingerprint) + else: + ipv6_addresses = [(address, port) for address, port, is_ipv6 in desc.or_addresses if is_ipv6] + ipv6_field = ' ipv6=[%s]:%s' % ipv6_addresses[0] if ipv6_addresses else '' + print('%s:%s orport=%s id=%s%s # %s' % (desc.address, desc.dir_port, desc.or_port, fingerprint, ipv6_field, desc.nickname)) diff --git a/scripts/maint/lookupFallbackDirContact.py b/scripts/maint/lookupFallbackDirContact.py new file mode 100755 index 0000000000..14c53d1282 --- /dev/null +++ b/scripts/maint/lookupFallbackDirContact.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +# Lookup fallback directory contact lines for every fingerprint passed as an +# argument. +# +# Usage: +# lookupFallbackDirContact.py fingerprint ... + +import sys + +import stem.descriptor.remote as remote + +if len(sys.argv) <= 1: + print "Usage: {} fingerprint ...".format(sys.argv[0]) + sys.exit(-1) + +# we need descriptors, because the consensus does not have contact infos +descriptor_list = remote.get_server_descriptors(fingerprints=sys.argv[1:]).run() + +descriptor_list_fingerprints = [] +for d in descriptor_list: + assert d.fingerprint in sys.argv[1:] + descriptor_list_fingerprints.append(d.fingerprint) + print "{} {}".format(d.fingerprint, d.contact) + +for fingerprint in sys.argv[1:]: + if fingerprint not in descriptor_list_fingerprints: + print "{} not found in current descriptors".format(fingerprint) diff --git a/scripts/maint/redox.py b/scripts/maint/redox.py index 12aed6463a..53d3d902eb 100755 --- a/scripts/maint/redox.py +++ b/scripts/maint/redox.py @@ -101,7 +101,7 @@ def read(): def findline(lines, lineno, ident): """Given a list of all the lines in the file (adjusted so 1-indexing works), - a line number that ident is alledgedly on, and ident, I figure out + a line number that ident is allegedly on, and ident, I figure out the line where ident was really declared.""" lno = lineno for lineno in xrange(lineno, 0, -1): diff --git a/scripts/maint/updateFallbackDirs.py b/scripts/maint/updateFallbackDirs.py index 82a60420b4..c854830e18 100755 --- a/scripts/maint/updateFallbackDirs.py +++ b/scripts/maint/updateFallbackDirs.py @@ -1,12 +1,12 @@ -#!/usr/bin/python +#!/usr/bin/env python # Usage: # # Regenerate the list: -# scripts/maint/updateFallbackDirs.py > src/or/fallback_dirs.inc +# scripts/maint/updateFallbackDirs.py > src/or/fallback_dirs.inc 2> fallback_dirs.log # # Check the existing list: -# scripts/maint/updateFallbackDirs.py check_existing > fallback_dirs.inc.ok +# scripts/maint/updateFallbackDirs.py check_existing > fallback_dirs.inc.ok 2> fallback_dirs.log # mv fallback_dirs.inc.ok src/or/fallback_dirs.inc # # This script should be run from a stable, reliable network connection, @@ -14,14 +14,12 @@ # If this is not possible, please disable: # PERFORM_IPV4_DIRPORT_CHECKS and PERFORM_IPV6_DIRPORT_CHECKS # -# Needs dateutil (and potentially other python packages) -# Needs stem available in your PYTHONPATH, or just ln -s ../stem/stem . +# Needs dateutil, stem, and potentially other python packages. # Optionally uses ipaddress (python 3 builtin) or py2-ipaddress (package) -# for netblock analysis, in PYTHONPATH, or just -# ln -s ../py2-ipaddress-3.4.1/ipaddress.py . +# for netblock analysis. # # Then read the logs to make sure the fallbacks aren't dominated by a single -# netblock or port +# netblock or port. # Script by weasel, April 2015 # Portions by gsathya & karsten, 2013 @@ -47,7 +45,7 @@ import copy import re from stem.descriptor import DocumentHandler -from stem.descriptor.remote import get_consensus +from stem.descriptor.remote import get_consensus, get_server_descriptors, MAX_FINGERPRINTS import logging logging.root.name = '' @@ -68,6 +66,17 @@ except ImportError: ## Top-Level Configuration +# We use semantic versioning: https://semver.org +# In particular: +# * major changes include removing a mandatory field, or anything else that +# would break an appropriately tolerant parser, +# * minor changes include adding a field, +# * patch changes include changing header comments or other unstructured +# content +FALLBACK_FORMAT_VERSION = '2.0.0' +SECTION_SEPARATOR_BASE = '=====' +SECTION_SEPARATOR_COMMENT = '/* ' + SECTION_SEPARATOR_BASE + ' */' + # Output all candidate fallbacks, or only output selected fallbacks? OUTPUT_CANDIDATES = False @@ -98,11 +107,12 @@ DOWNLOAD_MICRODESC_CONSENSUS = True # reject consensuses that are older than REASONABLY_LIVE_TIME. # For the consensus expiry check to be accurate, the machine running this # script needs an accurate clock. -# We use 24 hours to compensate for #20909, where relays on 0.2.9.5-alpha and -# 0.3.0.0-alpha-dev and later deliver stale consensuses, but typically recover -# after ~12 hours. -# We should make this lower when #20909 is fixed, see #20942. -CONSENSUS_EXPIRY_TOLERANCE = 24*60*60 +# +# Relays on 0.3.0 and later return a 404 when they are about to serve an +# expired consensus. This makes them fail the download check. +# We use a tolerance of 0, so that 0.2.x series relays also fail the download +# check if they serve an expired consensus. +CONSENSUS_EXPIRY_TOLERANCE = 0 # Output fallback name, flags, bandwidth, and ContactInfo in a C comment? OUTPUT_COMMENTS = True if OUTPUT_CANDIDATES else False @@ -160,25 +170,30 @@ MAX_LIST_FILE_SIZE = 1024 * 1024 # Require fallbacks to have the same address and port for a set amount of time # We used to have this at 1 week, but that caused many fallback failures, which -# meant that we had to rebuild the list more often. +# meant that we had to rebuild the list more often. We want fallbacks to be +# stable for 2 years, so we set it to a few months. # # There was a bug in Tor 0.2.8.1-alpha and earlier where a relay temporarily # submits a 0 DirPort when restarted. # This causes OnionOO to (correctly) reset its stability timer. -# Affected relays should upgrade to Tor 0.2.8.7 or later, which has a fix +# Affected relays should upgrade to Tor 0.2.9 or later, which has a fix # for this issue. -ADDRESS_AND_PORT_STABLE_DAYS = 30 +# +# If a relay changes address or port, that's it, it's not useful any more, +# because clients can't find it +ADDRESS_AND_PORT_STABLE_DAYS = 90 # We ignore relays that have been down for more than this period MAX_DOWNTIME_DAYS = 0 if MUST_BE_RUNNING_NOW else 7 -# What time-weighted-fraction of these flags must FallbackDirs -# Equal or Exceed? -CUTOFF_RUNNING = .90 -CUTOFF_V2DIR = .90 -# Tolerate lower guard flag averages, as guard flags are removed for some time -# after a relay restarts -CUTOFF_GUARD = .80 -# What time-weighted-fraction of these flags must FallbackDirs -# Equal or Fall Under? +# FallbackDirs must have a time-weighted-fraction that is greater than or +# equal to: +# Mirrors that are down half the time are still useful half the time +CUTOFF_RUNNING = .50 +CUTOFF_V2DIR = .50 +# Guard flags are removed for some time after a relay restarts, so we ignore +# the guard flag. +CUTOFF_GUARD = .00 +# FallbackDirs must have a time-weighted-fraction that is less than or equal +# to: # .00 means no bad exits PERMITTED_BADEXIT = .00 @@ -201,17 +216,23 @@ MAX_FALLBACK_COUNT = None if OUTPUT_CANDIDATES else 200 MIN_FALLBACK_COUNT = 0 if OUTPUT_CANDIDATES else MAX_FALLBACK_COUNT*0.5 # The maximum number of fallbacks on the same address, contact, or family -# With 200 fallbacks, this means each operator can see 1% of client bootstraps -# (The directory authorities used to see ~12% of client bootstraps each.) +# +# With 150 fallbacks, this means each operator sees 5% of client bootstraps. +# For comparison: +# - We try to limit guard and exit operators to 5% of the network +# - The directory authorities used to see 11% of client bootstraps each +# +# We also don't want too much of the list to go down if a single operator +# has to move all their relays. MAX_FALLBACKS_PER_IP = 1 MAX_FALLBACKS_PER_IPV4 = MAX_FALLBACKS_PER_IP MAX_FALLBACKS_PER_IPV6 = MAX_FALLBACKS_PER_IP -MAX_FALLBACKS_PER_CONTACT = 3 -MAX_FALLBACKS_PER_FAMILY = 3 +MAX_FALLBACKS_PER_CONTACT = 7 +MAX_FALLBACKS_PER_FAMILY = 7 ## Fallback Bandwidth Requirements -# Any fallback with the Exit flag has its bandwidth multipled by this fraction +# Any fallback with the Exit flag has its bandwidth multiplied by this fraction # to make sure we aren't further overloading exits # (Set to 1.0, because we asked that only lightly loaded exits opt-in, # and the extra load really isn't that much for large relays.) @@ -219,11 +240,11 @@ EXIT_BANDWIDTH_FRACTION = 1.0 # If a single fallback's bandwidth is too low, it's pointless adding it # We expect fallbacks to handle an extra 10 kilobytes per second of traffic -# Make sure they can support a hundred times the expected extra load -# (Use 102.4 to make it come out nicely in MByte/s) +# Make sure they can support fifty times the expected extra load +# # We convert this to a consensus weight before applying the filter, # because all the bandwidth amounts are specified by the relay -MIN_BANDWIDTH = 102.4 * 10.0 * 1024.0 +MIN_BANDWIDTH = 50.0 * 10.0 * 1024.0 # Clients will time out after 30 seconds trying to download a consensus # So allow fallback directories half that to deliver a consensus @@ -235,21 +256,6 @@ CONSENSUS_DOWNLOAD_SPEED_MAX = 15.0 # This avoids delisting a relay due to transient network conditions CONSENSUS_DOWNLOAD_RETRY = True -## Fallback Weights for Client Selection - -# All fallback weights are equal, and set to the value below -# Authorities are weighted 1.0 by default -# Clients use these weights to select fallbacks and authorities at random -# If there are 100 fallbacks and 9 authorities: -# - each fallback is chosen with probability 10.0/(10.0*100 + 1.0*9) ~= 0.99% -# - each authority is chosen with probability 1.0/(10.0*100 + 1.0*9) ~= 0.09% -# A client choosing a bootstrap directory server will choose a fallback for -# 10.0/(10.0*100 + 1.0*9) * 100 = 99.1% of attempts, and an authority for -# 1.0/(10.0*100 + 1.0*9) * 9 = 0.9% of attempts. -# (This disregards the bootstrap schedules, where clients start by choosing -# from fallbacks & authoritites, then later choose from only authorities.) -FALLBACK_OUTPUT_WEIGHT = 10.0 - ## Parsing Functions def parse_ts(t): @@ -289,6 +295,10 @@ def cleanse_c_multiline_comment(raw_string): bad_char_list = '*/' # Prevent a malicious string from using C nulls bad_char_list += '\0' + # Avoid confusing parsers by making sure there is only one comma per fallback + bad_char_list += ',' + # Avoid confusing parsers by making sure there is only one equals per field + bad_char_list += '=' # Be safer by removing bad characters entirely cleansed_string = remove_bad_chars(cleansed_string, bad_char_list) # Some compilers may further process the content of comments @@ -309,6 +319,10 @@ def cleanse_c_string(raw_string): bad_char_list += '\\' # Prevent a malicious string from using C nulls bad_char_list += '\0' + # Avoid confusing parsers by making sure there is only one comma per fallback + bad_char_list += ',' + # Avoid confusing parsers by making sure there is only one equals per field + bad_char_list += '=' # Be safer by removing bad characters entirely cleansed_string = remove_bad_chars(cleansed_string, bad_char_list) # Some compilers may further process the content of strings @@ -547,7 +561,7 @@ class Candidate(object): details['flags'] = [] if (not 'advertised_bandwidth' in details or details['advertised_bandwidth'] is None): - # relays without advertised bandwdith have it calculated from their + # relays without advertised bandwidth have it calculated from their # consensus weight details['advertised_bandwidth'] = 0 if (not 'effective_family' in details @@ -570,6 +584,7 @@ class Candidate(object): if not self.has_ipv6(): logging.debug("Failed to get an ipv6 address for %s."%(self._fpr,)) self._compute_version() + self._extra_info_cache = None def _stable_sort_or_addresses(self): # replace self._data['or_addresses'] with a stable ordering, @@ -1335,8 +1350,14 @@ class Candidate(object): # comment-out the returned string def fallbackdir_info(self, dl_speed_ok): # "address:dirport orport=port id=fingerprint" + # (insert additional madatory fields here) # "[ipv6=addr:orport]" - # "weight=FALLBACK_OUTPUT_WEIGHT", + # (insert additional optional fields here) + # /* nickname=name */ + # /* extrainfo={0,1} */ + # (insert additional comment fields here) + # /* ===== */ + # , # # Do we want a C string, or a commented-out string? c_string = dl_speed_ok @@ -1357,10 +1378,34 @@ class Candidate(object): self.orport, cleanse_c_string(self._fpr)) s += '\n' + # (insert additional madatory fields here) if self.has_ipv6(): s += '" ipv6=%s:%d"'%(cleanse_c_string(self.ipv6addr), self.ipv6orport) s += '\n' - s += '" weight=%d",'%(FALLBACK_OUTPUT_WEIGHT) + # (insert additional optional fields here) + if not comment_string: + s += '/* ' + s += 'nickname=%s'%(cleanse_c_string(self._data['nickname'])) + if not comment_string: + s += ' */' + s += '\n' + # if we know that the fallback is an extrainfo cache, flag it + # and if we don't know, assume it is not + if not comment_string: + s += '/* ' + s += 'extrainfo=%d'%(1 if self._extra_info_cache else 0) + if not comment_string: + s += ' */' + s += '\n' + # (insert additional comment fields here) + # The terminator and comma must be the last line in each fallback entry + if not comment_string: + s += '/* ' + s += SECTION_SEPARATOR_BASE + if not comment_string: + s += ' */' + s += '\n' + s += ',' if comment_string: s += '\n' s += '*/' @@ -1550,7 +1595,7 @@ class CandidateList(dict): excluded_count, initial_count) # calculate each fallback's measured bandwidth based on the median - # consensus weight to advertised bandwdith ratio + # consensus weight to advertised bandwidth ratio def calculate_measured_bandwidth(self): self.sort_fallbacks_by_cw_to_bw_factor() median_fallback = self.fallback_median(True) @@ -1745,6 +1790,53 @@ class CandidateList(dict): self.fallbacks = family_limit_fallbacks return original_count - len(self.fallbacks) + # try once to get the descriptors for fingerprint_list using stem + # returns an empty list on exception + @staticmethod + def get_fallback_descriptors_once(fingerprint_list): + desc_list = get_server_descriptors(fingerprints=fingerprint_list).run(suppress=True) + return desc_list + + # try up to max_retries times to get the descriptors for fingerprint_list + # using stem. Stops retrying when all descriptors have been retrieved. + # returns a list containing the descriptors that were retrieved + @staticmethod + def get_fallback_descriptors(fingerprint_list, max_retries=5): + # we can't use stem's retries=, because we want to support more than 96 + # descriptors + # + # add an attempt for every MAX_FINGERPRINTS (or part thereof) in the list + max_retries += (len(fingerprint_list) + MAX_FINGERPRINTS - 1) / MAX_FINGERPRINTS + remaining_list = fingerprint_list + desc_list = [] + for _ in xrange(max_retries): + if len(remaining_list) == 0: + break + new_desc_list = CandidateList.get_fallback_descriptors_once(remaining_list[0:MAX_FINGERPRINTS]) + for d in new_desc_list: + try: + remaining_list.remove(d.fingerprint) + except ValueError: + # warn and ignore if a directory mirror returned a bad descriptor + logging.warning("Directory mirror returned unwanted descriptor %s, ignoring", + d.fingerprint) + continue + desc_list.append(d) + return desc_list + + # find the fallbacks that cache extra-info documents + # Onionoo doesn't know this, so we have to use stem + def mark_extra_info_caches(self): + fingerprint_list = [ f._fpr for f in self.fallbacks ] + logging.info("Downloading fallback descriptors to find extra-info caches") + desc_list = CandidateList.get_fallback_descriptors(fingerprint_list) + for d in desc_list: + self[d.fingerprint]._extra_info_cache = d.extra_info_cache + missing_descriptor_list = [ f._fpr for f in self.fallbacks + if f._extra_info_cache is None ] + for f in missing_descriptor_list: + logging.warning("No descriptor for {}. Assuming extrainfo=0.".format(f)) + # try a download check on each fallback candidate in order # stop after max_count successful downloads # but don't remove any candidates from the array @@ -1904,7 +1996,7 @@ class CandidateList(dict): # this doesn't actually tell us anything useful #self.describe_fallback_ipv4_netblock_mask(8) self.describe_fallback_ipv4_netblock_mask(16) - self.describe_fallback_ipv4_netblock_mask(24) + #self.describe_fallback_ipv4_netblock_mask(24) # log a message about the proportion of fallbacks in each IPv6 /12 (RIR), # /23 (smaller RIR blocks), /32 (LIR), /48 (Customer), and /64 (Host) @@ -1914,7 +2006,7 @@ class CandidateList(dict): #self.describe_fallback_ipv6_netblock_mask(12) #self.describe_fallback_ipv6_netblock_mask(23) self.describe_fallback_ipv6_netblock_mask(32) - self.describe_fallback_ipv6_netblock_mask(48) + #self.describe_fallback_ipv6_netblock_mask(48) self.describe_fallback_ipv6_netblock_mask(64) # log a message about the proportion of fallbacks in each IPv4 and IPv6 @@ -1992,6 +2084,18 @@ class CandidateList(dict): CandidateList.describe_percentage(dir_count, fallback_count))) + # return a list of fallbacks which cache extra-info documents + def fallbacks_with_extra_info_cache(self): + return filter(lambda x: x._extra_info_cache, self.fallbacks) + + # log a message about the proportion of fallbacks that cache extra-info docs + def describe_fallback_extra_info_caches(self): + extra_info_falback_count = len(self.fallbacks_with_extra_info_cache()) + fallback_count = len(self.fallbacks) + logging.warning('%s of fallbacks cache extra-info documents'%( + CandidateList.describe_percentage(extra_info_falback_count, + fallback_count))) + # return a list of fallbacks which have the Exit flag def fallbacks_with_exit(self): return filter(lambda x: x.is_exit(), self.fallbacks) @@ -2019,10 +2123,6 @@ class CandidateList(dict): def summarise_fallbacks(self, eligible_count, operator_count, failed_count, guard_count, target_count): s = '' - s += '/* To comment-out entries in this file, use C comments, and add *' - s += ' to the start of each line. (stem finds fallback entries using "' - s += ' at the start of a line.) */' - s += '\n' # Report: # whether we checked consensus download times # the number of fallback directories (and limits/exclusions, if relevant) @@ -2123,6 +2223,16 @@ def list_fallbacks(whitelist, blacklist): """ Fetches required onionoo documents and evaluates the fallback directory criteria for each of the relays """ + print "/* type=fallback */" + print ("/* version={} */" + .format(cleanse_c_multiline_comment(FALLBACK_FORMAT_VERSION))) + now = datetime.datetime.utcnow() + timestamp = now.strftime('%Y%m%d%H%M%S') + print ("/* timestamp={} */" + .format(cleanse_c_multiline_comment(timestamp))) + # end the header with a separator, to make it easier for parsers + print SECTION_SEPARATOR_COMMENT + logging.warning('Downloading and parsing Onionoo data. ' + 'This may take some time.') # find relays that could be fallbacks @@ -2188,6 +2298,9 @@ def list_fallbacks(whitelist, blacklist): 'This may take some time.') failed_count = candidates.perform_download_consensus_checks(max_count) + # work out which fallbacks cache extra-infos + candidates.mark_extra_info_caches() + # analyse and log interesting diversity metrics # like netblock, ports, exit, IPv4-only # (we can't easily analyse AS, and it's hard to accurately analyse country) @@ -2196,6 +2309,7 @@ def list_fallbacks(whitelist, blacklist): if HAVE_IPADDRESS: candidates.describe_fallback_netblocks() candidates.describe_fallback_ports() + candidates.describe_fallback_extra_info_caches() candidates.describe_fallback_exit_flag() # output C comments summarising the fallback selection process @@ -2210,6 +2324,9 @@ def list_fallbacks(whitelist, blacklist): for s in fetch_source_list(): print describe_fetch_source(s) + # start the list with a separator, to make it easy for parsers + print SECTION_SEPARATOR_COMMENT + # sort the list differently depending on why we've created it: # if we're outputting the final fallback list, sort by fingerprint # this makes diffs much more stable diff --git a/scripts/test/scan-build.sh b/scripts/test/scan-build.sh index 793adf87e2..8d126cbcee 100755 --- a/scripts/test/scan-build.sh +++ b/scripts/test/scan-build.sh @@ -26,9 +26,9 @@ CHECKERS="\ -enable-checker alpha.unix.Stream \ -enable-checker alpha.unix.cstring.BufferOverlap \ -enable-checker alpha.unix.cstring.NotNullTerminated \ - -enable-checker alpha.valist.CopyToSelf \ - -enable-checker alpha.valist.Uninitialized \ - -enable-checker alpha.valist.Unterminated \ + -enable-checker valist.CopyToSelf \ + -enable-checker valist.Uninitialized \ + -enable-checker valist.Unterminated \ -enable-checker security.FloatLoopCounter \ -enable-checker security.insecureAPI.strcpy \ " diff --git a/src/common/address.c b/src/common/address.c index c683e90aac..a2f4c93b91 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1125,7 +1125,7 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, case AF_UNIX: /* HACKHACKHACKHACKHACK: * tor_addr_t doesn't contain a copy of sun_path, so it's not - * possible to comapre this at all. + * possible to compare this at all. * * Since the only time we currently actually should be comparing * 2 AF_UNIX addresses is when dealing with ISO_CLIENTADDR (which @@ -1540,6 +1540,18 @@ get_interface_addresses_win32(int severity, sa_family_t family) #define _SIZEOF_ADDR_IFREQ sizeof #endif +/* Free ifc->ifc_buf safely. */ +static void +ifconf_free_ifc_buf(struct ifconf *ifc) +{ + /* On macOS, tor_free() takes the address of ifc.ifc_buf, which leads to + * undefined behaviour, because pointer-to-pointers are expected to be + * aligned at 8-bytes, but the ifconf structure is packed. So we use + * raw_free() instead. */ + raw_free(ifc->ifc_buf); + ifc->ifc_buf = NULL; +} + /** Convert <b>*buf</b>, an ifreq structure array of size <b>buflen</b>, * into smartlist of <b>tor_addr_t</b> structures. */ @@ -1626,7 +1638,7 @@ get_interface_addresses_ioctl(int severity, sa_family_t family) done: if (fd >= 0) close(fd); - tor_free(ifc.ifc_buf); + ifconf_free_ifc_buf(&ifc); return result; } #endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */ @@ -1784,14 +1796,14 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) break; } SMARTLIST_FOREACH_END(a); - free_interface_address6_list(addrs); + interface_address6_list_free(addrs); return rv; } /** Free a smartlist of IP addresses returned by get_interface_address6_list. */ void -free_interface_address6_list(smartlist_t *addrs) +interface_address6_list_free_(smartlist_t *addrs) { if (addrs != NULL) { SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); @@ -1806,7 +1818,7 @@ free_interface_address6_list(smartlist_t *addrs) * An empty smartlist means that there are no addresses of the selected type * matching these criteria. * Returns NULL on failure. - * Use free_interface_address6_list to free the returned list. + * Use interface_address6_list_free to free the returned list. */ MOCK_IMPL(smartlist_t *, get_interface_address6_list,(int severity, diff --git a/src/common/address.h b/src/common/address.h index 2b9546782e..c9d9543dee 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -206,7 +206,9 @@ const char * fmt_addr32(uint32_t addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); -void free_interface_address6_list(smartlist_t * addrs); +void interface_address6_list_free_(smartlist_t * addrs);// XXXX +#define interface_address6_list_free(addrs) \ + FREE_AND_NULL(smartlist_t, interface_address6_list_free_, (addrs)) MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity, sa_family_t family, int include_internal)); @@ -323,13 +325,8 @@ int addr_mask_get_bits(uint32_t mask); int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr)); -/** Free a smartlist of IP addresses returned by get_interface_address_list. - */ -static inline void -free_interface_address_list(smartlist_t *addrs) -{ - free_interface_address6_list(addrs); -} +#define interface_address_list_free(lst)\ + interface_address6_list_free(lst) /** Return a smartlist of the IPv4 addresses of all interfaces on the server. * Excludes loopback and multicast addresses. Only includes internal addresses * if include_internal is true. (Note that a relay behind NAT may use an diff --git a/src/common/address_set.c b/src/common/address_set.c index 4924cb65c2..f61fa294e0 100644 --- a/src/common/address_set.c +++ b/src/common/address_set.c @@ -34,7 +34,7 @@ * independent siphashes rather than messing around with bit-shifts. The * approach here is probably more sound, and we should prefer it if&when we * unify the implementations. - **/ + */ struct address_set_t { /** siphash keys to make N_HASHES independent hashes for each address. */ @@ -63,7 +63,7 @@ address_set_new(int max_addresses_guess) } /** - * Release all storage associated with <b>set</b> + * Release all storage associated with <b>set</b>. */ void address_set_free(address_set_t *set) @@ -107,7 +107,7 @@ address_set_add_ipv4h(address_set_t *set, uint32_t addr) } /** - * Return true if <b>addr</b> if a member of <b>set</b>. (And probably, + * Return true if <b>addr</b> is a member of <b>set</b>. (And probably, * return false if <b>addr</b> is not a member of set.) */ int diff --git a/src/common/address_set.h b/src/common/address_set.h index aedf17fc66..28d29f3fdf 100644 --- a/src/common/address_set.h +++ b/src/common/address_set.h @@ -2,7 +2,7 @@ /* See LICENSE for licensing information */ /** - * \file addressset.h + * \file address_set.h * \brief Types to handle sets of addresses. * * This module was first written on a semi-emergency basis to improve the diff --git a/src/common/aes.c b/src/common/aes.c index 20b51a6758..5d0841dfa3 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -18,6 +18,7 @@ #include <openssl/opensslv.h> #include "crypto.h" +#include "crypto_openssl_mgt.h" #if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0) #error "We require OpenSSL >= 1.0.0" @@ -110,7 +111,7 @@ aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits) return (aes_cnt_cipher_t *) cipher; } void -aes_cipher_free(aes_cnt_cipher_t *cipher_) +aes_cipher_free_(aes_cnt_cipher_t *cipher_) { if (!cipher_) return; @@ -254,7 +255,7 @@ evaluate_ctr_for_aes(void) /* LCOV_EXCL_START */ log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; " "quitting tor."); - exit(1); + exit(1); // exit ok: openssl is broken. /* LCOV_EXCL_STOP */ } return 0; @@ -324,7 +325,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits) /** Release storage held by <b>cipher</b> */ void -aes_cipher_free(aes_cnt_cipher_t *cipher) +aes_cipher_free_(aes_cnt_cipher_t *cipher) { if (!cipher) return; diff --git a/src/common/aes.h b/src/common/aes.h index 1e400a56e0..0b17cd55a4 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -17,7 +17,9 @@ typedef struct aes_cnt_cipher aes_cnt_cipher_t; aes_cnt_cipher_t* aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits); -void aes_cipher_free(aes_cnt_cipher_t *cipher); +void aes_cipher_free_(aes_cnt_cipher_t *cipher); +#define aes_cipher_free(cipher) \ + FREE_AND_NULL(aes_cnt_cipher_t, aes_cipher_free_, (cipher)) void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); int evaluate_evp_for_aes(int force_value); diff --git a/src/common/buffers.c b/src/common/buffers.c index c45e13d551..a01add9bef 100644 --- a/src/common/buffers.c +++ b/src/common/buffers.c @@ -409,7 +409,7 @@ buf_slack(const buf_t *buf) /** Release storage held by <b>buf</b>. */ void -buf_free(buf_t *buf) +buf_free_(buf_t *buf) { if (!buf) return; @@ -472,7 +472,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) chunk = chunk_new_with_alloc_size(buf_preferred_chunk_size(capacity)); } - chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec(); + chunk->inserted_time = monotime_coarse_get_stamp(); if (buf->tail) { tor_assert(buf->head); @@ -487,8 +487,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) } /** Return the age of the oldest chunk in the buffer <b>buf</b>, in - * milliseconds. Requires the current monotonic time, in truncated msec, - * as its input <b>now</b>. + * timestamp units. Requires the current monotonic timestamp as its + * input <b>now</b>. */ uint32_t buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now) @@ -714,6 +714,53 @@ buf_add(buf_t *buf, const char *string, size_t string_len) return (int)buf->datalen; } +/** Add a nul-terminated <b>string</b> to <b>buf</b>, not including the + * terminating NUL. */ +void +buf_add_string(buf_t *buf, const char *string) +{ + buf_add(buf, string, strlen(string)); +} + +/** As tor_snprintf, but write the results into a buf_t */ +void +buf_add_printf(buf_t *buf, const char *format, ...) +{ + va_list ap; + va_start(ap,format); + buf_add_vprintf(buf, format, ap); + va_end(ap); +} + +/** As tor_vsnprintf, but write the results into a buf_t. */ +void +buf_add_vprintf(buf_t *buf, const char *format, va_list args) +{ + /* XXXX Faster implementations are easy enough, but let's optimize later */ + char *tmp; + tor_vasprintf(&tmp, format, args); + buf_add(buf, tmp, strlen(tmp)); + tor_free(tmp); +} + +/** Return a heap-allocated string containing the contents of <b>buf</b>, plus + * a NUL byte. If <b>sz_out</b> is provided, set *<b>sz_out</b> to the length + * of the returned string, not including the terminating NUL. */ +char * +buf_extract(buf_t *buf, size_t *sz_out) +{ + tor_assert(buf); + + size_t sz = buf_datalen(buf); + char *result; + result = tor_malloc(sz+1); + buf_peek(buf, result, sz); + result[sz] = 0; + if (sz_out) + *sz_out = sz; + return result; +} + /** Helper: copy the first <b>string_len</b> bytes from <b>buf</b> * onto <b>string</b>. */ @@ -795,6 +842,28 @@ buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen) return (int)cp; } +/** Moves all data from <b>buf_in</b> to <b>buf_out</b>, without copying. + */ +void +buf_move_all(buf_t *buf_out, buf_t *buf_in) +{ + tor_assert(buf_out); + if (!buf_in) + return; + + if (buf_out->head == NULL) { + buf_out->head = buf_in->head; + buf_out->tail = buf_in->tail; + } else { + buf_out->tail->next = buf_in->head; + buf_out->tail = buf_in->tail; + } + + buf_out->datalen += buf_in->datalen; + buf_in->head = buf_in->tail = NULL; + buf_in->datalen = 0; +} + /** Internal structure: represents a position in a buffer. */ typedef struct buf_pos_t { const chunk_t *chunk; /**< Which chunk are we pointing to? */ diff --git a/src/common/buffers.h b/src/common/buffers.h index 1eaa5f2d04..22a5f7bfa3 100644 --- a/src/common/buffers.h +++ b/src/common/buffers.h @@ -24,7 +24,8 @@ struct tor_compress_state_t; buf_t *buf_new(void); buf_t *buf_new_with_capacity(size_t size); size_t buf_get_default_chunk_size(const buf_t *buf); -void buf_free(buf_t *buf); +void buf_free_(buf_t *buf); +#define buf_free(b) FREE_AND_NULL(buf_t, buf_free_, (b)) void buf_clear(buf_t *buf); buf_t *buf_copy(const buf_t *buf); @@ -43,9 +44,15 @@ int buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz, size_t *buf_flushlen); int buf_add(buf_t *buf, const char *string, size_t string_len); +void buf_add_string(buf_t *buf, const char *string); +void buf_add_printf(buf_t *buf, const char *format, ...) + CHECK_PRINTF(2, 3); +void buf_add_vprintf(buf_t *buf, const char *format, va_list args) + CHECK_PRINTF(2, 0); int buf_add_compress(buf_t *buf, struct tor_compress_state_t *state, const char *data, size_t data_len, int done); int buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen); +void buf_move_all(buf_t *buf_out, buf_t *buf_in); void buf_peek(const buf_t *buf, char *string, size_t string_len); void buf_drain(buf_t *buf, size_t n); int buf_get_bytes(buf_t *buf, char *string, size_t string_len); @@ -62,6 +69,7 @@ void buf_assert_ok(buf_t *buf); int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); void buf_pullup(buf_t *buf, size_t bytes, const char **head_out, size_t *len_out); +char *buf_extract(buf_t *buf, size_t *sz_out); #ifdef BUFFERS_PRIVATE #ifdef TOR_UNIT_TESTS @@ -79,8 +87,7 @@ typedef struct chunk_t { size_t DBG_alloc; #endif char *data; /**< A pointer to the first byte of data stored in <b>mem</b>. */ - uint32_t inserted_time; /**< Timestamp in truncated ms since epoch - * when this chunk was inserted. */ + uint32_t inserted_time; /**< Timestamp when this chunk was inserted. */ char mem[FLEXIBLE_ARRAY_MEMBER]; /**< The actual memory used for storage in * this chunk. */ } chunk_t; diff --git a/src/common/compat.c b/src/common/compat.c index 83bb707e17..4cb346dfa5 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1186,7 +1186,7 @@ mark_socket_open(tor_socket_t s) bitarray_set(open_sockets, s); } #else /* !(defined(DEBUG_SOCKET_COUNTING)) */ -#define mark_socket_open(s) STMT_NIL +#define mark_socket_open(s) ((void) (s)) #endif /* defined(DEBUG_SOCKET_COUNTING) */ /** @} */ @@ -1273,11 +1273,22 @@ tor_open_socket_with_extensions(int domain, int type, int protocol, goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: + tor_take_socket_ownership(s); + return s; +} + +/** + * For socket accounting: remember that we are the owner of the socket + * <b>s</b>. This will prevent us from overallocating sockets, and prevent us + * from asserting later when we close the socket <b>s</b>. + */ +void +tor_take_socket_ownership(tor_socket_t s) +{ socket_accounting_lock(); ++n_sockets_open; mark_socket_open(s); socket_accounting_unlock(); - return s; } /** As accept(), but counts the number of open sockets. */ @@ -1358,10 +1369,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); + tor_take_socket_ownership(s); return s; } @@ -1897,9 +1905,12 @@ tor_passwd_dup(const struct passwd *pw) return new_pw; } +#define tor_passwd_free(pw) \ + FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw)) + /** Helper: free one of our cached 'struct passwd' values. */ static void -tor_passwd_free(struct passwd *pw) +tor_passwd_free_(struct passwd *pw) { if (!pw) return; @@ -2444,7 +2455,7 @@ get_environment(void) /** Get name of current host and write it to <b>name</b> array, whose * length is specified by <b>namelen</b> argument. Return 0 upon - * successfull completion; otherwise return return -1. (Currently, + * successful completion; otherwise return return -1. (Currently, * this function is merely a mockable wrapper for POSIX gethostname().) */ MOCK_IMPL(int, @@ -2879,7 +2890,7 @@ compute_num_cpus(void) /** Helper: Deal with confused or out-of-bounds values from localtime_r and * friends. (On some platforms, they can give out-of-bounds values or can * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise - * it's from gmtime. The function returned <b>r</b>, when given <b>timep</b> + * it's from gmtime. The function returns <b>r</b>, when given <b>timep</b> * as its input. If we need to store new results, store them in * <b>resultbuf</b>. */ static struct tm * diff --git a/src/common/compat.h b/src/common/compat.h index fee9e6587d..93301feda0 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -10,6 +10,9 @@ #ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> +#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY +#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747b +#endif #endif #include "torint.h" #include "testsupport.h" @@ -483,6 +486,7 @@ typedef int socklen_t; int tor_close_socket_simple(tor_socket_t s); MOCK_DECL(int, tor_close_socket, (tor_socket_t s)); +void tor_take_socket_ownership(tor_socket_t s); tor_socket_t tor_open_socket_with_extensions( int domain, int type, int protocol, int cloexec, int nonblock); diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 740cc2a11d..735385557c 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -69,7 +69,7 @@ suppress_libevent_log_msg(const char *msg) /* Wrapper for event_free() that tolerates tor_event_free(NULL) */ void -tor_event_free(struct event *ev) +tor_event_free_(struct event *ev) { if (ev == NULL) return; @@ -126,7 +126,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) if (!the_event_base) { /* LCOV_EXCL_START */ log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); - exit(1); + exit(1); // exit ok: libevent is broken. /* LCOV_EXCL_STOP */ } @@ -213,7 +213,7 @@ periodic_timer_new(struct event_base *base, /** Stop and free a periodic timer */ void -periodic_timer_free(periodic_timer_t *timer) +periodic_timer_free_(periodic_timer_t *timer) { if (!timer) return; @@ -237,6 +237,17 @@ tor_init_libevent_rng(void) return rv; } +/** + * Un-initialize libevent in preparation for an exit + */ +void +tor_libevent_free_all(void) +{ + if (the_event_base) + event_base_free(the_event_base); + the_event_base = NULL; +} + #if defined(LIBEVENT_VERSION_NUMBER) && \ LIBEVENT_VERSION_NUMBER >= V(2,1,1) && \ !defined(TOR_UNIT_TESTS) diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 834354c405..1853e50917 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -19,7 +19,9 @@ void suppress_libevent_log_msg(const char *msg); evdns_add_server_port_with_base(tor_libevent_get_base(), \ (sock),(tcp),(cb),(data)); -void tor_event_free(struct event *ev); +void tor_event_free_(struct event *ev); +#define tor_event_free(ev) \ + FREE_AND_NULL(struct event, tor_event_free_, (ev)) typedef struct periodic_timer_t periodic_timer_t; @@ -27,9 +29,12 @@ periodic_timer_t *periodic_timer_new(struct event_base *base, const struct timeval *tv, void (*cb)(periodic_timer_t *timer, void *data), void *data); -void periodic_timer_free(periodic_timer_t *); +void periodic_timer_free_(periodic_timer_t *); +#define periodic_timer_free(t) \ + FREE_AND_NULL(periodic_timer_t, periodic_timer_free_, (t)) #define tor_event_base_loopexit event_base_loopexit +#define tor_event_base_loopbreak event_base_loopbreak /** Defines a configuration for using libevent with Tor: passed as an argument * to tor_libevent_initialize() to describe how we want to set up. */ @@ -47,6 +52,7 @@ const char *tor_libevent_get_method(void); void tor_check_libevent_header_compatibility(void); const char *tor_libevent_get_version_str(void); const char *tor_libevent_get_header_version_str(void); +void tor_libevent_free_all(void); int tor_init_libevent_rng(void); diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h index c695f1e9df..1299ac36bb 100644 --- a/src/common/compat_openssl.h +++ b/src/common/compat_openssl.h @@ -8,11 +8,11 @@ #define TOR_COMPAT_OPENSSL_H #include <openssl/opensslv.h> - +#include "crypto_openssl_mgt.h" /** * \file compat_openssl.h * - * \brief compatability definitions for working with different openssl forks + * \brief compatibility definitions for working with different openssl forks **/ #if !defined(LIBRESSL_VERSION_NUMBER) && \ diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c deleted file mode 100644 index 366fd4037b..0000000000 --- a/src/common/compat_rust.c +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_compat.c - * \brief Rust FFI compatibility functions and helpers. This file is only built - * if Rust is not used. - **/ - -#include "compat_rust.h" -#include "util.h" - -/** - * Free storage pointed to by <b>str</b>, and itself. - */ -void -rust_str_free(rust_str_t str) -{ - char *s = (char *)str; - tor_free(s); -} - -/** - * Return zero-terminated contained string. - */ -const char * -rust_str_get(const rust_str_t str) -{ - return (const char *)str; -} - -/* If we were using Rust, we'd say so on startup. */ -rust_str_t -rust_welcome_string(void) -{ - char *s = tor_malloc_zero(1); - return (rust_str_t)s; -} - diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h deleted file mode 100644 index 72fde39296..0000000000 --- a/src/common/compat_rust.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_compat.h - * \brief Headers for rust_compat.c - **/ - -#ifndef TOR_RUST_COMPAT_H -#define TOR_RUST_COMPAT_H - -#include "torint.h" - -/** - * Strings allocated in Rust must be freed from Rust code again. Let's make - * it less likely to accidentally mess up and call tor_free() on it, because - * currently it'll just work but might break at any time. - */ -typedef uintptr_t rust_str_t; - -void rust_str_free(rust_str_t); - -const char *rust_str_get(const rust_str_t); - -rust_str_t rust_welcome_string(void); - -#endif /* !defined(TOR_RUST_COMPAT_H) */ - diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c index 208d3138d9..3171c4b2f2 100644 --- a/src/common/compat_threads.c +++ b/src/common/compat_threads.c @@ -48,7 +48,7 @@ tor_mutex_new_nonrecursive(void) } /** Release all storage and system resources held by <b>m</b>. */ void -tor_mutex_free(tor_mutex_t *m) +tor_mutex_free_(tor_mutex_t *m) { if (!m) return; @@ -68,7 +68,7 @@ tor_cond_new(void) /** Free all storage held in <b>c</b>. */ void -tor_cond_free(tor_cond_t *c) +tor_cond_free_(tor_cond_t *c) { if (!c) return; @@ -352,12 +352,7 @@ alert_sockets_close(alert_sockets_t *socks) socks->read_fd = socks->write_fd = -1; } -/* - * XXXX We might be smart to move to compiler intrinsics or real atomic - * XXXX operations at some point. But not yet. - * - */ - +#ifndef HAVE_STDATOMIC_H /** Initialize a new atomic counter with the value 0 */ void atomic_counter_init(atomic_counter_t *counter) @@ -397,4 +392,16 @@ atomic_counter_get(atomic_counter_t *counter) tor_mutex_release(&counter->mutex); return val; } +/** Replace the value of an atomic counter; return the old one. */ +size_t +atomic_counter_exchange(atomic_counter_t *counter, size_t newval) +{ + size_t oldval; + tor_mutex_acquire(&counter->mutex); + oldval = counter->val; + counter->val = newval; + tor_mutex_release(&counter->mutex); + return oldval; +} +#endif /* !defined(HAVE_STDATOMIC_H) */ diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h index 42f14eab2a..c93e601ec5 100644 --- a/src/common/compat_threads.h +++ b/src/common/compat_threads.h @@ -14,6 +14,10 @@ #include <pthread.h> #endif +#ifdef HAVE_STDATOMIC_H +#include <stdatomic.h> +#endif + #if defined(_WIN32) #define USE_WIN32_THREADS #elif defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_CREATE) @@ -50,7 +54,8 @@ void tor_mutex_init(tor_mutex_t *m); void tor_mutex_init_nonrecursive(tor_mutex_t *m); void tor_mutex_acquire(tor_mutex_t *m); void tor_mutex_release(tor_mutex_t *m); -void tor_mutex_free(tor_mutex_t *m); +void tor_mutex_free_(tor_mutex_t *m); +#define tor_mutex_free(m) FREE_AND_NULL(tor_mutex_t, tor_mutex_free_, (m)) void tor_mutex_uninit(tor_mutex_t *m); unsigned long tor_get_thread_id(void); void tor_threads_init(void); @@ -77,7 +82,8 @@ typedef struct tor_cond_t { } tor_cond_t; tor_cond_t *tor_cond_new(void); -void tor_cond_free(tor_cond_t *cond); +void tor_cond_free_(tor_cond_t *cond); +#define tor_cond_free(c) FREE_AND_NULL(tor_cond_t, tor_cond_free_, (c)) int tor_cond_init(tor_cond_t *cond); void tor_cond_uninit(tor_cond_t *cond); int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, @@ -150,16 +156,68 @@ void tor_threadlocal_set(tor_threadlocal_t *threadlocal, void *value); /** * Atomic counter type; holds a size_t value. */ +#ifdef HAVE_STDATOMIC_H +typedef struct atomic_counter_t { + atomic_size_t val; +} atomic_counter_t; +#define ATOMIC_LINKAGE static +#else /* !(defined(HAVE_STDATOMIC_H)) */ typedef struct atomic_counter_t { tor_mutex_t mutex; size_t val; } atomic_counter_t; - -void atomic_counter_init(atomic_counter_t *counter); -void atomic_counter_destroy(atomic_counter_t *counter); -void atomic_counter_add(atomic_counter_t *counter, size_t add); -void atomic_counter_sub(atomic_counter_t *counter, size_t sub); -size_t atomic_counter_get(atomic_counter_t *counter); +#define ATOMIC_LINKAGE +#endif /* defined(HAVE_STDATOMIC_H) */ + +ATOMIC_LINKAGE void atomic_counter_init(atomic_counter_t *counter); +ATOMIC_LINKAGE void atomic_counter_destroy(atomic_counter_t *counter); +ATOMIC_LINKAGE void atomic_counter_add(atomic_counter_t *counter, size_t add); +ATOMIC_LINKAGE void atomic_counter_sub(atomic_counter_t *counter, size_t sub); +ATOMIC_LINKAGE size_t atomic_counter_get(atomic_counter_t *counter); +ATOMIC_LINKAGE size_t atomic_counter_exchange(atomic_counter_t *counter, + size_t newval); +#undef ATOMIC_LINKAGE + +#ifdef HAVE_STDATOMIC_H +/** Initialize a new atomic counter with the value 0 */ +static inline void +atomic_counter_init(atomic_counter_t *counter) +{ + atomic_init(&counter->val, 0); +} +/** Clean up all resources held by an atomic counter. */ +static inline void +atomic_counter_destroy(atomic_counter_t *counter) +{ + (void)counter; +} +/** Add a value to an atomic counter. */ +static inline void +atomic_counter_add(atomic_counter_t *counter, size_t add) +{ + (void) atomic_fetch_add(&counter->val, add); +} +/** Subtract a value from an atomic counter. */ +static inline void +atomic_counter_sub(atomic_counter_t *counter, size_t sub) +{ + (void) atomic_fetch_sub(&counter->val, sub); +} +/** Return the current value of an atomic counter */ +static inline size_t +atomic_counter_get(atomic_counter_t *counter) +{ + return atomic_load(&counter->val); +} +/** Replace the value of an atomic counter; return the old one. */ +static inline size_t +atomic_counter_exchange(atomic_counter_t *counter, size_t newval) +{ + return atomic_exchange(&counter->val, newval); +} + +#else /* !(defined(HAVE_STDATOMIC_H)) */ +#endif /* defined(HAVE_STDATOMIC_H) */ #endif /* !defined(TOR_COMPAT_THREADS_H) */ diff --git a/src/common/compat_time.c b/src/common/compat_time.c index 1ce6f5ce4e..183a60a480 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -90,7 +90,7 @@ tor_gettimeofday(struct timeval *timeval) if (ft.ft_64 < EPOCH_BIAS) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"System time is before 1970; failing."); - exit(1); + exit(1); // exit ok: system clock is broken. /* LCOV_EXCL_STOP */ } ft.ft_64 -= EPOCH_BIAS; @@ -102,7 +102,7 @@ tor_gettimeofday(struct timeval *timeval) log_err(LD_GENERAL,"gettimeofday failed."); /* If gettimeofday dies, we have either given a bad timezone (we didn't), or segfaulted.*/ - exit(1); + exit(1); // exit ok: gettimeofday failed. /* LCOV_EXCL_STOP */ } #elif defined(HAVE_FTIME) @@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void) * nanoseconds. */ static struct mach_timebase_info mach_time_info; +static int monotime_shift = 0; static void monotime_init_internal(void) @@ -287,6 +288,14 @@ monotime_init_internal(void) int r = mach_timebase_info(&mach_time_info); tor_assert(r == 0); tor_assert(mach_time_info.denom != 0); + + { + // approximate only. + uint64_t ns_per_tick = mach_time_info.numer / mach_time_info.denom; + uint64_t ms_per_tick = ns_per_tick * ONE_MILLION; + // requires that tor_log2(0) == 0. + monotime_shift = tor_log2(ms_per_tick); + } } /** @@ -305,6 +314,21 @@ monotime_get(monotime_t *out) out->abstime_ = mach_absolute_time(); } +#if defined(HAVE_MACH_APPROXIMATE_TIME) +void +monotime_coarse_get(monotime_coarse_t *out) +{ +#ifdef TOR_UNIT_TESTS + if (monotime_mocking_enabled) { + out->abstime_ = (mock_time_nsec_coarse * mach_time_info.denom) + / mach_time_info.numer; + return; + } +#endif /* defined(TOR_UNIT_TESTS) */ + out->abstime_ = mach_approximate_time(); +} +#endif + /** * Return the number of nanoseconds between <b>start</b> and <b>end</b>. */ @@ -321,6 +345,26 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + return (uint32_t)(t->abstime_ >> monotime_shift); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->abstime_ == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint64_t nsec = msec * ONE_MILLION; + const uint64_t ticks = (nsec * mach_time_info.denom) / mach_time_info.numer; + out->abstime_ = val->abstime_ + ticks; +} + /* end of "__APPLE__" */ #elif defined(HAVE_CLOCK_GETTIME) @@ -399,6 +443,37 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +/* This value is ONE_BILLION >> 20. */ +static const uint32_t STAMP_TICKS_PER_SECOND = 953; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + uint32_t nsec = (uint32_t)t->ts_.tv_nsec; + uint32_t sec = (uint32_t)t->ts_.tv_sec; + + return (sec * STAMP_TICKS_PER_SECOND) + (nsec >> 20); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->ts_.tv_sec == 0 && val->ts_.tv_nsec == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint32_t sec = msec / 1000; + const uint32_t msec_remainder = msec % 1000; + out->ts_.tv_sec = val->ts_.tv_sec + sec; + out->ts_.tv_nsec = val->ts_.tv_nsec + (msec_remainder * ONE_MILLION); + if (out->ts_.tv_nsec > ONE_BILLION) { + out->ts_.tv_nsec -= ONE_BILLION; + out->ts_.tv_sec += 1; + } +} + /* end of "HAVE_CLOCK_GETTIME" */ #elif defined (_WIN32) @@ -531,6 +606,41 @@ monotime_coarse_diff_nsec(const monotime_coarse_t *start, return monotime_coarse_diff_msec(start, end) * ONE_MILLION; } +static const uint32_t STAMP_TICKS_PER_SECOND = 1000; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + return (uint32_t) t->tick_count_; +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->pcount_ == 0; +} + +int +monotime_coarse_is_zero(const monotime_coarse_t *val) +{ + return val->tick_count_ == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint64_t nsec = msec * ONE_MILLION; + const uint64_t ticks = (nsec * nsec_per_tick_denom) / nsec_per_tick_numer; + out->pcount_ = val->pcount_ + ticks; +} + +void +monotime_coarse_add_msec(monotime_coarse_t *out, const monotime_coarse_t *val, + uint32_t msec) +{ + out->tick_count_ = val->tick_count_ + msec; +} + /* end of "_WIN32" */ #elif defined(MONOTIME_USING_GETTIMEOFDAY) @@ -567,6 +677,36 @@ monotime_diff_nsec(const monotime_t *start, return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000); } +/* This value is ONE_MILLION >> 10. */ +static const uint32_t STAMP_TICKS_PER_SECOND = 976; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + const uint32_t usec = (uint32_t)t->tv_.tv_usec; + const uint32_t sec = (uint32_t)t->tv_.tv_sec; + return (sec * STAMP_TICKS_PER_SECOND) | (nsec >> 10); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->tv_.tv_sec == 0 && val->tv_.tv_usec == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint32_t sec = msec / 1000; + const uint32_t msec_remainder = msec % 1000; + out->tv_.tv_sec = val->tv_.tv_sec + sec; + out->tv_.tv_usec = val->tv_.tv_nsec + (msec_remainder * 1000); + if (out->tv_.tv_usec > ONE_MILLION) { + out->tv_.tv_usec -= ONE_MILLION; + out->tv_.tv_sec += 1; + } +} + /* end of "MONOTIME_USING_GETTIMEOFDAY" */ #else #error "No way to implement monotonic timers." @@ -589,6 +729,19 @@ monotime_init(void) } } +void +monotime_zero(monotime_t *out) +{ + memset(out, 0, sizeof(*out)); +} +#ifdef MONOTIME_COARSE_TYPE_IS_DIFFERENT +void +monotime_coarse_zero(monotime_coarse_t *out) +{ + memset(out, 0, sizeof(*out)); +} +#endif + int64_t monotime_diff_usec(const monotime_t *start, const monotime_t *end) @@ -653,5 +806,35 @@ monotime_coarse_absolute_msec(void) { return monotime_coarse_absolute_nsec() / ONE_MILLION; } +#else +#define initialized_at_coarse initialized_at #endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */ +/** + * Return the current time "stamp" as described by monotime_coarse_to_stamp. + */ +uint32_t +monotime_coarse_get_stamp(void) +{ + monotime_coarse_t now; + monotime_coarse_get(&now); + return monotime_coarse_to_stamp(&now); +} + +#ifdef __APPLE__ +uint64_t +monotime_coarse_stamp_units_to_approx_msec(uint64_t units) +{ + /* Recover as much precision as we can. */ + uint64_t abstime_diff = (units << monotime_shift); + return (abstime_diff * mach_time_info.numer) / + (mach_time_info.denom * ONE_MILLION); +} +#else +uint64_t +monotime_coarse_stamp_units_to_approx_msec(uint64_t units) +{ + return (units * 1000) / STAMP_TICKS_PER_SECOND; +} +#endif + diff --git a/src/common/compat_time.h b/src/common/compat_time.h index 5ea4aae42b..6ddd11883d 100644 --- a/src/common/compat_time.h +++ b/src/common/compat_time.h @@ -65,6 +65,9 @@ typedef struct monotime_t { typedef struct monotime_coarse_t { uint64_t tick_count_; } monotime_coarse_t; +#elif defined(__APPLE__) && defined(HAVE_MACH_APPROXIMATE_TIME) +#define MONOTIME_COARSE_FN_IS_DIFFERENT +#define monotime_coarse_t monotime_t #else #define monotime_coarse_t monotime_t #endif /* defined(CLOCK_MONOTONIC_COARSE) && ... || ... */ @@ -102,6 +105,21 @@ uint64_t monotime_absolute_usec(void); */ uint64_t monotime_absolute_msec(void); +/** + * Set <b>out</b> to zero. + */ +void monotime_zero(monotime_t *out); +/** + * Return true iff <b>out</b> is zero + */ +int monotime_is_zero(const monotime_t *out); + +/** + * Set <b>out</b> to N milliseconds after <b>val</b>. + */ +/* XXXX We should add a more generic function here if we ever need to */ +void monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec); + #if defined(MONOTIME_COARSE_FN_IS_DIFFERENT) /** * Set <b>out</b> to the current coarse time. @@ -117,6 +135,23 @@ uint64_t monotime_coarse_absolute_msec(void); #define monotime_coarse_absolute_msec monotime_absolute_msec #endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */ +/** + * Return a "timestamp" approximation for a coarse monotonic timer. + * This timestamp is meant to be fast to calculate and easy to + * compare, and have a unit of something roughly around 1 msec. + * + * It will wrap over from time to time. + * + * It has no defined zero point. + */ +uint32_t monotime_coarse_to_stamp(const monotime_coarse_t *t); +/** + * Convert a difference, expressed in the units of monotime_coarse_to_stamp, + * into an approximate number of milliseconds. + */ +uint64_t monotime_coarse_stamp_units_to_approx_msec(uint64_t units); +uint32_t monotime_coarse_get_stamp(void); + #if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start, const monotime_coarse_t *end); @@ -124,10 +159,17 @@ int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start, const monotime_coarse_t *end); int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start, const monotime_coarse_t *end); +void monotime_coarse_zero(monotime_coarse_t *out); +int monotime_coarse_is_zero(const monotime_coarse_t *val); +void monotime_coarse_add_msec(monotime_coarse_t *out, + const monotime_coarse_t *val, uint32_t msec); #else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */ #define monotime_coarse_diff_nsec monotime_diff_nsec #define monotime_coarse_diff_usec monotime_diff_usec #define monotime_coarse_diff_msec monotime_diff_msec +#define monotime_coarse_zero monotime_zero +#define monotime_coarse_is_zero monotime_is_zero +#define monotime_coarse_add_msec monotime_add_msec #endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */ void tor_gettimeofday(struct timeval *timeval); diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c index 50a3c498ca..5f7ec94c23 100644 --- a/src/common/compat_winthreads.c +++ b/src/common/compat_winthreads.c @@ -48,10 +48,12 @@ void spawn_exit(void) { _endthread(); + // LCOV_EXCL_START //we should never get here. my compiler thinks that _endthread returns, this //is an attempt to fool it. tor_assert(0); - _exit(0); + _exit(0); // exit ok: unreachable. + // LCOV_EXCL_STOP } void diff --git a/src/common/compress.c b/src/common/compress.c index bc12a58ad6..47c93cf6a9 100644 --- a/src/common/compress.c +++ b/src/common/compress.c @@ -598,7 +598,7 @@ tor_compress_process(tor_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_compress_free(tor_compress_state_t *state) +tor_compress_free_(tor_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress.h b/src/common/compress.h index 23a9817479..952102bf97 100644 --- a/src/common/compress.h +++ b/src/common/compress.h @@ -80,7 +80,9 @@ tor_compress_output_t tor_compress_process(tor_compress_state_t *state, char **out, size_t *out_len, const char **in, size_t *in_len, int finish); -void tor_compress_free(tor_compress_state_t *state); +void tor_compress_free_(tor_compress_state_t *state); +#define tor_compress_free(st) \ + FREE_AND_NULL(tor_compress_state_t, tor_compress_free_, (st)) size_t tor_compress_state_size(const tor_compress_state_t *state); diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c index 6426ede4fd..051c59ba2d 100644 --- a/src/common/compress_lzma.c +++ b/src/common/compress_lzma.c @@ -323,7 +323,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_lzma_compress_free(tor_lzma_compress_state_t *state) +tor_lzma_compress_free_(tor_lzma_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_lzma.h b/src/common/compress_lzma.h index 7639d98a70..38a447c1f3 100644 --- a/src/common/compress_lzma.h +++ b/src/common/compress_lzma.h @@ -31,7 +31,10 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_lzma_compress_free(tor_lzma_compress_state_t *state); +void tor_lzma_compress_free_(tor_lzma_compress_state_t *state); +#define tor_lzma_compress_free(st) \ + FREE_AND_NULL(tor_lzma_compress_state_t, \ + tor_lzma_compress_free_, (st)) size_t tor_lzma_compress_state_size(const tor_lzma_compress_state_t *state); diff --git a/src/common/compress_zlib.c b/src/common/compress_zlib.c index 284542e885..23d71d27be 100644 --- a/src/common/compress_zlib.c +++ b/src/common/compress_zlib.c @@ -265,7 +265,7 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_zlib_compress_free(tor_zlib_compress_state_t *state) +tor_zlib_compress_free_(tor_zlib_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_zlib.h b/src/common/compress_zlib.h index 8ace467bf0..e3c1a2b339 100644 --- a/src/common/compress_zlib.h +++ b/src/common/compress_zlib.h @@ -31,7 +31,10 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_zlib_compress_free(tor_zlib_compress_state_t *state); +void tor_zlib_compress_free_(tor_zlib_compress_state_t *state); +#define tor_zlib_compress_free(st) \ + FREE_AND_NULL(tor_zlib_compress_state_t, \ + tor_zlib_compress_free_, (st)) size_t tor_zlib_compress_state_size(const tor_zlib_compress_state_t *state); diff --git a/src/common/compress_zstd.c b/src/common/compress_zstd.c index c1cdaf17ad..0db87d61b7 100644 --- a/src/common/compress_zstd.c +++ b/src/common/compress_zstd.c @@ -399,7 +399,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_zstd_compress_free(tor_zstd_compress_state_t *state) +tor_zstd_compress_free_(tor_zstd_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_zstd.h b/src/common/compress_zstd.h index 02466010ff..9bca24ded7 100644 --- a/src/common/compress_zstd.h +++ b/src/common/compress_zstd.h @@ -31,7 +31,10 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_zstd_compress_free(tor_zstd_compress_state_t *state); +void tor_zstd_compress_free_(tor_zstd_compress_state_t *state); +#define tor_zstd_compress_free(st) \ + FREE_AND_NULL(tor_zstd_compress_state_t, \ + tor_zstd_compress_free_, (st)) size_t tor_zstd_compress_state_size(const tor_zstd_compress_state_t *state); diff --git a/src/common/confline.c b/src/common/confline.c index 04545bc2c3..bf613ab742 100644 --- a/src/common/confline.c +++ b/src/common/confline.c @@ -12,15 +12,18 @@ static int config_get_lines_aux(const char *string, config_line_t **result, int extended, int allow_include, - int *has_include, int recursion_level, - config_line_t **last); -static smartlist_t *config_get_file_list(const char *path); -static int config_get_included_list(const char *path, int recursion_level, - int extended, config_line_t **list, - config_line_t **list_last); + int *has_include, smartlist_t *opened_lst, + int recursion_level, config_line_t **last); +static smartlist_t *config_get_file_list(const char *path, + smartlist_t *opened_files); +static int config_get_included_config(const char *path, int recursion_level, + int extended, config_line_t **config, + config_line_t **config_last, + smartlist_t *opened_lst); static int config_process_include(const char *path, int recursion_level, int extended, config_line_t **list, - config_line_t **list_last); + config_line_t **list_last, + smartlist_t *opened_lst); /** Helper: allocate a new configuration option mapping 'key' to 'val', * append it to *<b>lst</b>. */ @@ -80,11 +83,13 @@ config_line_find(const config_line_t *lines, /** Auxiliary function that does all the work of config_get_lines. * <b>recursion_level</b> is the count of how many nested %includes we have. + * <b>opened_lst</b> will have a list of opened files if provided. * Returns the a pointer to the last element of the <b>result</b> in * <b>last</b>. */ static int config_get_lines_aux(const char *string, config_line_t **result, int extended, - int allow_include, int *has_include, int recursion_level, + int allow_include, int *has_include, + smartlist_t *opened_lst, int recursion_level, config_line_t **last) { config_line_t *list = NULL, **next, *list_last = NULL; @@ -134,7 +139,7 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, config_line_t *include_list; if (config_process_include(v, recursion_level, extended, &include_list, - &list_last) < 0) { + &list_last, opened_lst) < 0) { log_warn(LD_CONFIG, "Error reading included configuration " "file or directory: \"%s\".", v); config_free_lines(list); @@ -176,24 +181,27 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Set *has_include to 1 if <b>result</b> has values from - * %included files. Return 0 on success, -1 on failure. Warn and ignore any + * %included files. <b>opened_lst</b> will have a list of opened files if + * provided. Return 0 on success, -1 on failure. Warn and ignore any * misformatted lines. * * If <b>extended</b> is set, then treat keys beginning with / and with + as * indicating "clear" and "append" respectively. */ int config_get_lines_include(const char *string, config_line_t **result, - int extended, int *has_include) + int extended, int *has_include, + smartlist_t *opened_lst) { - return config_get_lines_aux(string, result, extended, 1, has_include, 1, - NULL); + return config_get_lines_aux(string, result, extended, 1, has_include, + opened_lst, 1, NULL); } /** Same as config_get_lines_include but does not allow %include */ int config_get_lines(const char *string, config_line_t **result, int extended) { - return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL); + return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1, + NULL); } /** Adds a list of configuration files present on <b>path</b> to @@ -201,12 +209,18 @@ config_get_lines(const char *string, config_line_t **result, int extended) * only that file will be added to <b>file_list</b>. If it is a directory, * all paths for files on that directory root (no recursion) except for files * whose name starts with a dot will be added to <b>file_list</b>. - * Return 0 on success, -1 on failure. Ignores empty files. + * <b>opened_files</b> will have a list of files opened by this function + * if provided. Return 0 on success, -1 on failure. Ignores empty files. */ static smartlist_t * -config_get_file_list(const char *path) +config_get_file_list(const char *path, smartlist_t *opened_files) { smartlist_t *file_list = smartlist_new(); + + if (opened_files) { + smartlist_add_strdup(opened_files, path); + } + file_status_t file_type = file_status(path); if (file_type == FN_FILE) { smartlist_add_strdup(file_list, path); @@ -228,6 +242,10 @@ config_get_file_list(const char *path) tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f); tor_free(f); + if (opened_files) { + smartlist_add_strdup(opened_files, fullname); + } + if (file_status(fullname) != FN_FILE) { tor_free(fullname); continue; @@ -245,19 +263,21 @@ config_get_file_list(const char *path) } /** Creates a list of config lines present on included <b>path</b>. - * Set <b>list</b> to the list and <b>list_last</b> to the last element of - * <b>list</b>. Return 0 on success, -1 on failure. */ + * Set <b>config</b> to the list and <b>config_last</b> to the last element of + * <b>config</b>. <b>opened_lst</b> will have a list of opened files if + * provided. Return 0 on success, -1 on failure. */ static int -config_get_included_list(const char *path, int recursion_level, int extended, - config_line_t **list, config_line_t **list_last) +config_get_included_config(const char *path, int recursion_level, int extended, + config_line_t **config, config_line_t **config_last, + smartlist_t *opened_lst) { char *included_conf = read_file_to_str(path, 0, NULL); if (!included_conf) { return -1; } - if (config_get_lines_aux(included_conf, list, extended, 1, NULL, - recursion_level+1, list_last) < 0) { + if (config_get_lines_aux(included_conf, config, extended, 1, NULL, + opened_lst, recursion_level+1, config_last) < 0) { tor_free(included_conf); return -1; } @@ -268,41 +288,31 @@ config_get_included_list(const char *path, int recursion_level, int extended, /** Process an %include <b>path</b> in a config file. Set <b>list</b> to the * list of configuration settings obtained and <b>list_last</b> to the last - * element of the same list. Return 0 on success, -1 on failure. */ + * element of the same list. <b>opened_lst</b> will have a list of opened + * files if provided. Return 0 on success, -1 on failure. */ static int config_process_include(const char *path, int recursion_level, int extended, - config_line_t **list, config_line_t **list_last) + config_line_t **list, config_line_t **list_last, + smartlist_t *opened_lst) { config_line_t *ret_list = NULL; config_line_t **next = &ret_list; -#if 0 - // Disabled -- we already unescape_string() on the result. */ - char *unquoted_path = get_unquoted_path(path); - if (!unquoted_path) { - return -1; - } - smartlist_t *config_files = config_get_file_list(unquoted_path); - if (!config_files) { - tor_free(unquoted_path); - return -1; - } - tor_free(unquoted_path); -#endif /* 0 */ - smartlist_t *config_files = config_get_file_list(path); + smartlist_t *config_files = config_get_file_list(path, opened_lst); if (!config_files) { return -1; } int rv = -1; SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) { - config_line_t *included_list = NULL; - if (config_get_included_list(config_file, recursion_level, extended, - &included_list, list_last) < 0) { + config_line_t *included_config = NULL; + if (config_get_included_config(config_file, recursion_level, extended, + &included_config, list_last, + opened_lst) < 0) { goto done; } - *next = included_list; + *next = included_config; if (*list_last) next = &(*list_last)->next; @@ -320,7 +330,7 @@ config_process_include(const char *path, int recursion_level, int extended, * Free all the configuration lines on the linked list <b>front</b>. */ void -config_free_lines(config_line_t *front) +config_free_lines_(config_line_t *front) { config_line_t *tmp; diff --git a/src/common/confline.h b/src/common/confline.h index 8256326f2d..772a9bbbdc 100644 --- a/src/common/confline.h +++ b/src/common/confline.h @@ -7,6 +7,8 @@ #ifndef TOR_CONFLINE_H #define TOR_CONFLINE_H +#include "container.h" + /** Ordinary configuration line. */ #define CONFIG_LINE_NORMAL 0 /** Appends to previous configuration for the same option, even if we @@ -44,8 +46,14 @@ int config_lines_eq(config_line_t *a, config_line_t *b); int config_count_key(const config_line_t *a, const char *key); int config_get_lines(const char *string, config_line_t **result, int extended); int config_get_lines_include(const char *string, config_line_t **result, - int extended, int *has_include); -void config_free_lines(config_line_t *front); + int extended, int *has_include, + smartlist_t *opened_lst); +void config_free_lines_(config_line_t *front); +#define config_free_lines(front) \ + do { \ + config_free_lines_(front); \ + (front) = NULL; \ + } while (0) const char *parse_config_line_from_str_verbose(const char *line, char **key_out, char **value_out, const char **err_out); diff --git a/src/common/container.c b/src/common/container.c index 8645cb4826..54b0b2028f 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -42,7 +42,7 @@ smartlist_new,(void)) * list's elements. */ MOCK_IMPL(void, -smartlist_free,(smartlist_t *sl)) +smartlist_free_,(smartlist_t *sl)) { if (!sl) return; @@ -1163,19 +1163,26 @@ HT_GENERATE2(digest256map_impl, digest256map_entry_t, node, digest256map_entry_hash, digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_) +#define strmap_entry_free(ent) \ + FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent)) +#define digestmap_entry_free(ent) \ + FREE_AND_NULL(digestmap_entry_t, digestmap_entry_free_, (ent)) +#define digest256map_entry_free(ent) \ + FREE_AND_NULL(digest256map_entry_t, digest256map_entry_free_, (ent)) + static inline void -strmap_entry_free(strmap_entry_t *ent) +strmap_entry_free_(strmap_entry_t *ent) { tor_free(ent->key); tor_free(ent); } static inline void -digestmap_entry_free(digestmap_entry_t *ent) +digestmap_entry_free_(digestmap_entry_t *ent) { tor_free(ent); } static inline void -digest256map_entry_free(digest256map_entry_t *ent) +digest256map_entry_free_(digest256map_entry_t *ent) { tor_free(ent); } @@ -1335,7 +1342,7 @@ digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key) * those entries. If free_val is provided, invoked it every value in \ * <b>map</b>. */ \ MOCK_IMPL(void, \ - prefix##_free, (maptype *map, void (*free_val)(void*))) \ + prefix##_free_, (maptype *map, void (*free_val)(void*))) \ { \ prefix##_entry_t **ent, **next, *this; \ if (!map) \ @@ -1525,7 +1532,7 @@ digestset_new(int max_elements) /** Free all storage held in <b>set</b>. */ void -digestset_free(digestset_t *set) +digestset_free_(digestset_t *set) { if (!set) return; diff --git a/src/common/container.h b/src/common/container.h index f6affd3bc6..5d2dce5416 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -28,7 +28,9 @@ typedef struct smartlist_t { } smartlist_t; MOCK_DECL(smartlist_t *, smartlist_new, (void)); -MOCK_DECL(void, smartlist_free, (smartlist_t *sl)); +MOCK_DECL(void, smartlist_free_, (smartlist_t *sl)); +#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl)) + void smartlist_clear(smartlist_t *sl); void smartlist_add(smartlist_t *sl, void *element); void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2); @@ -350,7 +352,7 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join, void* prefix##set(maptype *map, keytype key, void *val); \ void* prefix##get(const maptype *map, keytype key); \ void* prefix##remove(maptype *map, keytype key); \ - MOCK_DECL(void, prefix##free, (maptype *map, void (*free_val)(void*))); \ + MOCK_DECL(void, prefix##free_, (maptype *map, void (*free_val)(void*))); \ int prefix##isempty(const maptype *map); \ int prefix##size(const maptype *map); \ prefix##iter_t *prefix##iter_init(maptype *map); \ @@ -368,6 +370,16 @@ DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_); * table. */ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); +#define MAP_FREE_AND_NULL(maptype, map, fn) \ + do { \ + maptype ## _free_((map), (fn)); \ + (map) = NULL; \ + } while (0) + +#define strmap_free(map, fn) MAP_FREE_AND_NULL(strmap, (map), (fn)) +#define digestmap_free(map, fn) MAP_FREE_AND_NULL(digestmap, (map), (fn)) +#define digest256map_free(map, fn) MAP_FREE_AND_NULL(digest256map, (map), (fn)) + #undef DECLARE_MAP_FNS /** Iterates over the key-value pairs in a map <b>map</b> in order. @@ -528,9 +540,9 @@ void* strmap_remove_lc(strmap_t *map, const char *key); return (valtype*)digestmap_remove((digestmap_t*)map, key); \ } \ ATTR_UNUSED static inline void \ - prefix##f##ree(maptype *map, void (*free_val)(void*)) \ + prefix##f##ree_(maptype *map, void (*free_val)(void*)) \ { \ - digestmap_free((digestmap_t*)map, free_val); \ + digestmap_free_((digestmap_t*)map, free_val); \ } \ ATTR_UNUSED static inline int \ prefix##isempty(maptype *map) \ @@ -614,10 +626,12 @@ bitarray_expand(bitarray_t *ba, } /** Free the bit array <b>ba</b>. */ static inline void -bitarray_free(bitarray_t *ba) +bitarray_free_(bitarray_t *ba) { tor_free(ba); } +#define bitarray_free(ba) FREE_AND_NULL(bitarray_t, bitarray_free_, (ba)) + /** Set the <b>bit</b>th bit in <b>b</b> to 1. */ static inline void bitarray_set(bitarray_t *b, int bit) @@ -679,7 +693,8 @@ digestset_contains(const digestset_t *set, const char *digest) #undef BIT digestset_t *digestset_new(int max_elements); -void digestset_free(digestset_t* set); +void digestset_free_(digestset_t* set); +#define digestset_free(set) FREE_AND_NULL(digestset_t, digestset_free_, (set)) /* These functions, given an <b>array</b> of <b>n_elements</b>, return the * <b>nth</b> lowest element. <b>nth</b>=0 gives the lowest element; diff --git a/src/common/crypto.c b/src/common/crypto.c index c9db7cb4ba..d85aca4004 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -28,6 +28,7 @@ #include "crypto_curve25519.h" #include "crypto_ed25519.h" #include "crypto_format.h" +#include "crypto_rsa.h" DISABLE_GCC_WARNING(redundant-decls) @@ -82,80 +83,20 @@ ENABLE_GCC_WARNING(redundant-decls) #include "keccak-tiny/keccak-tiny.h" -#ifdef ANDROID -/* Android's OpenSSL seems to have removed all of its Engine support. */ -#define DISABLE_ENGINES -#endif - -#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && \ - !defined(LIBRESSL_VERSION_NUMBER) -/* OpenSSL as of 1.1.0pre4 has an "new" thread API, which doesn't require - * seting up various callbacks. - * - * OpenSSL 1.1.0pre4 has a messed up `ERR_remove_thread_state()` prototype, - * while the previous one was restored in pre5, and the function made a no-op - * (along with a deprecated annotation, which produces a compiler warning). - * - * While it is possible to support all three versions of the thread API, - * a version that existed only for one snapshot pre-release is kind of - * pointless, so let's not. - */ -#define NEW_THREAD_API -#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */ - /** Longest recognized */ #define MAX_DNS_LABEL_SIZE 63 /** Largest strong entropy request */ #define MAX_STRONGEST_RAND_SIZE 256 -#ifndef NEW_THREAD_API -/** A number of preallocated mutexes for use by OpenSSL. */ -static tor_mutex_t **openssl_mutexes_ = NULL; -/** How many mutexes have we allocated for use by OpenSSL? */ -static int n_openssl_mutexes_ = 0; -#endif /* !defined(NEW_THREAD_API) */ - -/** A public key, or a public/private key-pair. */ -struct crypto_pk_t -{ - int refs; /**< reference count, so we don't have to copy keys */ - RSA *key; /**< The key itself */ -}; - /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake * while we're waiting for the second.*/ struct crypto_dh_t { DH *dh; /**< The openssl DH object */ }; -static int setup_openssl_threading(void); static int tor_check_dh_key(int severity, const BIGNUM *bn); -/** Return the number of bytes added by padding method <b>padding</b>. - */ -static inline int -crypto_get_rsa_padding_overhead(int padding) -{ - switch (padding) - { - case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; - default: tor_assert(0); return -1; // LCOV_EXCL_LINE - } -} - -/** Given a padding method <b>padding</b>, return the correct OpenSSL constant. - */ -static inline int -crypto_get_rsa_padding(int padding) -{ - switch (padding) - { - case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; - default: tor_assert(0); return -1; // LCOV_EXCL_LINE - } -} - /** Boolean: has OpenSSL's crypto been initialized? */ static int crypto_early_initialized_ = 0; @@ -220,52 +161,6 @@ try_load_engine(const char *path, const char *engine) } #endif /* !defined(DISABLE_ENGINES) */ -/* Returns a trimmed and human-readable version of an openssl version string -* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10 -* May 2012' and this will parse them into a form similar to '1.0.0b' */ -static char * -parse_openssl_version_str(const char *raw_version) -{ - const char *end_of_version = NULL; - /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's - trim that down. */ - if (!strcmpstart(raw_version, "OpenSSL ")) { - raw_version += strlen("OpenSSL "); - end_of_version = strchr(raw_version, ' '); - } - - if (end_of_version) - return tor_strndup(raw_version, - end_of_version-raw_version); - else - return tor_strdup(raw_version); -} - -static char *crypto_openssl_version_str = NULL; -/* Return a human-readable version of the run-time openssl version number. */ -const char * -crypto_openssl_get_version_str(void) -{ - if (crypto_openssl_version_str == NULL) { - const char *raw_version = OpenSSL_version(OPENSSL_VERSION); - crypto_openssl_version_str = parse_openssl_version_str(raw_version); - } - return crypto_openssl_version_str; -} - -static char *crypto_openssl_header_version_str = NULL; -/* Return a human-readable version of the compile-time openssl version -* number. */ -const char * -crypto_openssl_get_header_version_str(void) -{ - if (crypto_openssl_header_version_str == NULL) { - crypto_openssl_header_version_str = - parse_openssl_version_str(OPENSSL_VERSION_TEXT); - } - return crypto_openssl_header_version_str; -} - /** Make sure that openssl is using its default PRNG. Return 1 if we had to * adjust it; 0 otherwise. */ STATIC int @@ -283,11 +178,12 @@ crypto_force_rand_ssleay(void) return 0; } +static int have_seeded_siphash = 0; + /** Set up the siphash key if we haven't already done so. */ int crypto_init_siphash_key(void) { - static int have_seeded_siphash = 0; struct sipkey key; if (have_seeded_siphash) return 0; @@ -437,73 +333,6 @@ crypto_thread_cleanup(void) #endif } -/** used internally: quicly validate a crypto_pk_t object as a private key. - * Return 1 iff the public key is valid, 0 if obviously invalid. - */ -static int -crypto_pk_private_ok(const crypto_pk_t *k) -{ -#ifdef OPENSSL_1_1_API - if (!k || !k->key) - return 0; - - const BIGNUM *p, *q; - RSA_get0_factors(k->key, &p, &q); - return p != NULL; /* XXX/yawning: Should we check q? */ -#else /* !(defined(OPENSSL_1_1_API)) */ - return k && k->key && k->key->p; -#endif /* defined(OPENSSL_1_1_API) */ -} - -/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ -crypto_pk_t * -crypto_new_pk_from_rsa_(RSA *rsa) -{ - crypto_pk_t *env; - tor_assert(rsa); - env = tor_malloc(sizeof(crypto_pk_t)); - env->refs = 1; - env->key = rsa; - return env; -} - -/** Helper, used by tor-gencert.c. Return the RSA from a - * crypto_pk_t. */ -RSA * -crypto_pk_get_rsa_(crypto_pk_t *env) -{ - return env->key; -} - -/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff - * private is set, include the private-key portion of the key. Return a valid - * pointer on success, and NULL on failure. */ -MOCK_IMPL(EVP_PKEY *, -crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private)) -{ - RSA *key = NULL; - EVP_PKEY *pkey = NULL; - tor_assert(env->key); - if (private) { - if (!(key = RSAPrivateKey_dup(env->key))) - goto error; - } else { - if (!(key = RSAPublicKey_dup(env->key))) - goto error; - } - if (!(pkey = EVP_PKEY_new())) - goto error; - if (!(EVP_PKEY_assign_RSA(pkey, key))) - goto error; - return pkey; - error: - if (pkey) - EVP_PKEY_free(pkey); - if (key) - RSA_free(key); - return NULL; -} - /** Used by tortls.c: Get the DH* from a crypto_dh_t. */ DH * @@ -512,38 +341,6 @@ crypto_dh_get_dh_(crypto_dh_t *dh) return dh->dh; } -/** Allocate and return storage for a public key. The key itself will not yet - * be set. - */ -MOCK_IMPL(crypto_pk_t *, -crypto_pk_new,(void)) -{ - RSA *rsa; - - rsa = RSA_new(); - tor_assert(rsa); - return crypto_new_pk_from_rsa_(rsa); -} - -/** Release a reference to an asymmetric key; when all the references - * are released, free the key. - */ -void -crypto_pk_free(crypto_pk_t *env) -{ - if (!env) - return; - - if (--env->refs > 0) - return; - tor_assert(env->refs == 0); - - if (env->key) - RSA_free(env->key); - - tor_free(env); -} - /** Allocate and return a new symmetric cipher using the provided key and iv. * The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes. Both * must be provided. Key length must be 128, 192, or 256 */ @@ -592,7 +389,7 @@ crypto_cipher_new(const char *key) /** Free a symmetric cipher. */ void -crypto_cipher_free(crypto_cipher_t *env) +crypto_cipher_free_(crypto_cipher_t *env) { if (!env) return; @@ -602,543 +399,6 @@ crypto_cipher_free(crypto_cipher_t *env) /* public key crypto */ -/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>. - * Return 0 on success, -1 on failure. - */ -MOCK_IMPL(int, -crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) -{ - tor_assert(env); - - if (env->key) { - RSA_free(env->key); - env->key = NULL; - } - - { - BIGNUM *e = BN_new(); - RSA *r = NULL; - if (!e) - goto done; - if (! BN_set_word(e, 65537)) - goto done; - r = RSA_new(); - if (!r) - goto done; - if (RSA_generate_key_ex(r, bits, e, NULL) == -1) - goto done; - - env->key = r; - r = NULL; - done: - if (e) - BN_clear_free(e); - if (r) - RSA_free(r); - } - - if (!env->key) { - crypto_log_errors(LOG_WARN, "generating RSA key"); - return -1; - } - - return 0; -} - -/** A PEM callback that always reports a failure to get a password */ -static int -pem_no_password_cb(char *buf, int size, int rwflag, void *u) -{ - (void)buf; - (void)size; - (void)rwflag; - (void)u; - return 0; -} - -/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b> - * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1, - * the string is nul-terminated. - */ -int -crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(s); - tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); - - /* Create a read-only memory BIO, backed by the string 's' */ - b = BIO_new_mem_buf((char*)s, (int)len); - if (!b) - return -1; - - if (env->key) - RSA_free(env->key); - - env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL); - - BIO_free(b); - - if (!env->key) { - crypto_log_errors(LOG_WARN, "Error parsing private key"); - return -1; - } - return 0; -} - -/** Read a PEM-encoded private key from the file named by - * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure. - */ -int -crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile) -{ - char *contents; - int r; - - /* Read the file into a string. */ - contents = read_file_to_str(keyfile, 0, NULL); - if (!contents) { - log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); - return -1; - } - - /* Try to parse it. */ - r = crypto_pk_read_private_key_from_string(env, contents, -1); - memwipe(contents, 0, strlen(contents)); - tor_free(contents); - if (r) - return -1; /* read_private_key_from_string already warned, so we don't.*/ - - /* Make sure it's valid. */ - if (crypto_pk_check_key(env) <= 0) - return -1; - - return 0; -} - -/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on - * success, -1 on failure. */ -static int -crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, - size_t *len, int is_public) -{ - BUF_MEM *buf; - BIO *b; - int r; - - tor_assert(env); - tor_assert(env->key); - tor_assert(dest); - - b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ - if (!b) - return -1; - - /* Now you can treat b as if it were a file. Just use the - * PEM_*_bio_* functions instead of the non-bio variants. - */ - if (is_public) - r = PEM_write_bio_RSAPublicKey(b, env->key); - else - r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); - - if (!r) { - crypto_log_errors(LOG_WARN, "writing RSA key to string"); - BIO_free(b); - return -1; - } - - BIO_get_mem_ptr(b, &buf); - - *dest = tor_malloc(buf->length+1); - memcpy(*dest, buf->data, buf->length); - (*dest)[buf->length] = 0; /* nul terminate it */ - *len = buf->length; - - BIO_free(b); - - return 0; -} - -/** PEM-encode the public key portion of <b>env</b> and write it to a - * newly allocated string. On success, set *<b>dest</b> to the new - * string, *<b>len</b> to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 1); -} - -/** PEM-encode the private key portion of <b>env</b> and write it to a - * newly allocated string. On success, set *<b>dest</b> to the new - * string, *<b>len</b> to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 0); -} - -/** Read a PEM-encoded public key from the first <b>len</b> characters of - * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on - * failure. - */ -int -crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, - size_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(src); - tor_assert(len<INT_MAX); - - b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ - if (!b) - return -1; - - BIO_write(b, src, (int)len); - - if (env->key) - RSA_free(env->key); - env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); - BIO_free(b); - if (!env->key) { - crypto_log_errors(LOG_WARN, "reading public key from string"); - return -1; - } - - return 0; -} - -/** Write the private key from <b>env</b> into the file named by <b>fname</b>, - * PEM-encoded. Return 0 on success, -1 on failure. - */ -int -crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname) -{ - BIO *bio; - char *cp; - long len; - char *s; - int r; - - tor_assert(crypto_pk_private_ok(env)); - - if (!(bio = BIO_new(BIO_s_mem()))) - return -1; - if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) - == 0) { - crypto_log_errors(LOG_WARN, "writing private key"); - BIO_free(bio); - return -1; - } - len = BIO_get_mem_data(bio, &cp); - tor_assert(len >= 0); - s = tor_malloc(len+1); - memcpy(s, cp, len); - s[len]='\0'; - r = write_str_to_file(fname, s, 0); - BIO_free(bio); - memwipe(s, 0, strlen(s)); - tor_free(s); - return r; -} - -/** Return true iff <b>env</b> has a valid key. - */ -int -crypto_pk_check_key(crypto_pk_t *env) -{ - int r; - tor_assert(env); - - r = RSA_check_key(env->key); - if (r <= 0) - crypto_log_errors(LOG_WARN,"checking RSA key"); - return r; -} - -/** Return true iff <b>key</b> contains the private-key portion of the RSA - * key. */ -int -crypto_pk_key_is_private(const crypto_pk_t *key) -{ - tor_assert(key); - return crypto_pk_private_ok(key); -} - -/** Return true iff <b>env</b> contains a public key whose public exponent - * equals 65537. - */ -int -crypto_pk_public_exponent_ok(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - const BIGNUM *e; - -#ifdef OPENSSL_1_1_API - const BIGNUM *n, *d; - RSA_get0_key(env->key, &n, &e, &d); -#else - e = env->key->e; -#endif /* defined(OPENSSL_1_1_API) */ - return BN_is_word(e, 65537); -} - -/** Compare the public-key components of a and b. Return less than 0 - * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is - * considered to be less than all non-NULL keys, and equal to itself. - * - * Note that this may leak information about the keys through timing. - */ -int -crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) -{ - int result; - char a_is_non_null = (a != NULL) && (a->key != NULL); - char b_is_non_null = (b != NULL) && (b->key != NULL); - char an_argument_is_null = !a_is_non_null | !b_is_non_null; - - result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); - if (an_argument_is_null) - return result; - - const BIGNUM *a_n, *a_e; - const BIGNUM *b_n, *b_e; - -#ifdef OPENSSL_1_1_API - const BIGNUM *a_d, *b_d; - RSA_get0_key(a->key, &a_n, &a_e, &a_d); - RSA_get0_key(b->key, &b_n, &b_e, &b_d); -#else - a_n = a->key->n; - a_e = a->key->e; - b_n = b->key->n; - b_e = b->key->e; -#endif /* defined(OPENSSL_1_1_API) */ - - tor_assert(a_n != NULL && a_e != NULL); - tor_assert(b_n != NULL && b_e != NULL); - - result = BN_cmp(a_n, b_n); - if (result) - return result; - return BN_cmp(a_e, b_e); -} - -/** Compare the public-key components of a and b. Return non-zero iff - * a==b. A NULL key is considered to be distinct from all non-NULL - * keys, and equal to itself. - * - * Note that this may leak information about the keys through timing. - */ -int -crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b) -{ - return (crypto_pk_cmp_keys(a, b) == 0); -} - -/** Return the size of the public key modulus in <b>env</b>, in bytes. */ -size_t -crypto_pk_keysize(const crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - return (size_t) RSA_size((RSA*)env->key); -} - -/** Return the size of the public key modulus of <b>env</b>, in bits. */ -int -crypto_pk_num_bits(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - -#ifdef OPENSSL_1_1_API - /* It's so stupid that there's no other way to check that n is valid - * before calling RSA_bits(). - */ - const BIGNUM *n, *e, *d; - RSA_get0_key(env->key, &n, &e, &d); - tor_assert(n != NULL); - - return RSA_bits(env->key); -#else /* !(defined(OPENSSL_1_1_API)) */ - tor_assert(env->key->n); - return BN_num_bits(env->key->n); -#endif /* defined(OPENSSL_1_1_API) */ -} - -/** Increase the reference count of <b>env</b>, and return it. - */ -crypto_pk_t * -crypto_pk_dup_key(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - env->refs++; - return env; -} - -#ifdef TOR_UNIT_TESTS -/** For testing: replace dest with src. (Dest must have a refcount - * of 1) */ -void -crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) -{ - tor_assert(dest); - tor_assert(dest->refs == 1); - tor_assert(src); - RSA_free(dest->key); - dest->key = RSAPrivateKey_dup(src->key); -} -#endif /* defined(TOR_UNIT_TESTS) */ - -/** Make a real honest-to-goodness copy of <b>env</b>, and return it. - * Returns NULL on failure. */ -crypto_pk_t * -crypto_pk_copy_full(crypto_pk_t *env) -{ - RSA *new_key; - int privatekey = 0; - tor_assert(env); - tor_assert(env->key); - - if (crypto_pk_private_ok(env)) { - new_key = RSAPrivateKey_dup(env->key); - privatekey = 1; - } else { - new_key = RSAPublicKey_dup(env->key); - } - if (!new_key) { - /* LCOV_EXCL_START - * - * We can't cause RSA*Key_dup() to fail, so we can't really test this. - */ - log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", - privatekey?"private":"public"); - crypto_log_errors(LOG_ERR, - privatekey ? "Duplicating a private key" : - "Duplicating a public key"); - tor_fragile_assert(); - return NULL; - /* LCOV_EXCL_STOP */ - } - - return crypto_new_pk_from_rsa_(new_key); -} - -/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key - * in <b>env</b>, using the padding method <b>padding</b>. On success, - * write the result to <b>to</b>, and return the number of bytes - * written. On failure, return -1. - * - * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be - * at least the length of the modulus of <b>env</b>. - */ -int -crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, int padding) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen<INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - - r = RSA_public_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - if (r<0) { - crypto_log_errors(LOG_WARN, "performing RSA encryption"); - return -1; - } - return r; -} - -/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key - * in <b>env</b>, using the padding method <b>padding</b>. On success, - * write the result to <b>to</b>, and return the number of bytes - * written. On failure, return -1. - * - * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be - * at least the length of the modulus of <b>env</b>. - */ -int -crypto_pk_private_decrypt(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(env->key); - tor_assert(fromlen<INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - if (!crypto_pk_key_is_private(env)) - /* Not a private key */ - return -1; - - r = RSA_private_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - - if (r<0) { - crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, - "performing RSA decryption"); - return -1; - } - return r; -} - -/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the - * public key in <b>env</b>, using PKCS1 padding. On success, write the - * signed data to <b>to</b>, and return the number of bytes written. - * On failure, return -1. - * - * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be - * at least the length of the modulus of <b>env</b>. - */ -MOCK_IMPL(int, -crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen)) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - r = RSA_public_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); - - if (r<0) { - crypto_log_errors(LOG_INFO, "checking RSA signature"); - return -1; - } - return r; -} - /** Check a siglen-byte long signature at <b>sig</b> against * <b>datalen</b> bytes of data at <b>data</b>, using the public key * in <b>env</b>. Return 0 if <b>sig</b> is a correct signature for @@ -1182,38 +442,6 @@ crypto_pk_public_checksig_digest,(crypto_pk_t *env, const char *data, return 0; } -/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in - * <b>env</b>, using PKCS1 padding. On success, write the signature to - * <b>to</b>, and return the number of bytes written. On failure, return - * -1. - * - * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be - * at least the length of the modulus of <b>env</b>. - */ -int -crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - if (!crypto_pk_key_is_private(env)) - /* Not a private key */ - return -1; - - r = RSA_private_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - (RSA*)env->key, RSA_PKCS1_PADDING); - if (r<0) { - crypto_log_errors(LOG_WARN, "generating RSA signature"); - return -1; - } - return r; -} - /** Compute a SHA1 digest of <b>fromlen</b> bytes of data stored at * <b>from</b>; sign the data with the private key in <b>env</b>, and * store it in <b>to</b>. Return the number of bytes written on @@ -1377,51 +605,6 @@ crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env, return -1; } -/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>. - * Return -1 on error, or the number of characters used on success. - */ -int -crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len) -{ - int len; - unsigned char *buf = NULL; - - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - - if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { - OPENSSL_free(buf); - return -1; - } - /* We don't encode directly into 'dest', because that would be illegal - * type-punning. (C99 is smarter than me, C99 is smarter than me...) - */ - memcpy(dest,buf,len); - OPENSSL_free(buf); - return len; -} - -/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on - * success and NULL on failure. - */ -crypto_pk_t * -crypto_pk_asn1_decode(const char *str, size_t len) -{ - RSA *rsa; - unsigned char *buf; - const unsigned char *cp; - cp = buf = tor_malloc(len); - memcpy(buf,str,len); - rsa = d2i_RSAPublicKey(NULL, &cp, len); - tor_free(buf); - if (!rsa) { - crypto_log_errors(LOG_WARN,"decoding public key"); - return NULL; - } - return crypto_new_pk_from_rsa_(rsa); -} - /** Given a private or public key <b>pk</b>, put a SHA1 hash of the * public key into <b>digest_out</b> (must have DIGEST_LEN bytes of space). * Return 0 on success, -1 on failure. @@ -1429,18 +612,24 @@ crypto_pk_asn1_decode(const char *str, size_t len) int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out) { - unsigned char *buf = NULL; + char *buf; + size_t buflen; int len; + int rv = -1; - len = i2d_RSAPublicKey((RSA*)pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - if (crypto_digest(digest_out, (char*)buf, len) < 0) { - OPENSSL_free(buf); - return -1; - } - OPENSSL_free(buf); - return 0; + buflen = crypto_pk_keysize(pk)*2; + buf = tor_malloc(buflen); + len = crypto_pk_asn1_encode(pk, buf, buflen); + if (len < 0) + goto done; + + if (crypto_digest(digest_out, buf, len) < 0) + goto done; + + rv = 0; + done: + tor_free(buf); + return rv; } /** Compute all digests of the DER encoding of <b>pk</b>, and store them @@ -1448,18 +637,24 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out) int crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out) { - unsigned char *buf = NULL; + char *buf; + size_t buflen; int len; + int rv = -1; - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - if (crypto_common_digests(digests_out, (char*)buf, len) < 0) { - OPENSSL_free(buf); - return -1; - } - OPENSSL_free(buf); - return 0; + buflen = crypto_pk_keysize(pk)*2; + buf = tor_malloc(buflen); + len = crypto_pk_asn1_encode(pk, buf, buflen); + if (len < 0) + goto done; + + if (crypto_common_digests(digests_out, (char*)buf, len) < 0) + goto done; + + rv = 0; + done: + tor_free(buf); + return rv; } /** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces @@ -1482,127 +677,6 @@ crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) *out = '\0'; } -/** Given a private or public key <b>pk</b>, put a fingerprint of the - * public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of - * space). Return 0 on success, -1 on failure. - * - * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding - * of the public key, converted to hexadecimal, in upper case, with a - * space after every four digits. - * - * If <b>add_space</b> is false, omit the spaces. - */ -int -crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) -{ - char digest[DIGEST_LEN]; - char hexdigest[HEX_DIGEST_LEN+1]; - if (crypto_pk_get_digest(pk, digest)) { - return -1; - } - base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); - if (add_space) { - crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); - } else { - strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); - } - return 0; -} - -/** Given a private or public key <b>pk</b>, put a hashed fingerprint of - * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 - * bytes of space). Return 0 on success, -1 on failure. - * - * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest - * of the ASN.1 encoding of the public key, converted to hexadecimal, in - * upper case. - */ -int -crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) -{ - char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN]; - if (crypto_pk_get_digest(pk, digest)) { - return -1; - } - if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) { - return -1; - } - base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); - return 0; -} - -/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the - * Base64 encoding of the DER representation of the private key as a NUL - * terminated string, and return it via <b>priv_out</b>. Return 0 on - * sucess, -1 on failure. - * - * It is the caller's responsibility to sanitize and free the resulting buffer. - */ -int -crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) -{ - unsigned char *der = NULL; - int der_len; - int ret = -1; - - *priv_out = NULL; - - der_len = i2d_RSAPrivateKey(pk->key, &der); - if (der_len < 0 || der == NULL) - return ret; - - size_t priv_len = base64_encode_size(der_len, 0) + 1; - char *priv = tor_malloc_zero(priv_len); - if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { - *priv_out = priv; - ret = 0; - } else { - tor_free(priv); - } - - memwipe(der, 0, der_len); - OPENSSL_free(der); - return ret; -} - -/** Given a string containing the Base64 encoded DER representation of the - * private key <b>str</b>, decode and return the result on success, or NULL - * on failure. - */ -crypto_pk_t * -crypto_pk_base64_decode(const char *str, size_t len) -{ - crypto_pk_t *pk = NULL; - - char *der = tor_malloc_zero(len + 1); - int der_len = base64_decode(der, len, str, len); - if (der_len <= 0) { - log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); - goto out; - } - - const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ - RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); - if (!rsa) { - crypto_log_errors(LOG_WARN, "decoding private key"); - goto out; - } - - pk = crypto_new_pk_from_rsa_(rsa); - - /* Make sure it's valid. */ - if (crypto_pk_check_key(pk) <= 0) { - crypto_pk_free(pk); - pk = NULL; - goto out; - } - - out: - memwipe(der, 0, len + 1); - tor_free(der); - return pk; -} - /* symmetric crypto */ /** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher @@ -1977,7 +1051,7 @@ crypto_digest512_new(digest_algorithm_t algorithm) /** Deallocate a digest object. */ void -crypto_digest_free(crypto_digest_t *digest) +crypto_digest_free_(crypto_digest_t *digest) { if (!digest) return; @@ -2224,7 +1298,7 @@ crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len) /** Cleanse and deallocate a XOF object. */ void -crypto_xof_free(crypto_xof_t *xof) +crypto_xof_free_(crypto_xof_t *xof) { if (!xof) return; @@ -2777,7 +1851,7 @@ crypto_expand_key_material_rfc5869_sha256( /** Free a DH key exchange object. */ void -crypto_dh_free(crypto_dh_t *dh) +crypto_dh_free_(crypto_dh_t *dh) { if (!dh) return; @@ -2817,6 +1891,12 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) { tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE); + /* We only log at notice-level here because in the case that this function + * fails the crypto_strongest_rand_raw() caller will log with a warning-level + * message and let crypto_strongest_rand() error out and finally terminating + * Tor with an assertion error. + */ + #ifdef TOR_UNIT_TESTS if (break_strongest_rng_syscall) return -1; @@ -2829,21 +1909,21 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) if (!provider_set) { if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]"); + log_notice(LD_CRYPTO, "Unable to set Windows CryptoAPI provider [1]."); return -1; } provider_set = 1; } if (!CryptGenRandom(provider, out_len, out)) { - log_warn(LD_CRYPTO, "Can't get entropy from CryptoAPI."); + log_notice(LD_CRYPTO, "Unable get entropy from the Windows CryptoAPI."); return -1; } return 0; #elif defined(__linux__) && defined(SYS_getrandom) - static int getrandom_works = 1; /* Be optimitic about our chances... */ + static int getrandom_works = 1; /* Be optimistic about our chances... */ - /* getrandom() isn't as straight foward as getentropy(), and has + /* getrandom() isn't as straightforward as getentropy(), and has * no glibc wrapper. * * As far as I can tell from getrandom(2) and the source code, the @@ -2856,7 +1936,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) * * We optimistically assume that getrandom() is available and functional * because it is the way of the future, and 2 branch mispredicts pale in - * comparision to the overheads involved with failing to open + * comparison to the overheads involved with failing to open * /dev/srandom followed by opening and reading from /dev/urandom. */ if (PREDICT_LIKELY(getrandom_works)) { @@ -2877,13 +1957,15 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) /* Useful log message for errno. */ if (errno == ENOSYS) { - log_warn(LD_CRYPTO, "Can't get entropy from getrandom()." - " You are running a version of Tor built to support" - " getrandom(), but the kernel doesn't implement this" - " function--probably because it is too old?"); + log_notice(LD_CRYPTO, "Can't get entropy from getrandom()." + " You are running a version of Tor built to support" + " getrandom(), but the kernel doesn't implement this" + " function--probably because it is too old?" + " Trying fallback method instead."); } else { - log_warn(LD_CRYPTO, "Can't get entropy from getrandom(): %s.", - strerror(errno)); + log_notice(LD_CRYPTO, "Can't get entropy from getrandom(): %s." + " Trying fallback method instead.", + strerror(errno)); } getrandom_works = 0; /* Don't bother trying again. */ @@ -2935,7 +2017,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) size_t n; for (i = 0; filenames[i]; ++i) { - log_debug(LD_FS, "Considering %s for entropy", filenames[i]); + log_debug(LD_FS, "Considering %s as entropy source", filenames[i]); fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0); if (fd<0) continue; log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]); @@ -2944,9 +2026,10 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) if (n != out_len) { /* LCOV_EXCL_START * We can't make /dev/foorandom actually fail. */ - log_warn(LD_CRYPTO, - "Error reading from entropy source (read only %lu bytes).", - (unsigned long)n); + log_notice(LD_CRYPTO, + "Error reading from entropy source %s (read only %lu bytes).", + filenames[i], + (unsigned long)n); return -1; /* LCOV_EXCL_STOP */ } @@ -3347,110 +2430,7 @@ memwipe(void *mem, uint8_t byte, size_t sz) memset(mem, byte, sz); } -#ifndef OPENSSL_THREADS -#error OpenSSL has been built without thread support. Tor requires an \ - OpenSSL library with thread support enabled. -#endif - -#ifndef NEW_THREAD_API -/** Helper: OpenSSL uses this callback to manipulate mutexes. */ -static void -openssl_locking_cb_(int mode, int n, const char *file, int line) -{ - (void)file; - (void)line; - if (!openssl_mutexes_) - /* This is not a really good fix for the - * "release-freed-lock-from-separate-thread-on-shutdown" problem, but - * it can't hurt. */ - return; - if (mode & CRYPTO_LOCK) - tor_mutex_acquire(openssl_mutexes_[n]); - else - tor_mutex_release(openssl_mutexes_[n]); -} - -static void -tor_set_openssl_thread_id(CRYPTO_THREADID *threadid) -{ - CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id()); -} -#endif /* !defined(NEW_THREAD_API) */ - -#if 0 -/* This code is disabled, because OpenSSL never actually uses these callbacks. - */ - -/** OpenSSL helper type: wraps a Tor mutex so that OpenSSL can use it - * as a lock. */ -struct CRYPTO_dynlock_value { - tor_mutex_t *lock; -}; - -/** OpenSSL callback function to allocate a lock: see CRYPTO_set_dynlock_* - * documentation in OpenSSL's docs for more info. */ -static struct CRYPTO_dynlock_value * -openssl_dynlock_create_cb_(const char *file, int line) -{ - struct CRYPTO_dynlock_value *v; - (void)file; - (void)line; - v = tor_malloc(sizeof(struct CRYPTO_dynlock_value)); - v->lock = tor_mutex_new(); - return v; -} - -/** OpenSSL callback function to acquire or release a lock: see - * CRYPTO_set_dynlock_* documentation in OpenSSL's docs for more info. */ -static void -openssl_dynlock_lock_cb_(int mode, struct CRYPTO_dynlock_value *v, - const char *file, int line) -{ - (void)file; - (void)line; - if (mode & CRYPTO_LOCK) - tor_mutex_acquire(v->lock); - else - tor_mutex_release(v->lock); -} - -/** OpenSSL callback function to free a lock: see CRYPTO_set_dynlock_* - * documentation in OpenSSL's docs for more info. */ -static void -openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v, - const char *file, int line) -{ - (void)file; - (void)line; - tor_mutex_free(v->lock); - tor_free(v); -} -#endif /* 0 */ - /** @{ */ -/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being - * multithreaded. Returns 0. */ -static int -setup_openssl_threading(void) -{ -#ifndef NEW_THREAD_API - int i; - int n = CRYPTO_num_locks(); - n_openssl_mutexes_ = n; - openssl_mutexes_ = tor_calloc(n, sizeof(tor_mutex_t *)); - for (i=0; i < n; ++i) - openssl_mutexes_[i] = tor_mutex_new(); - CRYPTO_set_locking_callback(openssl_locking_cb_); - CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id); -#endif /* !defined(NEW_THREAD_API) */ -#if 0 - CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_); - CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_); - CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_); -#endif - return 0; -} - /** Uninitialize the crypto library. Return 0 on success. Does not detect * failure. */ @@ -3470,6 +2450,8 @@ crypto_global_cleanup(void) if (dh_param_g) BN_clear_free(dh_param_g); + dh_param_p = dh_param_p_tls = dh_param_g = NULL; + #ifndef DISABLE_ENGINES ENGINE_cleanup(); #endif @@ -3477,22 +2459,13 @@ crypto_global_cleanup(void) CONF_modules_unload(1); CRYPTO_cleanup_all_ex_data(); -#ifndef NEW_THREAD_API - if (n_openssl_mutexes_) { - int n = n_openssl_mutexes_; - tor_mutex_t **ms = openssl_mutexes_; - int i; - openssl_mutexes_ = NULL; - n_openssl_mutexes_ = 0; - for (i=0;i<n;++i) { - tor_mutex_free(ms[i]); - } - tor_free(ms); - } -#endif /* !defined(NEW_THREAD_API) */ + crypto_openssl_free_all(); + + crypto_early_initialized_ = 0; + crypto_global_initialized_ = 0; + have_seeded_siphash = 0; + siphash_unset_global_key(); - tor_free(crypto_openssl_version_str); - tor_free(crypto_openssl_header_version_str); return 0; } diff --git a/src/common/crypto.h b/src/common/crypto.h index f9aeeee2c0..a9c8837b9e 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -19,42 +19,11 @@ #include "torint.h" #include "testsupport.h" #include "compat.h" +#include "util.h" +#include "crypto_rsa.h" -#include <openssl/engine.h> #include "keccak-tiny/keccak-tiny.h" -/* - Macro to create an arbitrary OpenSSL version number as used by - OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard - to read. - - Don't use this directly, instead use one of the other OPENSSL_V macros - below. - - The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit - status. - */ -#define OPENSSL_VER(a,b,c,d,e) \ - (((a)<<28) | \ - ((b)<<20) | \ - ((c)<<12) | \ - ((d)<< 4) | \ - (e)) -/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the - * version for the released version of 0.9.8j */ -#define OPENSSL_V(a,b,c,d) \ - OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf) -/** An openssl release number for the first release in the series. For - * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL - * 1.0.0. */ -#define OPENSSL_V_NOPATCH(a,b,c) \ - OPENSSL_VER((a),(b),(c),0,0xf) -/** The first version that would occur for any alpha or beta in an openssl - * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released - * 0.9.7, and less than any released 0.9.8. */ -#define OPENSSL_V_SERIES(a,b,c) \ - OPENSSL_VER((a),(b),(c),0,0) - /** Length of the output of our message digest. */ #define DIGEST_LEN 20 /** Length of the output of our second (improved) message digests. (For now @@ -68,8 +37,6 @@ #define CIPHER_IV_LEN 16 /** Length of our symmetric cipher's keys of 256-bit. */ #define CIPHER256_KEY_LEN 32 -/** Length of our public keys. */ -#define PK_BYTES (1024/8) /** Length of our DH keys. */ #define DH_BYTES (1024/8) @@ -86,12 +53,6 @@ * signs removed. */ #define BASE64_DIGEST512_LEN 86 -/** Constant used to indicate OAEP padding for public-key encryption */ -#define PK_PKCS1_OAEP_PADDING 60002 - -/** Number of bytes added for PKCS1-OAEP padding. */ -#define PKCS1_OAEP_PADDING_OVERHEAD 42 - /** Length of encoded public key fingerprints, including space; but not * including terminating NUL. */ #define FINGERPRINT_LEN 49 @@ -124,15 +85,12 @@ typedef struct { char d[N_COMMON_DIGEST_ALGORITHMS][DIGEST256_LEN]; } common_digests_t; -typedef struct crypto_pk_t crypto_pk_t; typedef struct aes_cnt_cipher crypto_cipher_t; typedef struct crypto_digest_t crypto_digest_t; typedef struct crypto_xof_t crypto_xof_t; typedef struct crypto_dh_t crypto_dh_t; /* global state */ -const char * crypto_openssl_get_version_str(void); -const char * crypto_openssl_get_header_version_str(void); int crypto_early_init(void) ATTR_WUR; int crypto_global_init(int hardwareAccel, const char *accelName, @@ -145,9 +103,6 @@ void crypto_thread_cleanup(void); int crypto_global_cleanup(void); /* environment setup */ -MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void)); -void crypto_pk_free(crypto_pk_t *env); - void crypto_set_tls_dh_prime(void); crypto_cipher_t *crypto_cipher_new(const char *key); crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits); @@ -155,49 +110,14 @@ crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv); crypto_cipher_t *crypto_cipher_new_with_iv_and_bits(const uint8_t *key, const uint8_t *iv, int bits); -void crypto_cipher_free(crypto_cipher_t *env); - -/* public key crypto */ -MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)); -#define crypto_pk_generate_key(env) \ - crypto_pk_generate_key_with_bits((env), (PK_BYTES*8)) - -int crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile); -int crypto_pk_write_public_key_to_string(crypto_pk_t *env, - char **dest, size_t *len); -int crypto_pk_write_private_key_to_string(crypto_pk_t *env, - char **dest, size_t *len); -int crypto_pk_read_public_key_from_string(crypto_pk_t *env, - const char *src, size_t len); -int crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len); -int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname); - -int crypto_pk_check_key(crypto_pk_t *env); -int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); -int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); -size_t crypto_pk_keysize(const crypto_pk_t *env); -int crypto_pk_num_bits(crypto_pk_t *env); -crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); -crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); -int crypto_pk_key_is_private(const crypto_pk_t *key); -int crypto_pk_public_exponent_ok(crypto_pk_t *env); - -int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, int padding); -int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure); -MOCK_DECL(int, crypto_pk_public_checksig,(const crypto_pk_t *env, - char *to, size_t tolen, - const char *from, size_t fromlen)); +void crypto_cipher_free_(crypto_cipher_t *env); +#define crypto_cipher_free(c) \ + FREE_AND_NULL(crypto_cipher_t, crypto_cipher_free_, (c)) + +/* public key crypto */ MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen)); -int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen); int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to, @@ -208,17 +128,9 @@ int crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); - -int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len); -crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out); int crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out); -int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); -int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); - -int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out); -crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len); /* symmetric crypto */ const char *crypto_cipher_get_key(crypto_cipher_t *env); @@ -258,7 +170,9 @@ int crypto_digest_algorithm_parse_name(const char *name); crypto_digest_t *crypto_digest_new(void); crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm); crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm); -void crypto_digest_free(crypto_digest_t *digest); +void crypto_digest_free_(crypto_digest_t *digest); +#define crypto_digest_free(d) \ + FREE_AND_NULL(crypto_digest_t, crypto_digest_free_, (d)) void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len); void crypto_digest_get_digest(crypto_digest_t *digest, @@ -276,7 +190,9 @@ void crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out, crypto_xof_t *crypto_xof_new(void); void crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len); void crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len); -void crypto_xof_free(crypto_xof_t *xof); +void crypto_xof_free_(crypto_xof_t *xof); +#define crypto_xof_free(xof) \ + FREE_AND_NULL(crypto_xof_t, crypto_xof_free_, (xof)) /* Key negotiation */ #define DH_TYPE_CIRCUIT 1 @@ -291,7 +207,8 @@ int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_out_len); -void crypto_dh_free(crypto_dh_t *dh); +void crypto_dh_free_(crypto_dh_t *dh); +#define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh)) int crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, @@ -329,13 +246,7 @@ void memwipe(void *mem, uint8_t byte, size_t sz); /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ -struct rsa_st; -struct evp_pkey_st; struct dh_st; -struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env); -crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); -MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env, - int private)); struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); @@ -352,7 +263,6 @@ extern int break_strongest_rng_fallback; #endif /* defined(CRYPTO_PRIVATE) */ #ifdef TOR_UNIT_TESTS -void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest); #endif diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h index d024ab79f5..11f7423b07 100644 --- a/src/common/crypto_curve25519.h +++ b/src/common/crypto_curve25519.h @@ -6,6 +6,7 @@ #include "testsupport.h" #include "torint.h" +#include "crypto_openssl_mgt.h" /** Length of a curve25519 public key when encoded. */ #define CURVE25519_PUBKEY_LEN 32 diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index 94b23e31b9..b962a59de1 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -225,8 +225,8 @@ ed25519_public_key_is_zero(const ed25519_public_key_t *pubkey) /* Return a heap-allocated array that contains <b>msg</b> prefixed by the * string <b>prefix_str</b>. Set <b>final_msg_len_out</b> to the size of the - * final array. If an error occured, return NULL. It's the resonsibility of the - * caller to free the returned array. */ + * final array. If an error occurred, return NULL. It's the responsibility of + * the caller to free the returned array. */ static uint8_t * get_prefixed_msg(const uint8_t *msg, size_t msg_len, const char *prefix_str, @@ -259,7 +259,7 @@ get_prefixed_msg(const uint8_t *msg, size_t msg_len, * Set <b>signature_out</b> to a signature of the <b>len</b>-byte message * <b>msg</b>, using the secret and public key in <b>keypair</b>. * - * Return 0 if we successfuly signed the message, otherwise return -1. + * Return 0 if we successfully signed the message, otherwise return -1. */ int ed25519_sign(ed25519_signature_t *signature_out, @@ -529,7 +529,7 @@ ed25519_keypair_blind(ed25519_keypair_t *out, /** * Given an ed25519 public key in <b>inp</b>, generate a corresponding blinded * public key in <b>out</b>, blinded with the 32-byte parameter in - * <b>param</b>. Return 0 on sucess, -1 on railure. + * <b>param</b>. Return 0 on success, -1 on railure. */ int ed25519_public_blind(ed25519_public_key_t *out, @@ -622,7 +622,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, /** Release all storage held for <b>kp</b>. */ void -ed25519_keypair_free(ed25519_keypair_t *kp) +ed25519_keypair_free_(ed25519_keypair_t *kp) { if (! kp) return; @@ -719,7 +719,7 @@ ed25519_impl_spot_check,(void)) return -1; /* XXX/yawning: Someone that's more paranoid than I am, can write "Assume - * ref0 is cannonical, and fuzz impl against it" if they want, but I doubt + * ref0 is canonical, and fuzz impl against it" if they want, but I doubt * that will catch anything that the known answer tests won't. */ goto end; @@ -762,7 +762,7 @@ pick_ed25519_impl(void) /* LCOV_EXCL_STOP */ } -/* Initialize the Ed25519 implementation. This is neccessary if you're +/* Initialize the Ed25519 implementation. This is necessary if you're * going to use them in a multithreaded setting, and not otherwise. */ void ed25519_init(void) diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 8d13a487d6..74269ccffd 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -7,6 +7,7 @@ #include "testsupport.h" #include "torint.h" #include "crypto_curve25519.h" +#include "util.h" #define ED25519_PUBKEY_LEN 32 #define ED25519_SECKEY_LEN 64 @@ -117,7 +118,9 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, char **tag_out, const char *filename); -void ed25519_keypair_free(ed25519_keypair_t *kp); +void ed25519_keypair_free_(ed25519_keypair_t *kp); +#define ed25519_keypair_free(kp) \ + FREE_AND_NULL(ed25519_keypair_t, ed25519_keypair_free_, (kp)) int ed25519_pubkey_eq(const ed25519_public_key_t *key1, const ed25519_public_key_t *key2); diff --git a/src/common/crypto_openssl_mgt.c b/src/common/crypto_openssl_mgt.c new file mode 100644 index 0000000000..ea3519efa2 --- /dev/null +++ b/src/common/crypto_openssl_mgt.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_openssl.c + * + * \brief Block of functions related to operations from OpenSSL. + **/ + +#include "compat_openssl.h" +#include "crypto_openssl_mgt.h" + +DISABLE_GCC_WARNING(redundant-decls) + +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/engine.h> +#include <openssl/rand.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/conf.h> +#include <openssl/hmac.h> +#include <openssl/crypto.h> + +ENABLE_GCC_WARNING(redundant-decls) + +#ifndef NEW_THREAD_API +/** A number of preallocated mutexes for use by OpenSSL. */ +static tor_mutex_t **openssl_mutexes_ = NULL; +/** How many mutexes have we allocated for use by OpenSSL? */ +static int n_openssl_mutexes_ = 0; +#endif /* !defined(NEW_THREAD_API) */ + +/** Declare STATIC functions */ +STATIC char * parse_openssl_version_str(const char *raw_version); +#ifndef NEW_THREAD_API +STATIC void openssl_locking_cb_(int mode, int n, const char *file, int line); +STATIC void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid); +#endif + +/* Returns a trimmed and human-readable version of an openssl version string +* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10 +* May 2012' and this will parse them into a form similar to '1.0.0b' */ +STATIC char * +parse_openssl_version_str(const char *raw_version) +{ + const char *end_of_version = NULL; + /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's + trim that down. */ + if (!strcmpstart(raw_version, "OpenSSL ")) { + raw_version += strlen("OpenSSL "); + end_of_version = strchr(raw_version, ' '); + } + + if (end_of_version) + return tor_strndup(raw_version, + end_of_version-raw_version); + else + return tor_strdup(raw_version); +} + +static char *crypto_openssl_version_str = NULL; +/* Return a human-readable version of the run-time openssl version number. */ +const char * +crypto_openssl_get_version_str(void) +{ + if (crypto_openssl_version_str == NULL) { + const char *raw_version = OpenSSL_version(OPENSSL_VERSION); + crypto_openssl_version_str = parse_openssl_version_str(raw_version); + } + return crypto_openssl_version_str; +} + +static char *crypto_openssl_header_version_str = NULL; +/* Return a human-readable version of the compile-time openssl version +* number. */ +const char * +crypto_openssl_get_header_version_str(void) +{ + if (crypto_openssl_header_version_str == NULL) { + crypto_openssl_header_version_str = + parse_openssl_version_str(OPENSSL_VERSION_TEXT); + } + return crypto_openssl_header_version_str; +} + +#ifndef OPENSSL_THREADS +#error OpenSSL has been built without thread support. Tor requires an \ + OpenSSL library with thread support enabled. +#endif + +#ifndef NEW_THREAD_API +/** Helper: OpenSSL uses this callback to manipulate mutexes. */ +STATIC void +openssl_locking_cb_(int mode, int n, const char *file, int line) +{ + (void)file; + (void)line; + if (!openssl_mutexes_) + /* This is not a really good fix for the + * "release-freed-lock-from-separate-thread-on-shutdown" problem, but + * it can't hurt. */ + return; + if (mode & CRYPTO_LOCK) + tor_mutex_acquire(openssl_mutexes_[n]); + else + tor_mutex_release(openssl_mutexes_[n]); +} + +STATIC void +tor_set_openssl_thread_id(CRYPTO_THREADID *threadid) +{ + CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id()); +} +#endif /* !defined(NEW_THREAD_API) */ + +/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being + * multithreaded. Returns 0. */ +int +setup_openssl_threading(void) +{ +#ifndef NEW_THREAD_API + int i; + int n = CRYPTO_num_locks(); + n_openssl_mutexes_ = n; + openssl_mutexes_ = tor_calloc(n, sizeof(tor_mutex_t *)); + for (i=0; i < n; ++i) + openssl_mutexes_[i] = tor_mutex_new(); + CRYPTO_set_locking_callback(openssl_locking_cb_); + CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id); +#endif /* !defined(NEW_THREAD_API) */ + return 0; +} + +/** free OpenSSL variables */ +void +crypto_openssl_free_all(void) +{ + tor_free(crypto_openssl_version_str); + tor_free(crypto_openssl_header_version_str); + +#ifndef NEW_THREAD_API + if (n_openssl_mutexes_) { + int n = n_openssl_mutexes_; + tor_mutex_t **ms = openssl_mutexes_; + int i; + openssl_mutexes_ = NULL; + n_openssl_mutexes_ = 0; + for (i=0;i<n;++i) { + tor_mutex_free(ms[i]); + } + tor_free(ms); + } +#endif /* !defined(NEW_THREAD_API) */ +} + diff --git a/src/common/crypto_openssl_mgt.h b/src/common/crypto_openssl_mgt.h new file mode 100644 index 0000000000..09b6737962 --- /dev/null +++ b/src/common/crypto_openssl_mgt.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_openssl.h + * + * \brief Headers for crypto_openssl.c + **/ + +#ifndef TOR_CRYPTO_OPENSSL_H +#define TOR_CRYPTO_OPENSSL_H + +#include <stdio.h> +#include "util.h" + +#include <openssl/engine.h> + +/* + Macro to create an arbitrary OpenSSL version number as used by + OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard + to read. + + Don't use this directly, instead use one of the other OPENSSL_V macros + below. + + The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit + status. + */ +#define OPENSSL_VER(a,b,c,d,e) \ + (((a)<<28) | \ + ((b)<<20) | \ + ((c)<<12) | \ + ((d)<< 4) | \ + (e)) +/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the + * version for the released version of 0.9.8j */ +#define OPENSSL_V(a,b,c,d) \ + OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf) +/** An openssl release number for the first release in the series. For + * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL + * 1.0.0. */ +#define OPENSSL_V_NOPATCH(a,b,c) \ + OPENSSL_VER((a),(b),(c),0,0xf) +/** The first version that would occur for any alpha or beta in an openssl + * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released + * 0.9.7, and less than any released 0.9.8. */ +#define OPENSSL_V_SERIES(a,b,c) \ + OPENSSL_VER((a),(b),(c),0,0) + +#ifdef ANDROID +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES +#endif + +#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && \ + !defined(LIBRESSL_VERSION_NUMBER) +/* OpenSSL as of 1.1.0pre4 has an "new" thread API, which doesn't require + * seting up various callbacks. + * + * OpenSSL 1.1.0pre4 has a messed up `ERR_remove_thread_state()` prototype, + * while the previous one was restored in pre5, and the function made a no-op + * (along with a deprecated annotation, which produces a compiler warning). + * + * While it is possible to support all three versions of the thread API, + * a version that existed only for one snapshot pre-release is kind of + * pointless, so let's not. + */ +#define NEW_THREAD_API +#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */ + +/* global openssl state */ +const char * crypto_openssl_get_version_str(void); +const char * crypto_openssl_get_header_version_str(void); + +/* OpenSSL threading setup function */ +int setup_openssl_threading(void); + +/* Tor OpenSSL utility functions */ +void crypto_openssl_free_all(void); + +#endif /* !defined(TOR_CRYPTO_OPENSSL_H) */ + diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h index cee8653587..a26b6d2c17 100644 --- a/src/common/crypto_pwbox.h +++ b/src/common/crypto_pwbox.h @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #ifndef CRYPTO_PWBOX_H_INCLUDED_ #define CRYPTO_PWBOX_H_INCLUDED_ diff --git a/src/common/crypto_rsa.c b/src/common/crypto_rsa.c new file mode 100644 index 0000000000..fa572580a4 --- /dev/null +++ b/src/common/crypto_rsa.c @@ -0,0 +1,923 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_rsa.c + * \brief Block of functions related with RSA utilities and operations. + **/ + +#include "crypto_rsa.h" +#include "crypto.h" +#include "compat_openssl.h" +#include "crypto_curve25519.h" +#include "crypto_ed25519.h" +#include "crypto_format.h" + +DISABLE_GCC_WARNING(redundant-decls) + +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/engine.h> +#include <openssl/rand.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/conf.h> +#include <openssl/hmac.h> + +ENABLE_GCC_WARNING(redundant-decls) + +#include "torlog.h" +#include "util.h" +#include "util_format.h" + +/** Declaration for crypto_pk_t structure. */ +struct crypto_pk_t +{ + int refs; /**< reference count, so we don't have to copy keys */ + RSA *key; /**< The key itself */ +}; + +/** Log all pending crypto errors at level <b>severity</b>. Use + * <b>doing</b> to describe our current activities. + */ +static void +crypto_log_errors(int severity, const char *doing) +{ + unsigned long err; + const char *msg, *lib, *func; + while ((err = ERR_get_error()) != 0) { + msg = (const char*)ERR_reason_error_string(err); + lib = (const char*)ERR_lib_error_string(err); + func = (const char*)ERR_func_error_string(err); + if (!msg) msg = "(null)"; + if (!lib) lib = "(null)"; + if (!func) func = "(null)"; + if (BUG(!doing)) doing = "(null)"; + tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", + doing, msg, lib, func); + } +} + +/** Return the number of bytes added by padding method <b>padding</b>. + */ +int +crypto_get_rsa_padding_overhead(int padding) +{ + switch (padding) + { + case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; + default: tor_assert(0); return -1; // LCOV_EXCL_LINE + } +} + +/** Given a padding method <b>padding</b>, return the correct OpenSSL constant. + */ +int +crypto_get_rsa_padding(int padding) +{ + switch (padding) + { + case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; + default: tor_assert(0); return -1; // LCOV_EXCL_LINE + } +} + +/** used internally: quicly validate a crypto_pk_t object as a private key. + * Return 1 iff the public key is valid, 0 if obviously invalid. + */ +static int +crypto_pk_private_ok(const crypto_pk_t *k) +{ +#ifdef OPENSSL_1_1_API + if (!k || !k->key) + return 0; + + const BIGNUM *p, *q; + RSA_get0_factors(k->key, &p, &q); + return p != NULL; /* XXX/yawning: Should we check q? */ +#else /* !(defined(OPENSSL_1_1_API)) */ + return k && k->key && k->key->p; +#endif /* defined(OPENSSL_1_1_API) */ +} + +/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ +crypto_pk_t * +crypto_new_pk_from_rsa_(RSA *rsa) +{ + crypto_pk_t *env; + tor_assert(rsa); + env = tor_malloc(sizeof(crypto_pk_t)); + env->refs = 1; + env->key = rsa; + return env; +} + +/** Helper, used by tor-gencert.c. Return the RSA from a + * crypto_pk_t. */ +RSA * +crypto_pk_get_rsa_(crypto_pk_t *env) +{ + return env->key; +} + +/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff + * private is set, include the private-key portion of the key. Return a valid + * pointer on success, and NULL on failure. */ +MOCK_IMPL(EVP_PKEY *, +crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private)) +{ + RSA *key = NULL; + EVP_PKEY *pkey = NULL; + tor_assert(env->key); + if (private) { + if (!(key = RSAPrivateKey_dup(env->key))) + goto error; + } else { + if (!(key = RSAPublicKey_dup(env->key))) + goto error; + } + if (!(pkey = EVP_PKEY_new())) + goto error; + if (!(EVP_PKEY_assign_RSA(pkey, key))) + goto error; + return pkey; + error: + if (pkey) + EVP_PKEY_free(pkey); + if (key) + RSA_free(key); + return NULL; +} + +/** Allocate and return storage for a public key. The key itself will not yet + * be set. + */ +MOCK_IMPL(crypto_pk_t *, +crypto_pk_new,(void)) +{ + RSA *rsa; + + rsa = RSA_new(); + tor_assert(rsa); + return crypto_new_pk_from_rsa_(rsa); +} + +/** Release a reference to an asymmetric key; when all the references + * are released, free the key. + */ +void +crypto_pk_free_(crypto_pk_t *env) +{ + if (!env) + return; + + if (--env->refs > 0) + return; + tor_assert(env->refs == 0); + + if (env->key) + RSA_free(env->key); + + tor_free(env); +} + +/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>. + * Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)) +{ + tor_assert(env); + + if (env->key) { + RSA_free(env->key); + env->key = NULL; + } + + { + BIGNUM *e = BN_new(); + RSA *r = NULL; + if (!e) + goto done; + if (! BN_set_word(e, 65537)) + goto done; + r = RSA_new(); + if (!r) + goto done; + if (RSA_generate_key_ex(r, bits, e, NULL) == -1) + goto done; + + env->key = r; + r = NULL; + done: + if (e) + BN_clear_free(e); + if (r) + RSA_free(r); + } + + if (!env->key) { + crypto_log_errors(LOG_WARN, "generating RSA key"); + return -1; + } + + return 0; +} + +/** A PEM callback that always reports a failure to get a password */ +static int +pem_no_password_cb(char *buf, int size, int rwflag, void *u) +{ + (void)buf; + (void)size; + (void)rwflag; + (void)u; + return 0; +} + +/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b> + * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1, + * the string is nul-terminated. + */ +int +crypto_pk_read_private_key_from_string(crypto_pk_t *env, + const char *s, ssize_t len) +{ + BIO *b; + + tor_assert(env); + tor_assert(s); + tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); + + /* Create a read-only memory BIO, backed by the string 's' */ + b = BIO_new_mem_buf((char*)s, (int)len); + if (!b) + return -1; + + if (env->key) + RSA_free(env->key); + + env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL); + + BIO_free(b); + + if (!env->key) { + crypto_log_errors(LOG_WARN, "Error parsing private key"); + return -1; + } + return 0; +} + +/** Read a PEM-encoded private key from the file named by + * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure. + */ +int +crypto_pk_read_private_key_from_filename(crypto_pk_t *env, + const char *keyfile) +{ + char *contents; + int r; + + /* Read the file into a string. */ + contents = read_file_to_str(keyfile, 0, NULL); + if (!contents) { + log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); + return -1; + } + + /* Try to parse it. */ + r = crypto_pk_read_private_key_from_string(env, contents, -1); + memwipe(contents, 0, strlen(contents)); + tor_free(contents); + if (r) + return -1; /* read_private_key_from_string already warned, so we don't.*/ + + /* Make sure it's valid. */ + if (crypto_pk_check_key(env) <= 0) + return -1; + + return 0; +} + +/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on + * success, -1 on failure. */ +static int +crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, + size_t *len, int is_public) +{ + BUF_MEM *buf; + BIO *b; + int r; + + tor_assert(env); + tor_assert(env->key); + tor_assert(dest); + + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + if (!b) + return -1; + + /* Now you can treat b as if it were a file. Just use the + * PEM_*_bio_* functions instead of the non-bio variants. + */ + if (is_public) + r = PEM_write_bio_RSAPublicKey(b, env->key); + else + r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); + + if (!r) { + crypto_log_errors(LOG_WARN, "writing RSA key to string"); + BIO_free(b); + return -1; + } + + BIO_get_mem_ptr(b, &buf); + + *dest = tor_malloc(buf->length+1); + memcpy(*dest, buf->data, buf->length); + (*dest)[buf->length] = 0; /* nul terminate it */ + *len = buf->length; + + BIO_free(b); + + return 0; +} + +/** PEM-encode the public key portion of <b>env</b> and write it to a + * newly allocated string. On success, set *<b>dest</b> to the new + * string, *<b>len</b> to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, + size_t *len) +{ + return crypto_pk_write_key_to_string_impl(env, dest, len, 1); +} + +/** PEM-encode the private key portion of <b>env</b> and write it to a + * newly allocated string. On success, set *<b>dest</b> to the new + * string, *<b>len</b> to the string's length, and return 0. On + * failure, return -1. + */ +int +crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, + size_t *len) +{ + return crypto_pk_write_key_to_string_impl(env, dest, len, 0); +} + +/** Read a PEM-encoded public key from the first <b>len</b> characters of + * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on + * failure. + */ +int +crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, + size_t len) +{ + BIO *b; + + tor_assert(env); + tor_assert(src); + tor_assert(len<INT_MAX); + + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + if (!b) + return -1; + + BIO_write(b, src, (int)len); + + if (env->key) + RSA_free(env->key); + env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL); + BIO_free(b); + if (!env->key) { + crypto_log_errors(LOG_WARN, "reading public key from string"); + return -1; + } + + return 0; +} + +/** Write the private key from <b>env</b> into the file named by <b>fname</b>, + * PEM-encoded. Return 0 on success, -1 on failure. + */ +int +crypto_pk_write_private_key_to_filename(crypto_pk_t *env, + const char *fname) +{ + BIO *bio; + char *cp; + long len; + char *s; + int r; + + tor_assert(crypto_pk_private_ok(env)); + + if (!(bio = BIO_new(BIO_s_mem()))) + return -1; + if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) + == 0) { + crypto_log_errors(LOG_WARN, "writing private key"); + BIO_free(bio); + return -1; + } + len = BIO_get_mem_data(bio, &cp); + tor_assert(len >= 0); + s = tor_malloc(len+1); + memcpy(s, cp, len); + s[len]='\0'; + r = write_str_to_file(fname, s, 0); + BIO_free(bio); + memwipe(s, 0, strlen(s)); + tor_free(s); + return r; +} + +/** Return true iff <b>env</b> has a valid key. + */ +int +crypto_pk_check_key(crypto_pk_t *env) +{ + int r; + tor_assert(env); + + r = RSA_check_key(env->key); + if (r <= 0) + crypto_log_errors(LOG_WARN,"checking RSA key"); + return r; +} + +/** Return true iff <b>key</b> contains the private-key portion of the RSA + * key. */ +int +crypto_pk_key_is_private(const crypto_pk_t *key) +{ + tor_assert(key); + return crypto_pk_private_ok(key); +} + +/** Return true iff <b>env</b> contains a public key whose public exponent + * equals 65537. + */ +int +crypto_pk_public_exponent_ok(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + const BIGNUM *e; + +#ifdef OPENSSL_1_1_API + const BIGNUM *n, *d; + RSA_get0_key(env->key, &n, &e, &d); +#else + e = env->key->e; +#endif /* defined(OPENSSL_1_1_API) */ + return BN_is_word(e, 65537); +} + +/** Compare the public-key components of a and b. Return less than 0 + * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is + * considered to be less than all non-NULL keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. + */ +int +crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) +{ + int result; + char a_is_non_null = (a != NULL) && (a->key != NULL); + char b_is_non_null = (b != NULL) && (b->key != NULL); + char an_argument_is_null = !a_is_non_null | !b_is_non_null; + + result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); + if (an_argument_is_null) + return result; + + const BIGNUM *a_n, *a_e; + const BIGNUM *b_n, *b_e; + +#ifdef OPENSSL_1_1_API + const BIGNUM *a_d, *b_d; + RSA_get0_key(a->key, &a_n, &a_e, &a_d); + RSA_get0_key(b->key, &b_n, &b_e, &b_d); +#else + a_n = a->key->n; + a_e = a->key->e; + b_n = b->key->n; + b_e = b->key->e; +#endif /* defined(OPENSSL_1_1_API) */ + + tor_assert(a_n != NULL && a_e != NULL); + tor_assert(b_n != NULL && b_e != NULL); + + result = BN_cmp(a_n, b_n); + if (result) + return result; + return BN_cmp(a_e, b_e); +} + +/** Compare the public-key components of a and b. Return non-zero iff + * a==b. A NULL key is considered to be distinct from all non-NULL + * keys, and equal to itself. + * + * Note that this may leak information about the keys through timing. + */ +int +crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b) +{ + return (crypto_pk_cmp_keys(a, b) == 0); +} + +/** Return the size of the public key modulus in <b>env</b>, in bytes. */ +size_t +crypto_pk_keysize(const crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + return (size_t) RSA_size((RSA*)env->key); +} + +/** Return the size of the public key modulus of <b>env</b>, in bits. */ +int +crypto_pk_num_bits(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + +#ifdef OPENSSL_1_1_API + /* It's so stupid that there's no other way to check that n is valid + * before calling RSA_bits(). + */ + const BIGNUM *n, *e, *d; + RSA_get0_key(env->key, &n, &e, &d); + tor_assert(n != NULL); + + return RSA_bits(env->key); +#else /* !(defined(OPENSSL_1_1_API)) */ + tor_assert(env->key->n); + return BN_num_bits(env->key->n); +#endif /* defined(OPENSSL_1_1_API) */ +} + +/** Increase the reference count of <b>env</b>, and return it. + */ +crypto_pk_t * +crypto_pk_dup_key(crypto_pk_t *env) +{ + tor_assert(env); + tor_assert(env->key); + + env->refs++; + return env; +} + +#ifdef TOR_UNIT_TESTS +/** For testing: replace dest with src. (Dest must have a refcount + * of 1) */ +void +crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) +{ + tor_assert(dest); + tor_assert(dest->refs == 1); + tor_assert(src); + RSA_free(dest->key); + dest->key = RSAPrivateKey_dup(src->key); +} +#endif /* defined(TOR_UNIT_TESTS) */ + +/** Make a real honest-to-goodness copy of <b>env</b>, and return it. + * Returns NULL on failure. */ +crypto_pk_t * +crypto_pk_copy_full(crypto_pk_t *env) +{ + RSA *new_key; + int privatekey = 0; + tor_assert(env); + tor_assert(env->key); + + if (crypto_pk_private_ok(env)) { + new_key = RSAPrivateKey_dup(env->key); + privatekey = 1; + } else { + new_key = RSAPublicKey_dup(env->key); + } + if (!new_key) { + /* LCOV_EXCL_START + * + * We can't cause RSA*Key_dup() to fail, so we can't really test this. + */ + log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", + privatekey?"private":"public"); + crypto_log_errors(LOG_ERR, + privatekey ? "Duplicating a private key" : + "Duplicating a public key"); + tor_fragile_assert(); + return NULL; + /* LCOV_EXCL_STOP */ + } + + return crypto_new_pk_from_rsa_(new_key); +} + +/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key + * in <b>env</b>, using the padding method <b>padding</b>. On success, + * write the result to <b>to</b>, and return the number of bytes + * written. On failure, return -1. + * + * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be + * at least the length of the modulus of <b>env</b>. + */ +int +crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen, int padding) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen<INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + + r = RSA_public_encrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, crypto_get_rsa_padding(padding)); + if (r<0) { + crypto_log_errors(LOG_WARN, "performing RSA encryption"); + return -1; + } + return r; +} + +/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key + * in <b>env</b>, using the padding method <b>padding</b>. On success, + * write the result to <b>to</b>, and return the number of bytes + * written. On failure, return -1. + * + * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be + * at least the length of the modulus of <b>env</b>. + */ +int +crypto_pk_private_decrypt(crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen, + int padding, int warnOnFailure) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(env->key); + tor_assert(fromlen<INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + if (!crypto_pk_key_is_private(env)) + /* Not a private key */ + return -1; + + r = RSA_private_decrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, crypto_get_rsa_padding(padding)); + + if (r<0) { + crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, + "performing RSA decryption"); + return -1; + } + return r; +} + +/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the + * public key in <b>env</b>, using PKCS1 padding. On success, write the + * signed data to <b>to</b>, and return the number of bytes written. + * On failure, return -1. + * + * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be + * at least the length of the modulus of <b>env</b>. + */ +MOCK_IMPL(int, +crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen)) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen < INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + r = RSA_public_decrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + env->key, RSA_PKCS1_PADDING); + + if (r<0) { + crypto_log_errors(LOG_INFO, "checking RSA signature"); + return -1; + } + return r; +} + +/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in + * <b>env</b>, using PKCS1 padding. On success, write the signature to + * <b>to</b>, and return the number of bytes written. On failure, return + * -1. + * + * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be + * at least the length of the modulus of <b>env</b>. + */ +int +crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen) +{ + int r; + tor_assert(env); + tor_assert(from); + tor_assert(to); + tor_assert(fromlen < INT_MAX); + tor_assert(tolen >= crypto_pk_keysize(env)); + if (!crypto_pk_key_is_private(env)) + /* Not a private key */ + return -1; + + r = RSA_private_encrypt((int)fromlen, + (unsigned char*)from, (unsigned char*)to, + (RSA*)env->key, RSA_PKCS1_PADDING); + if (r<0) { + crypto_log_errors(LOG_WARN, "generating RSA signature"); + return -1; + } + return r; +} + +/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>. + * Return -1 on error, or the number of characters used on success. + */ +int +crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len) +{ + int len; + unsigned char *buf = NULL; + + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) + return -1; + + if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { + OPENSSL_free(buf); + return -1; + } + /* We don't encode directly into 'dest', because that would be illegal + * type-punning. (C99 is smarter than me, C99 is smarter than me...) + */ + memcpy(dest,buf,len); + OPENSSL_free(buf); + return len; +} + +/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on + * success and NULL on failure. + */ +crypto_pk_t * +crypto_pk_asn1_decode(const char *str, size_t len) +{ + RSA *rsa; + unsigned char *buf; + const unsigned char *cp; + cp = buf = tor_malloc(len); + memcpy(buf,str,len); + rsa = d2i_RSAPublicKey(NULL, &cp, len); + tor_free(buf); + if (!rsa) { + crypto_log_errors(LOG_WARN,"decoding public key"); + return NULL; + } + return crypto_new_pk_from_rsa_(rsa); +} + +/** Given a private or public key <b>pk</b>, put a fingerprint of the + * public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of + * space). Return 0 on success, -1 on failure. + * + * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding + * of the public key, converted to hexadecimal, in upper case, with a + * space after every four digits. + * + * If <b>add_space</b> is false, omit the spaces. + */ +int +crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) +{ + char digest[DIGEST_LEN]; + char hexdigest[HEX_DIGEST_LEN+1]; + if (crypto_pk_get_digest(pk, digest)) { + return -1; + } + base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); + if (add_space) { + crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); + } else { + strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); + } + return 0; +} + +/** Given a private or public key <b>pk</b>, put a hashed fingerprint of + * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 + * bytes of space). Return 0 on success, -1 on failure. + * + * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest + * of the ASN.1 encoding of the public key, converted to hexadecimal, in + * upper case. + */ +int +crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) +{ + char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN]; + if (crypto_pk_get_digest(pk, digest)) { + return -1; + } + if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) { + return -1; + } + base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); + return 0; +} + +/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the + * Base64 encoding of the DER representation of the private key as a NUL + * terminated string, and return it via <b>priv_out</b>. Return 0 on + * success, -1 on failure. + * + * It is the caller's responsibility to sanitize and free the resulting buffer. + */ +int +crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) +{ + unsigned char *der = NULL; + int der_len; + int ret = -1; + + *priv_out = NULL; + + der_len = i2d_RSAPrivateKey(pk->key, &der); + if (der_len < 0 || der == NULL) + return ret; + + size_t priv_len = base64_encode_size(der_len, 0) + 1; + char *priv = tor_malloc_zero(priv_len); + if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { + *priv_out = priv; + ret = 0; + } else { + tor_free(priv); + } + + memwipe(der, 0, der_len); + OPENSSL_free(der); + return ret; +} + +/** Given a string containing the Base64 encoded DER representation of the + * private key <b>str</b>, decode and return the result on success, or NULL + * on failure. + */ +crypto_pk_t * +crypto_pk_base64_decode(const char *str, size_t len) +{ + crypto_pk_t *pk = NULL; + + char *der = tor_malloc_zero(len + 1); + int der_len = base64_decode(der, len, str, len); + if (der_len <= 0) { + log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); + goto out; + } + + const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ + RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); + if (!rsa) { + crypto_log_errors(LOG_WARN, "decoding private key"); + goto out; + } + + pk = crypto_new_pk_from_rsa_(rsa); + + /* Make sure it's valid. */ + if (crypto_pk_check_key(pk) <= 0) { + crypto_pk_free(pk); + pk = NULL; + goto out; + } + + out: + memwipe(der, 0, len + 1); + tor_free(der); + return pk; +} + diff --git a/src/common/crypto_rsa.h b/src/common/crypto_rsa.h new file mode 100644 index 0000000000..5b9025c629 --- /dev/null +++ b/src/common/crypto_rsa.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file crypto_rsa.h + * + * \brief Headers for crypto_rsa.c + **/ + +#ifndef TOR_CRYPTO_RSA_H +#define TOR_CRYPTO_RSA_H + +#include "orconfig.h" + +#include <stdio.h> +#include "torint.h" +#include "testsupport.h" +#include "compat.h" +#include "util.h" +#include "torlog.h" +#include "crypto_curve25519.h" + +/** Length of our public keys. */ +#define PK_BYTES (1024/8) + +/** Constant used to indicate OAEP padding for public-key encryption */ +#define PK_PKCS1_OAEP_PADDING 60002 + +/** Number of bytes added for PKCS1-OAEP padding. */ +#define PKCS1_OAEP_PADDING_OVERHEAD 42 + +/** A public key, or a public/private key-pair. */ +typedef struct crypto_pk_t crypto_pk_t; + +/* RSA enviroment setup */ +MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void)); +void crypto_pk_free_(crypto_pk_t *env); +#define crypto_pk_free(pk) FREE_AND_NULL(crypto_pk_t, crypto_pk_free_, (pk)) +int crypto_get_rsa_padding_overhead(int padding); +int crypto_get_rsa_padding(int padding); + +/* public key crypto */ +MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)); +#define crypto_pk_generate_key(env) \ + crypto_pk_generate_key_with_bits((env), (PK_BYTES*8)) + +int crypto_pk_read_private_key_from_filename(crypto_pk_t *env, + const char *keyfile); +int crypto_pk_write_public_key_to_string(crypto_pk_t *env, + char **dest, size_t *len); +int crypto_pk_write_private_key_to_string(crypto_pk_t *env, + char **dest, size_t *len); +int crypto_pk_read_public_key_from_string(crypto_pk_t *env, + const char *src, size_t len); +int crypto_pk_read_private_key_from_string(crypto_pk_t *env, + const char *s, ssize_t len); +int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, + const char *fname); + +int crypto_pk_check_key(crypto_pk_t *env); +int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); +int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); +size_t crypto_pk_keysize(const crypto_pk_t *env); +int crypto_pk_num_bits(crypto_pk_t *env); +crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); +crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); +int crypto_pk_key_is_private(const crypto_pk_t *key); +int crypto_pk_public_exponent_ok(crypto_pk_t *env); +int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen, int padding); +int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen, + int padding, int warnOnFailure); +MOCK_DECL(int, crypto_pk_public_checksig,(const crypto_pk_t *env, + char *to, size_t tolen, + const char *from, size_t fromlen)); +int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, + const char *from, size_t fromlen); +int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len); +crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); +int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); +int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); + +int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out); +crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len); + +/* Prototypes for private functions only used by tortls.c, crypto.c, and the + * unit tests. */ +struct rsa_st; +struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env); +crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); +MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env, + int private)); +struct evp_pkey_st; + +#ifdef TOR_UNIT_TESTS +void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); +#endif + +#endif + diff --git a/src/common/di_ops.c b/src/common/di_ops.c index 7c0b4e7630..90e9357c8e 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -148,7 +148,7 @@ struct di_digest256_map_t { /** Release all storage held in <b>map</b>, calling free_fn on each value * as we go. */ void -dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn) +dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn) { while (map) { di_digest256_map_t *victim = map; diff --git a/src/common/di_ops.h b/src/common/di_ops.h index e79973ba52..67d9c9f0df 100644 --- a/src/common/di_ops.h +++ b/src/common/di_ops.h @@ -37,7 +37,12 @@ int safe_mem_is_zero(const void *mem, size_t sz); typedef struct di_digest256_map_t di_digest256_map_t; typedef void (*dimap_free_fn)(void *); -void dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn); +void dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn); +#define dimap_free(map, free_fn) \ + do { \ + dimap_free_((map), (free_fn)); \ + (map) = NULL; \ + } while (0) void dimap_add_entry(di_digest256_map_t **map, const uint8_t *key, void *val); void *dimap_search(const di_digest256_map_t *map, const uint8_t *key, diff --git a/src/common/handles.h b/src/common/handles.h index a610753a1c..aef8cd89ef 100644 --- a/src/common/handles.h +++ b/src/common/handles.h @@ -59,7 +59,7 @@ #define HANDLE_DECL(name, structname, linkage) \ typedef struct name ## _handle_t name ## _handle_t; \ linkage name ## _handle_t *name ## _handle_new(struct structname *object); \ - linkage void name ## _handle_free(name ## _handle_t *); \ + linkage void name ## _handle_free_(name ## _handle_t *); \ linkage struct structname *name ## _handle_get(name ## _handle_t *); \ linkage void name ## _handles_clear(struct structname *object); @@ -113,7 +113,7 @@ } \ \ linkage void \ - name ## _handle_free(struct name ## _handle_t *ref) \ + name ## _handle_free_(struct name ## _handle_t *ref) \ { \ if (! ref) return; \ name ## _handle_head_t *head = ref->head; \ diff --git a/src/common/include.am b/src/common/include.am index 715ec0264c..6945285108 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -102,11 +102,6 @@ LIBOR_A_SRC = \ $(threads_impl_source) \ $(readpassphrase_source) -if USE_RUST -else -LIBOR_A_SRC += src/common/compat_rust.c -endif - src/common/src_common_libor_testing_a-log.$(OBJEXT) \ src/common/log.$(OBJEXT): micro-revision.i @@ -119,6 +114,8 @@ LIBOR_CRYPTO_A_SRC = \ src/common/compress_zlib.c \ src/common/compress_zstd.c \ src/common/crypto.c \ + src/common/crypto_rsa.c \ + src/common/crypto_openssl_mgt.c \ src/common/crypto_pwbox.c \ src/common/crypto_s2k.c \ src/common/crypto_format.c \ @@ -158,7 +155,6 @@ COMMONHEADERS = \ src/common/compat.h \ src/common/compat_libevent.h \ src/common/compat_openssl.h \ - src/common/compat_rust.h \ src/common/compat_threads.h \ src/common/compat_time.h \ src/common/compress.h \ @@ -172,6 +168,8 @@ COMMONHEADERS = \ src/common/crypto_curve25519.h \ src/common/crypto_ed25519.h \ src/common/crypto_format.h \ + src/common/crypto_openssl_mgt.h \ + src/common/crypto_rsa.h \ src/common/crypto_pwbox.h \ src/common/crypto_s2k.h \ src/common/di_ops.h \ diff --git a/src/common/log.c b/src/common/log.c index e4d5cd8fd8..d59e5a4036 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -35,6 +35,9 @@ #define LOG_PRIVATE #include "torlog.h" #include "container.h" +#ifdef HAVE_ANDROID_LOG_H +#include <android/log.h> +#endif // HAVE_ANDROID_LOG_H. /** Given a severity, yields an index into log_severity_list_t.masks to use * for that severity. */ @@ -58,12 +61,16 @@ typedef struct logfile_t { int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */ int is_temporary; /**< Boolean: close after initializing logging subsystem.*/ int is_syslog; /**< Boolean: send messages to syslog. */ + int is_android; /**< Boolean: send messages to Android's log subsystem. */ + char *android_tag; /**< Identity Tag used in Android's log subsystem. */ log_callback callback; /**< If not NULL, send messages to this function. */ log_severity_list_t *severities; /**< Which severity of messages should we * log for each log domain? */ } logfile_t; -static void log_free(logfile_t *victim); +static void log_free_(logfile_t *victim); +#define log_free(lg) \ + FREE_AND_NULL(logfile_t, log_free_, (lg)) /** Helper: map a log severity to descriptive string. */ static inline const char * @@ -101,6 +108,33 @@ should_log_function_name(log_domain_mask_t domain, int severity) } } +#ifdef HAVE_ANDROID_LOG_H +/** Helper function to convert Tor's log severity into the matching + * Android log priority. + */ +static int +severity_to_android_log_priority(int severity) +{ + switch (severity) { + case LOG_DEBUG: + return ANDROID_LOG_VERBOSE; + case LOG_INFO: + return ANDROID_LOG_DEBUG; + case LOG_NOTICE: + return ANDROID_LOG_INFO; + case LOG_WARN: + return ANDROID_LOG_WARN; + case LOG_ERR: + return ANDROID_LOG_ERROR; + default: + // LCOV_EXCL_START + raw_assert(0); + return 0; + // LCOV_EXCL_STOP + } +} +#endif // HAVE_ANDROID_LOG_H. + /** A mutex to guard changes to logfiles and logging. */ static tor_mutex_t log_mutex; /** True iff we have initialized log_mutex */ @@ -385,9 +419,12 @@ pending_log_message_new(int severity, log_domain_mask_t domain, return m; } +#define pending_log_message_free(msg) \ + FREE_AND_NULL(pending_log_message_t, pending_log_message_free_, (msg)) + /** Release all storage held by <b>msg</b>. */ static void -pending_log_message_free(pending_log_message_t *msg) +pending_log_message_free_(pending_log_message_t *msg) { if (!msg) return; @@ -396,6 +433,16 @@ pending_log_message_free(pending_log_message_t *msg) tor_free(msg); } +/** Helper function: returns true iff the log file, given in <b>lf</b>, is + * handled externally via the system log API, the Android logging API, or is an + * external callback function. */ +static inline int +logfile_is_external(const logfile_t *lf) +{ + raw_assert(lf); + return lf->is_syslog || lf->is_android || lf->callback; +} + /** Return true iff <b>lf</b> would like to receive a message with the * specified <b>severity</b> in the specified <b>domain</b>. */ @@ -406,7 +453,7 @@ logfile_wants_message(const logfile_t *lf, int severity, if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) { return 0; } - if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) { + if (! (lf->fd >= 0 || logfile_is_external(lf))) { return 0; } if (lf->seems_dead) { @@ -449,6 +496,11 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len, syslog(severity, "%s", msg_after_prefix); #endif /* defined(MAXLINE) */ #endif /* defined(HAVE_SYSLOG_H) */ + } else if (lf->is_android) { +#ifdef HAVE_ANDROID_LOG_H + int priority = severity_to_android_log_priority(severity); + __android_log_write(priority, lf->android_tag, msg_after_prefix); +#endif // HAVE_ANDROID_LOG_H. } else if (lf->callback) { if (domain & LD_NOCB) { if (!*callbacks_deferred && pending_cb_messages) { @@ -641,8 +693,8 @@ tor_log_update_sigsafe_err_fds(void) /* Don't try callback to the control port, or syslogs: We can't * do them from a signal handler. Don't try stdout: we always do stderr. */ - if (lf->is_temporary || lf->is_syslog || - lf->callback || lf->seems_dead || lf->fd < 0) + if (lf->is_temporary || logfile_is_external(lf) + || lf->seems_dead || lf->fd < 0) continue; if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] & (LD_BUG|LD_GENERAL)) { @@ -678,7 +730,7 @@ tor_log_get_logfile_names(smartlist_t *out) LOCK_LOGS(); for (lf = logfiles; lf; lf = lf->next) { - if (lf->is_temporary || lf->is_syslog || lf->callback) + if (lf->is_temporary || logfile_is_external(lf)) continue; if (lf->filename == NULL) continue; @@ -721,12 +773,13 @@ log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, /** Free all storage held by <b>victim</b>. */ static void -log_free(logfile_t *victim) +log_free_(logfile_t *victim) { if (!victim) return; tor_free(victim->severities); tor_free(victim->filename); + tor_free(victim->android_tag); tor_free(victim); } @@ -1146,6 +1199,39 @@ add_syslog_log(const log_severity_list_t *severity, } #endif /* defined(HAVE_SYSLOG_H) */ +#ifdef HAVE_ANDROID_LOG_H +/** + * Add a log handler to send messages to the Android platform log facility. + */ +int +add_android_log(const log_severity_list_t *severity, + const char *android_tag) +{ + logfile_t *lf = NULL; + + lf = tor_malloc_zero(sizeof(logfile_t)); + lf->fd = -1; + lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); + lf->filename = tor_strdup("<android>"); + lf->is_android = 1; + + if (android_tag == NULL) + lf->android_tag = tor_strdup("Tor"); + else { + char buf[256]; + tor_snprintf(buf, sizeof(buf), "Tor-%s", android_tag); + lf->android_tag = tor_strdup(buf); + } + + LOCK_LOGS(); + lf->next = logfiles; + logfiles = lf; + log_global_min_severity_ = get_min_log_level(); + UNLOCK_LOGS(); + return 0; +} +#endif // HAVE_ANDROID_LOG_H. + /** If <b>level</b> is a valid log severity, return the corresponding * numeric value. Otherwise, return -1. */ int @@ -1313,7 +1399,8 @@ parse_log_severity_config(const char **cfg_ptr, if (!strcasecmpstart(cfg, "file") || !strcasecmpstart(cfg, "stderr") || !strcasecmpstart(cfg, "stdout") || - !strcasecmpstart(cfg, "syslog")) { + !strcasecmpstart(cfg, "syslog") || + !strcasecmpstart(cfg, "android")) { goto done; } if (got_an_unqualified_range > 1) diff --git a/src/common/memarea.c b/src/common/memarea.c index b059987e0e..68c1625fe4 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -153,7 +153,7 @@ memarea_new(void) /** Free <b>area</b>, invalidating all pointers returned from memarea_alloc() * and friends for this area */ void -memarea_drop_all(memarea_t *area) +memarea_drop_all_(memarea_t *area) { memarea_chunk_t *chunk, *next; for (chunk = area->first; chunk; chunk = next) { @@ -322,7 +322,7 @@ memarea_new(void) return ma; } void -memarea_drop_all(memarea_t *area) +memarea_drop_all_(memarea_t *area) { memarea_clear(area); smartlist_free(area->pieces); diff --git a/src/common/memarea.h b/src/common/memarea.h index c3d954e1ce..5207e8a5bd 100644 --- a/src/common/memarea.h +++ b/src/common/memarea.h @@ -8,7 +8,12 @@ typedef struct memarea_t memarea_t; memarea_t *memarea_new(void); -void memarea_drop_all(memarea_t *area); +void memarea_drop_all_(memarea_t *area); +#define memarea_drop_all(area) \ + do { \ + memarea_drop_all_(area); \ + (area) = NULL; \ + } while (0) void memarea_clear(memarea_t *area); int memarea_owns_ptr(const memarea_t *area, const void *ptr); void *memarea_alloc(memarea_t *area, size_t sz); diff --git a/src/common/procmon.c b/src/common/procmon.c index 26c11823e8..abcbbeaa21 100644 --- a/src/common/procmon.c +++ b/src/common/procmon.c @@ -325,7 +325,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, /** Free the process-termination monitor <b>procmon</b>. */ void -tor_process_monitor_free(tor_process_monitor_t *procmon) +tor_process_monitor_free_(tor_process_monitor_t *procmon) { if (procmon == NULL) return; diff --git a/src/common/procmon.h b/src/common/procmon.h index 10ead11ba8..63777e4111 100644 --- a/src/common/procmon.h +++ b/src/common/procmon.h @@ -27,7 +27,9 @@ tor_process_monitor_t *tor_process_monitor_new(struct event_base *base, tor_procmon_callback_t cb, void *cb_arg, const char **msg); -void tor_process_monitor_free(tor_process_monitor_t *procmon); +void tor_process_monitor_free_(tor_process_monitor_t *procmon); +#define tor_process_monitor_free(procmon) \ + FREE_AND_NULL(tor_process_monitor_t, tor_process_monitor_free_, (procmon)) #endif /* !defined(TOR_PROCMON_H) */ diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 8827370c24..37f582048c 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -162,6 +162,7 @@ static int filter_nopar_gen[] = { SCMP_SYS(fstat64), #endif SCMP_SYS(futex), + SCMP_SYS(getdents), SCMP_SYS(getdents64), SCMP_SYS(getegid), #ifdef __NR_getegid32 @@ -433,9 +434,9 @@ libc_uses_openat_for_everything(void) return 1; else return 0; -#else +#else /* !(defined(CHECK_LIBC_VERSION)) */ return 0; -#endif +#endif /* defined(CHECK_LIBC_VERSION) */ } /** Allow a single file to be opened. If <b>use_openat</b> is true, @@ -1130,7 +1131,7 @@ sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter) SCMP_CMP(1, SCMP_CMP_EQ, 0)); #else return 0; -#endif +#endif /* defined(__NR_kill) */ } /** @@ -1500,8 +1501,12 @@ cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, return (a->family == b->family) && 0 == strcmp(a->name, b->name); } +#define cached_getaddrinfo_item_free(item) \ + FREE_AND_NULL(cached_getaddrinfo_item_t, \ + cached_getaddrinfo_item_free_, (item)) + static void -cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item) +cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item) { if (item == NULL) return; @@ -1812,7 +1817,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context) #endif #if defined(DEBUGGING_CLOSE) - _exit(1); + _exit(1); // exit ok: programming error has led to sandbox failure. #endif // DEBUGGING_CLOSE } diff --git a/src/common/storagedir.c b/src/common/storagedir.c index c471ea911f..e2c7b4bb87 100644 --- a/src/common/storagedir.c +++ b/src/common/storagedir.c @@ -59,7 +59,7 @@ storage_dir_new(const char *dirname, int max_files) * Drop all in-RAM storage for <b>d</b>. Does not delete any files. */ void -storage_dir_free(storage_dir_t *d) +storage_dir_free_(storage_dir_t *d) { if (d == NULL) return; diff --git a/src/common/storagedir.h b/src/common/storagedir.h index 3de0afc361..d99bd7ec52 100644 --- a/src/common/storagedir.h +++ b/src/common/storagedir.h @@ -9,7 +9,10 @@ struct config_line_t; struct sandbox_cfg_elem; storage_dir_t * storage_dir_new(const char *dirname, int n_files); -void storage_dir_free(storage_dir_t *d); +void storage_dir_free_(storage_dir_t *d); +#define storage_dir_free(d) \ + FREE_AND_NULL(storage_dir_t, storage_dir_free_, (d)) + int storage_dir_register_with_sandbox(storage_dir_t *d, struct sandbox_cfg_elem **cfg); const smartlist_t *storage_dir_list(storage_dir_t *d); diff --git a/src/common/timers.c b/src/common/timers.c index c8e09414f4..552080b11e 100644 --- a/src/common/timers.c +++ b/src/common/timers.c @@ -63,7 +63,7 @@ struct timeout_cb { #define TIMEOUT_CB_OVERRIDE /* We're going to support timers that are pretty far out in advance. Making * this big can be inefficient, but having a significant number of timers - * above TIMEOUT_MAX can also be super-inefficent. Choosing 5 here sets + * above TIMEOUT_MAX can also be super-inefficient. Choosing 5 here sets * timeout_max to 2^30 ticks, or 29 hours with our value for USEC_PER_TICK */ #define WHEEL_NUM 5 #include "src/ext/timeouts/timeout.c" @@ -245,7 +245,7 @@ timer_new(timer_cb_fn_t cb, void *arg) * scheduled. */ void -timer_free(tor_timer_t *t) +timer_free_(tor_timer_t *t) { if (! t) return; diff --git a/src/common/timers.h b/src/common/timers.h index d4d4fb00a9..6d27f3e01e 100644 --- a/src/common/timers.h +++ b/src/common/timers.h @@ -17,7 +17,8 @@ void timer_get_cb(const tor_timer_t *t, timer_cb_fn_t *cb_out, void **arg_out); void timer_schedule(tor_timer_t *t, const struct timeval *delay); void timer_disable(tor_timer_t *t); -void timer_free(tor_timer_t *t); +void timer_free_(tor_timer_t *t); +#define timer_free(t) FREE_AND_NULL(tor_timer_t, timer_free_, (t)) void timers_initialize(void); void timers_shutdown(void); diff --git a/src/common/torint.h b/src/common/torint.h index bc81c114f8..0b8061d24f 100644 --- a/src/common/torint.h +++ b/src/common/torint.h @@ -348,6 +348,16 @@ typedef uint32_t uintptr_t; #endif /* (SIZEOF_SIZE_T == 4) || ... */ #endif /* !defined(SIZE_MAX) */ +#ifdef _WIN32 +# ifdef _WIN64 +# define TOR_PRIuSZ PRIu64 +# else +# define TOR_PRIuSZ PRIu32 +# endif +#else +# define TOR_PRIuSZ "zu" +#endif + #ifndef SSIZE_MAX #if (SIZEOF_SIZE_T == 4) #define SSIZE_MAX INT32_MAX diff --git a/src/common/torlog.h b/src/common/torlog.h index be24b2b908..cadfe3b879 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -148,7 +148,11 @@ int add_file_log(const log_severity_list_t *severity, const char *filename, #ifdef HAVE_SYSLOG_H int add_syslog_log(const log_severity_list_t *severity, const char* syslog_identity_tag); -#endif +#endif // HAVE_SYSLOG_H. +#ifdef HAVE_ANDROID_LOG_H +int add_android_log(const log_severity_list_t *severity, + const char *android_identity_tag); +#endif // HAVE_ANDROID_LOG_H. int add_callback_log(const log_severity_list_t *severity, log_callback cb); void logs_set_domain_logging(int enabled); int get_min_log_level(void); diff --git a/src/common/tortls.c b/src/common/tortls.c index e8c51879bd..50609b8ac7 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -669,7 +669,7 @@ static const char CLIENT_CIPHER_LIST[] = /** Free all storage held in <b>cert</b> */ void -tor_x509_cert_free(tor_x509_cert_t *cert) +tor_x509_cert_free_(tor_x509_cert_t *cert) { if (! cert) return; @@ -1817,7 +1817,7 @@ tor_tls_is_server(tor_tls_t *tls) * underlying file descriptor. */ void -tor_tls_free(tor_tls_t *tls) +tor_tls_free_(tor_tls_t *tls) { if (!tls) return; @@ -1964,7 +1964,7 @@ tor_tls_handshake(tor_tls_t *tls) return r; } -/** Perform the final part of the intial TLS handshake on <b>tls</b>. This +/** Perform the final part of the initial TLS handshake on <b>tls</b>. This * should be called for the first handshake only: it determines whether the v1 * or the v2 handshake was used, and adjusts things for the renegotiation * handshake as appropriate. diff --git a/src/common/tortls.h b/src/common/tortls.h index 6145f7dbc9..1dbf0b332f 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -216,7 +216,8 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), void *arg); int tor_tls_is_server(tor_tls_t *tls); -void tor_tls_free(tor_tls_t *tls); +void tor_tls_free_(tor_tls_t *tls); +#define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls)) int tor_tls_peer_has_cert(tor_tls_t *tls); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); @@ -263,7 +264,9 @@ void check_no_tls_errors_(const char *fname, int line); void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing); -void tor_x509_cert_free(tor_x509_cert_t *cert); +void tor_x509_cert_free_(tor_x509_cert_t *cert); +#define tor_x509_cert_free(c) \ + FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len); void tor_x509_cert_get_der(const tor_x509_cert_t *cert, diff --git a/src/common/util.c b/src/common/util.c index 5ff7e104d6..90204befc0 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -156,7 +156,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS) /* If these functions die within a worker process, they won't call * spawn_exit, but that's ok, since the parent will run out of memory soon * anyway. */ - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return result; @@ -244,7 +244,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS) if (PREDICT_UNLIKELY(result == NULL)) { /* LCOV_EXCL_START */ log_err(LD_MM,"Out of memory on realloc(). Dying."); - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return result; @@ -282,7 +282,7 @@ tor_strdup_(const char *s DMALLOC_PARAMS) if (PREDICT_UNLIKELY(duplicate == NULL)) { /* LCOV_EXCL_START */ log_err(LD_MM,"Out of memory on strdup(). Dying."); - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return duplicate; @@ -3047,7 +3047,7 @@ unescape_string(const char *s, char **result, size_t *size_out) /** Removes enclosing quotes from <b>path</b> and unescapes quotes between the * enclosing quotes. Backslashes are not unescaped. Return the unquoted - * <b>path</b> on sucess or 0 if <b>path</b> is not quoted correctly. */ + * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */ char * get_unquoted_path(const char *path) { @@ -3590,14 +3590,14 @@ start_daemon(void) if (pipe(daemon_filedes)) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno)); - exit(1); + exit(1); // exit ok: during daemonize, pipe failed. /* LCOV_EXCL_STOP */ } pid = fork(); if (pid < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"fork failed. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, fork failed /* LCOV_EXCL_STOP */ } if (pid) { /* Parent */ @@ -3612,9 +3612,9 @@ start_daemon(void) } fflush(stdout); if (ok == 1) - exit(0); + exit(0); // exit ok: during daemonize, daemonizing. else - exit(1); /* child reported error */ + exit(1); /* child reported error. exit ok: daemonize failed. */ } else { /* Child */ close(daemon_filedes[0]); /* we only write */ @@ -3626,7 +3626,7 @@ start_daemon(void) * _Advanced Programming in the Unix Environment_. */ if (fork() != 0) { - exit(0); + exit(0); // exit ok: during daemonize, fork failed (2) } set_main_thread(); /* We are now the main thread. */ @@ -3655,14 +3655,14 @@ finish_daemon(const char *desired_cwd) /* Don't hold the wrong FS mounted */ if (chdir(desired_cwd) < 0) { log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd); - exit(1); + exit(1); // exit ok: during daemonize, chdir failed. } nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0); if (nullfd < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"/dev/null can't be opened. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, couldn't open /dev/null /* LCOV_EXCL_STOP */ } /* close fds linking to invoking terminal, but @@ -3674,7 +3674,7 @@ finish_daemon(const char *desired_cwd) dup2(nullfd,2) < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"dup2 failed. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, dup2 failed. /* LCOV_EXCL_STOP */ } if (nullfd > 2) @@ -3898,7 +3898,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len, * call it with a signed int and an unsigned char, and since the C standard * does not guarantee that an int is wider than a char (an int must be at * least 16 bits but it is permitted for a char to be that wide as well), we - * can't assume a signed int is sufficient to accomodate an unsigned char. + * can't assume a signed int is sufficient to accommodate an unsigned char. * Thus, format_helper_exit_status() will still need to emit any require '-' * on its own. * @@ -3928,7 +3928,7 @@ format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len) * * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded * with spaces. CHILD_STATE indicates where - * in the processs of starting the child process did the failure occur (see + * in the process of starting the child process did the failure occur (see * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of * errno when the failure occurred. * @@ -4474,7 +4474,7 @@ tor_spawn_background(const char *const filename, const char **argv, err += (nbytes < 0); } - _exit(err?254:255); + _exit(err?254:255); // exit ok: in child. } /* Never reached, but avoids compiler warning */ @@ -4713,7 +4713,7 @@ environment_variable_names_equal(const char *s1, const char *s2) /** Free <b>env</b> (assuming it was produced by * process_environment_make). */ void -process_environment_free(process_environment_t *env) +process_environment_free_(process_environment_t *env) { if (env == NULL) return; diff --git a/src/common/util.h b/src/common/util.h index 6bc853da26..2ee0ea28cd 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -79,13 +79,28 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, * * This is a macro. If you need a function pointer to release memory from * tor_malloc(), use tor_free_(). + * + * Note that this macro takes the address of the pointer it is going to + * free and clear. If that pointer is stored with a nonstandard + * alignment (eg because of a "packed" pragma) it is not correct to use + * tor_free(). */ +#ifdef __GNUC__ +#define tor_free(p) STMT_BEGIN \ + typeof(&(p)) tor_free__tmpvar = &(p); \ + if (PREDICT_LIKELY((*tor_free__tmpvar)!=NULL)) { \ + raw_free(*tor_free__tmpvar); \ + *tor_free__tmpvar=NULL; \ + } \ + STMT_END +#else #define tor_free(p) STMT_BEGIN \ if (PREDICT_LIKELY((p)!=NULL)) { \ raw_free(p); \ (p)=NULL; \ } \ STMT_END +#endif #endif /* defined(USE_DMALLOC) */ #define tor_malloc(size) tor_malloc_(size DMALLOC_ARGS) @@ -109,6 +124,17 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, void tor_log_mallinfo(int severity); +/* Helper macro: free a variable of type 'typename' using freefn, and + * set the variable to NULL. + */ +#define FREE_AND_NULL(typename, freefn, var) \ + do { \ + /* only evaluate (var) once. */ \ + typename **tmp__free__ptr ## freefn = &(var); \ + freefn(*tmp__free__ptr ## freefn); \ + (*tmp__free__ptr ## freefn) = NULL; \ + } while (0) + /** Macro: yield a pointer to the field at position <b>off</b> within the * structure <b>st</b>. Example: * <pre> @@ -423,7 +449,9 @@ struct process_environment_t { }; process_environment_t *process_environment_make(struct smartlist_t *env_vars); -void process_environment_free(process_environment_t *env); +void process_environment_free_(process_environment_t *env); +#define process_environment_free(env) \ + FREE_AND_NULL(process_environment_t, process_environment_free_, (env)) struct smartlist_t *get_current_process_environment_variables(void); diff --git a/src/common/workqueue.c b/src/common/workqueue.c index 42723224d3..ec96959b7d 100644 --- a/src/common/workqueue.c +++ b/src/common/workqueue.c @@ -148,12 +148,15 @@ workqueue_entry_new(workqueue_reply_t (*fn)(void*, void*), return ent; } +#define workqueue_entry_free(ent) \ + FREE_AND_NULL(workqueue_entry_t, workqueue_entry_free_, (ent)) + /** * Release all storage held in <b>ent</b>. Call only when <b>ent</b> is not on * any queue. */ static void -workqueue_entry_free(workqueue_entry_t *ent) +workqueue_entry_free_(workqueue_entry_t *ent) { if (!ent) return; diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c index 508e4f6ceb..0427c87950 100644 --- a/src/ext/csiphash.c +++ b/src/ext/csiphash.c @@ -123,3 +123,9 @@ void siphash_set_global_key(const struct sipkey *key) the_siphash_key.k1 = key->k1; the_siphash_key_is_set = 1; } + +void siphash_unset_global_key(void) +{ + the_siphash_key_is_set = 0; + memset(&the_siphash_key, 0, sizeof(the_siphash_key)); +} diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c index 44ec562f02..84fc3850a2 100644 --- a/src/ext/ed25519/donna/ed25519_tor.c +++ b/src/ext/ed25519/donna/ed25519_tor.c @@ -132,7 +132,7 @@ ED25519_FN(curved25519_scalarmult_basepoint) (curved25519_key pk, const curved25 } /* - Tor has a specific idea of how an Ed25519 implementaion should behave. + Tor has a specific idea of how an Ed25519 implementation should behave. Implement such a beast using the ed25519-donna primitives/internals. * Private key generation using Tor's CSPRNG. diff --git a/src/ext/siphash.h b/src/ext/siphash.h index d9b34b8980..730e49937d 100644 --- a/src/ext/siphash.h +++ b/src/ext/siphash.h @@ -9,5 +9,6 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k void siphash_set_global_key(const struct sipkey *key); uint64_t siphash24g(const void *src, unsigned long src_sz); +void siphash_unset_global_key(void); #endif diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 7e92633601..96ce275578 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -90,34 +90,47 @@ addressmap_init(void) virtaddress_reversemap = strmap_new(); } +#define addressmap_ent_free(ent) \ + FREE_AND_NULL(addressmap_entry_t, addressmap_ent_free_, (ent)) + /** Free the memory associated with the addressmap entry <b>_ent</b>. */ static void -addressmap_ent_free(void *_ent) +addressmap_ent_free_(addressmap_entry_t *ent) { - addressmap_entry_t *ent; - if (!_ent) + if (!ent) return; - ent = _ent; tor_free(ent->new_address); tor_free(ent); } +static void +addressmap_ent_free_void(void *ent) +{ + addressmap_ent_free_(ent); +} + +#define addressmap_virtaddress_ent_free(ent) \ + FREE_AND_NULL(virtaddress_entry_t, addressmap_virtaddress_ent_free_, (ent)) + /** Free storage held by a virtaddress_entry_t* entry in <b>_ent</b>. */ static void -addressmap_virtaddress_ent_free(void *_ent) +addressmap_virtaddress_ent_free_(virtaddress_entry_t *ent) { - virtaddress_entry_t *ent; - if (!_ent) + if (!ent) return; - - ent = _ent; tor_free(ent->ipv4_address); tor_free(ent->ipv6_address); tor_free(ent->hostname_address); tor_free(ent); } +static void +addressmap_virtaddress_ent_free_void(void *ent) +{ + addressmap_virtaddress_ent_free_(ent); +} + /** Remove <b>address</b> (which must map to <b>ent</b>) from the * virtual address map. */ static void @@ -311,10 +324,10 @@ addressmap_clean(time_t now) void addressmap_free_all(void) { - strmap_free(addressmap, addressmap_ent_free); + strmap_free(addressmap, addressmap_ent_free_void); addressmap = NULL; - strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free); + strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free_void); virtaddress_reversemap = NULL; } @@ -541,7 +554,7 @@ addressmap_have_mapping(const char *address, int update_expiry) * (virtual address mapping) from the controller.) * * <b>new_address</b> should be a newly dup'ed string, which we'll use or - * free as appropriate. We will leave address alone. + * free as appropriate. We will leave <b>address</b> alone. * * If <b>wildcard_addr</b> is true, then the mapping will match any address * equal to <b>address</b>, or any address ending with a period followed by @@ -554,7 +567,6 @@ addressmap_have_mapping(const char *address, int update_expiry) * <b>wildcard_new_addr</b>, remove any mappings that exist from * <b>address</b>. * - * * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is * not set. */ void diff --git a/src/or/bridges.c b/src/or/bridges.c index 0b1bbbd158..d88d6c6434 100644 --- a/src/or/bridges.c +++ b/src/or/bridges.c @@ -53,7 +53,10 @@ struct bridge_info_t { smartlist_t *socks_args; }; -static void bridge_free(bridge_info_t *bridge); +#define bridge_free(bridge) \ + FREE_AND_NULL(bridge_info_t, bridge_free_, (bridge)) + +static void bridge_free_(bridge_info_t *bridge); static void rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node); @@ -101,7 +104,7 @@ clear_bridge_list(void) /** Free the bridge <b>bridge</b>. */ static void -bridge_free(bridge_info_t *bridge) +bridge_free_(bridge_info_t *bridge) { if (!bridge) return; @@ -353,7 +356,7 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, { /* Iterate the already-registered bridge list: - If you find a bridge with the same adress and port, mark it for + If you find a bridge with the same address and port, mark it for removal. It doesn't make sense to have two active bridges with the same IP:PORT. If the bridge in question has a different digest or transport than <b>digest</b>/<b>transport_name</b>, @@ -716,7 +719,6 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) if (node->ri) { routerinfo_t *ri = node->ri; tor_addr_from_ipv4h(&addr, ri->addr); - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && bridge->port == ri->or_port) || (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && @@ -774,16 +776,58 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) routerstatus_t *rs = node->rs; tor_addr_from_ipv4h(&addr, rs->addr); - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == rs->or_port) { + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == rs->or_port) || + (!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) && + bridge->port == rs->ipv6_orport)) { /* they match, so no need to do anything */ } else { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerstatus for '%s' to match " - "configured address %s.", - rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + if (tor_addr_family(&bridge->addr) == AF_INET) { + rs->addr = tor_addr_to_ipv4h(&bridge->addr); + rs->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match " + "configured address %s.", + rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + /* set IPv6 preferences even if there is no ri */ + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&rs->ipv6_addr, &bridge->addr); + rs->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match configured" + " address %s.", + rs->nickname, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + if (options->ClientPreferIPv6ORPort == -1) { + /* Mark which address to use based on which bridge_t we got. */ + node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && + !tor_addr_is_null(&node->rs->ipv6_addr)); + } else { + /* Mark which address to use based on user preference */ + node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && + !tor_addr_is_null(&node->rs->ipv6_addr)); + } + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&rs->ipv6_addr)) { + tor_addr_port_t ap; + node_get_pref_orport(node, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s) based on %s.", + rs->nickname, + node->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addrport(&ap.addr, ap.port), + options->ClientPreferIPv6ORPort == -1 ? + "the configured Bridge address" : + "ClientPreferIPv6ORPort"); } } } diff --git a/src/or/channel.c b/src/or/channel.c index 3c0025aff6..ff1cfde2ad 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1,3 +1,4 @@ + /* * Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -5,9 +6,8 @@ * \file channel.c * * \brief OR/OP-to-OR channel abstraction layer. A channel's job is to - * transfer cells from Tor instance to Tor instance. - * Currently, there is only one implementation of the channel abstraction: in - * channeltls.c. + * transfer cells from Tor instance to Tor instance. Currently, there is only + * one implementation of the channel abstraction: in channeltls.c. * * Channels are a higher-level abstraction than or_connection_t: In general, * any means that two Tor relays use to exchange cells, or any means that a @@ -24,23 +24,34 @@ * connection. * * Every channel implementation is responsible for being able to transmit - * cells that are added to it with channel_write_cell() and related functions, - * and to receive incoming cells with the channel_queue_cell() and related - * functions. See the channel_t documentation for more information. - * - * When new cells arrive on a channel, they are passed to cell handler - * functions, which can be set by channel_set_cell_handlers() - * functions. (Tor's cell handlers are in command.c.) - * - * Tor flushes cells to channels from relay.c in - * channel_flush_from_first_active_circuit(). + * cells that are passed to it + * + * For *inbound* cells, the entry point is: channel_process_cell(). It takes a + * cell and will pass it to the cell handler set by + * channel_set_cell_handlers(). Currently, this is passed back to the command + * subsystem which is command_process_cell(). + * + * NOTE: For now, the separation between channels and specialized channels + * (like channeltls) is not that well defined. So the channeltls layer calls + * channel_process_cell() which originally comes from the connection subsytem. + * This should be hopefully be fixed with #23993. + * + * For *outbound* cells, the entry point is: channel_write_packed_cell(). + * Only packed cells are dequeued from the circuit queue by the scheduler + * which uses channel_flush_from_first_active_circuit() to decide which cells + * to flush from which circuit on the channel. They are then passed down to + * the channel subsystem. This calls the low layer with the function pointer + * .write_packed_cell(). + * + * Each specialized channel (currently only channeltls_t) MUST implement a + * series of function found in channel_t. See channel.h for more + * documentation. **/ /* * Define this so channel.h gives us things only channel_t subclasses * should touch. */ - #define TOR_CHANNEL_INTERNAL_ /* This one's for stuff only channel.c and the test suite should see */ @@ -112,59 +123,6 @@ HANDLE_IMPL(channel, channel_s,) /* Counter for ID numbers */ static uint64_t n_channels_allocated = 0; -/* - * Channel global byte/cell counters, for statistics and for scheduler high - * /low-water marks. - */ - -/* - * Total number of cells ever given to any channel with the - * channel_write_*_cell() functions. - */ - -static uint64_t n_channel_cells_queued = 0; - -/* - * Total number of cells ever passed to a channel lower layer with the - * write_*_cell() methods. - */ - -static uint64_t n_channel_cells_passed_to_lower_layer = 0; - -/* - * Current number of cells in all channel queues; should be - * n_channel_cells_queued - n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_cells_in_queues = 0; - -/* - * Total number of bytes for all cells ever queued to a channel and - * counted in n_channel_cells_queued. - */ - -static uint64_t n_channel_bytes_queued = 0; - -/* - * Total number of bytes for all cells ever passed to a channel lower layer - * and counted in n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_passed_to_lower_layer = 0; - -/* - * Current number of bytes in all channel queues; should be - * n_channel_bytes_queued - n_channel_bytes_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_in_queues = 0; - -/* - * Current total estimated queue size *including lower layer queues and - * transmit overhead* - */ - -STATIC uint64_t estimated_total_queue_size = 0; /* Digest->channel map * @@ -201,49 +159,23 @@ HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, HT_GENERATE2(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_) -static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q); -#if 0 -static int cell_queue_entry_is_padding(cell_queue_entry_t *q); -#endif -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell); -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell); -static int is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out); - -static void channel_assert_counter_consistency(void); - /* Functions to maintain the digest map */ -static void channel_add_to_digest_map(channel_t *chan); static void channel_remove_from_digest_map(channel_t *chan); -/* - * Flush cells from just the outgoing queue without trying to get them - * from circuits; used internall by channel_flush_some_cells(). - */ -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells); -static void channel_force_free(channel_t *chan); -static void -channel_free_list(smartlist_t *channels, int mark_for_close); -static void -channel_listener_free_list(smartlist_t *channels, int mark_for_close); -static void channel_listener_force_free(channel_listener_t *chan_l); -static size_t channel_get_cell_queue_entry_size(channel_t *chan, - cell_queue_entry_t *q); -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); +static void channel_force_xfree(channel_t *chan); +static void channel_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_force_xfree(channel_listener_t *chan_l); /*********************************** * Channel state utility functions * **********************************/ /** - * Indicate whether a given channel state is valid + * Indicate whether a given channel state is valid. */ - int channel_state_is_valid(channel_state_t state) { @@ -267,9 +199,8 @@ channel_state_is_valid(channel_state_t state) } /** - * Indicate whether a given channel listener state is valid + * Indicate whether a given channel listener state is valid. */ - int channel_listener_state_is_valid(channel_listener_state_t state) { @@ -291,13 +222,12 @@ channel_listener_state_is_valid(channel_listener_state_t state) } /** - * Indicate whether a channel state transition is valid + * Indicate whether a channel state transition is valid. * * This function takes two channel states and indicates whether a * transition between them is permitted (see the state definitions and * transition table in or.h at the channel_state_t typedef). */ - int channel_state_can_transition(channel_state_t from, channel_state_t to) { @@ -338,13 +268,12 @@ channel_state_can_transition(channel_state_t from, channel_state_t to) } /** - * Indicate whether a channel listener state transition is valid + * Indicate whether a channel listener state transition is valid. * * This function takes two channel listener states and indicates whether a * transition between them is permitted (see the state definitions and * transition table in or.h at the channel_listener_state_t typedef). */ - int channel_listener_state_can_transition(channel_listener_state_t from, channel_listener_state_t to) @@ -375,9 +304,8 @@ channel_listener_state_can_transition(channel_listener_state_t from, } /** - * Return a human-readable description for a channel state + * Return a human-readable description for a channel state. */ - const char * channel_state_to_string(channel_state_t state) { @@ -411,9 +339,8 @@ channel_state_to_string(channel_state_t state) } /** - * Return a human-readable description for a channel listenier state + * Return a human-readable description for a channel listener state. */ - const char * channel_listener_state_to_string(channel_listener_state_t state) { @@ -445,12 +372,11 @@ channel_listener_state_to_string(channel_listener_state_t state) ***************************************/ /** - * Register a channel + * Register a channel. * * This function registers a newly created channel in the global lists/maps * of active channels. */ - void channel_register(channel_t *chan) { @@ -503,12 +429,11 @@ channel_register(channel_t *chan) } /** - * Unregister a channel + * Unregister a channel. * * This function removes a channel from the global lists and maps and is used * when freeing a closed/errored channel. */ - void channel_unregister(channel_t *chan) { @@ -543,12 +468,11 @@ channel_unregister(channel_t *chan) } /** - * Register a channel listener + * Register a channel listener. * - * This function registers a newly created channel listner in the global + * This function registers a newly created channel listener in the global * lists/maps of active channel listeners. */ - void channel_listener_register(channel_listener_t *chan_l) { @@ -585,12 +509,11 @@ channel_listener_register(channel_listener_t *chan_l) } /** - * Unregister a channel listener + * Unregister a channel listener. * * This function removes a channel listener from the global lists and maps * and is used when freeing a closed/errored channel listener. */ - void channel_listener_unregister(channel_listener_t *chan_l) { @@ -621,14 +544,13 @@ channel_listener_unregister(channel_listener_t *chan_l) *********************************/ /** - * Add a channel to the digest map + * Add a channel to the digest map. * * This function adds a channel to the digest map and inserts it into the * correct linked list if channels with that remote endpoint identity digest * already exist. */ - -static void +STATIC void channel_add_to_digest_map(channel_t *chan) { channel_idmap_entry_t *ent, search; @@ -660,12 +582,11 @@ channel_add_to_digest_map(channel_t *chan) } /** - * Remove a channel from the digest map + * Remove a channel from the digest map. * * This function removes a channel from the digest map and the linked list of * channels for that digest if more than one exists. */ - static void channel_remove_from_digest_map(channel_t *chan) { @@ -676,33 +597,6 @@ channel_remove_from_digest_map(channel_t *chan) /* Assert that there is a digest */ tor_assert(!tor_digest_is_zero(chan->identity_digest)); -#if 0 - /* Make sure we have a map */ - if (!channel_identity_map) { - /* - * No identity map, so we can't find it by definition. This - * case is similar to digestmap_get() failing below. - */ - log_warn(LD_BUG, - "Trying to remove channel %p (global ID " U64_FORMAT ") " - "with digest %s from identity map, but didn't have any identity " - "map", - chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - /* Clear out its next/prev pointers */ - if (chan->next_with_same_id) { - chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; - } - if (chan->prev_with_same_id) { - chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; - } - chan->next_with_same_id = NULL; - chan->prev_with_same_id = NULL; - - return; - } -#endif /* 0 */ - /* Pull it out of its list, wherever that list is */ TOR_LIST_REMOVE(chan, next_with_same_id); @@ -740,13 +634,12 @@ channel_remove_from_digest_map(channel_t *chan) ***************************/ /** - * Find channel by global ID + * Find channel by global ID. * * This function searches for a channel by the global_identifier assigned * at initialization time. This identifier is unique for the lifetime of the * Tor process. */ - channel_t * channel_find_by_global_id(uint64_t global_identifier) { @@ -784,7 +677,7 @@ channel_remote_identity_matches(const channel_t *chan, } /** - * Find channel by RSA/Ed25519 identity of of the remote endpoint + * Find channel by RSA/Ed25519 identity of of the remote endpoint. * * This function looks up a channel by the digest of its remote endpoint's RSA * identity key. If <b>ed_id</b> is provided and nonzero, only a channel @@ -823,12 +716,11 @@ channel_find_by_remote_identity(const char *rsa_id_digest, } /** - * Get next channel with digest + * Get next channel with digest. * * This function takes a channel and finds the next channel in the list * with the same digest. */ - channel_t * channel_next_with_rsa_identity(channel_t *chan) { @@ -915,13 +807,12 @@ channel_check_for_duplicates(void) } /** - * Initialize a channel + * Initialize a channel. * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the * channel should be allocated with tor_malloc_zero(). */ - void channel_init(channel_t *chan) { @@ -936,10 +827,6 @@ channel_init(channel_t *chan) /* Warn about exhausted circuit IDs no more than hourly. */ chan->last_warned_circ_ids_exhausted.rate = 3600; - /* Initialize queues. */ - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - /* Initialize list entries. */ memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id)); @@ -957,13 +844,12 @@ channel_init(channel_t *chan) } /** - * Initialize a channel listener + * Initialize a channel listener. * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the * channel listener should be allocated with tor_malloc_zero(). */ - void channel_init_listener(channel_listener_t *chan_l) { @@ -980,9 +866,8 @@ channel_init_listener(channel_listener_t *chan_l) * Free a channel; nothing outside of channel.c and subclasses should call * this - it frees channels after they have closed and been unregistered. */ - void -channel_free(channel_t *chan) +channel_free_(channel_t *chan) { if (!chan) return; @@ -1025,8 +910,6 @@ channel_free(channel_t *chan) chan->cmux = NULL; } - /* We're in CLOSED or ERROR, so the cell queue is already empty */ - tor_free(chan); } @@ -1035,9 +918,8 @@ channel_free(channel_t *chan) * should call this - it frees channel listeners after they have closed and * been unregistered. */ - void -channel_listener_free(channel_listener_t *chan_l) +channel_listener_free_(channel_listener_t *chan_l) { if (!chan_l) return; @@ -1055,11 +937,6 @@ channel_listener_free(channel_listener_t *chan_l) /* Call a free method if there is one */ if (chan_l->free_fn) chan_l->free_fn(chan_l); - /* - * We're in CLOSED or ERROR, so the incoming channel queue is already - * empty. - */ - tor_free(chan_l); } @@ -1068,11 +945,9 @@ channel_listener_free(channel_listener_t *chan_l) * use-only function should be called only from channel_free_all() when * shutting down the Tor process. */ - static void -channel_force_free(channel_t *chan) +channel_force_xfree(channel_t *chan) { - cell_queue_entry_t *cell, *cell_tmp; tor_assert(chan); log_debug(LD_CHANNEL, @@ -1106,29 +981,16 @@ channel_force_free(channel_t *chan) chan->cmux = NULL; } - /* We might still have a cell queue; kill it */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - - /* Outgoing cell queue is similar, but we can have to free packed cells */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - tor_free(chan); } /** - * Free a channel listener and skip the state/reigstration asserts; this + * Free a channel listener and skip the state/registration asserts; this * internal-use-only function should be called only from channel_free_all() * when shutting down the Tor process. */ - static void -channel_listener_force_free(channel_listener_t *chan_l) +channel_listener_force_xfree(channel_listener_t *chan_l) { tor_assert(chan_l); @@ -1159,30 +1021,11 @@ channel_listener_force_free(channel_listener_t *chan_l) } /** - * Return the current registered listener for a channel listener - * - * This function returns a function pointer to the current registered - * handler for new incoming channels on a channel listener. - */ - -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING) - return chan_l->listener; - - return NULL; -} - -/** - * Set the listener for a channel listener + * Set the listener for a channel listener. * * This function sets the handler for new incoming channels on a channel * listener. */ - void channel_listener_set_listener_fn(channel_listener_t *chan_l, channel_listener_fn_ptr listener) @@ -1201,12 +1044,11 @@ channel_listener_set_listener_fn(channel_listener_t *chan_l, } /** - * Return the fixed-length cell handler for a channel + * Return the fixed-length cell handler for a channel. * * This function gets the handler for incoming fixed-length cells installed * on a channel. */ - channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan) { @@ -1219,12 +1061,11 @@ channel_get_cell_handler(channel_t *chan) } /** - * Return the variable-length cell handler for a channel + * Return the variable-length cell handler for a channel. * * This function gets the handler for incoming variable-length cells * installed on a channel. */ - channel_var_cell_handler_fn_ptr channel_get_var_cell_handler(channel_t *chan) { @@ -1237,21 +1078,17 @@ channel_get_var_cell_handler(channel_t *chan) } /** - * Set both cell handlers for a channel + * Set both cell handlers for a channel. * * This function sets both the fixed-length and variable length cell handlers - * for a channel and processes any incoming cells that had been blocked in the - * queue because none were available. + * for a channel. */ - void channel_set_cell_handlers(channel_t *chan, channel_cell_handler_fn_ptr cell_handler, channel_var_cell_handler_fn_ptr var_cell_handler) { - int try_again = 0; - tor_assert(chan); tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); @@ -1262,21 +1099,9 @@ channel_set_cell_handlers(channel_t *chan, "Setting var_cell_handler callback for channel %p to %p", chan, var_cell_handler); - /* Should we try the queue? */ - if (cell_handler && - cell_handler != chan->cell_handler) try_again = 1; - if (var_cell_handler && - var_cell_handler != chan->var_cell_handler) try_again = 1; - /* Change them */ chan->cell_handler = cell_handler; chan->var_cell_handler = var_cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) && - try_again && - (chan->cell_handler || - chan->var_cell_handler)) channel_process_cells(chan); } /* @@ -1293,13 +1118,12 @@ channel_set_cell_handlers(channel_t *chan, */ /** - * Mark a channel for closure + * Mark a channel for closure. * * This function tries to close a channel_t; it will go into the CLOSING * state, and eventually the lower layer should put it into the CLOSED or * ERROR state. Then, channel_run_cleanup() will eventually free it. */ - void channel_mark_for_close(channel_t *chan) { @@ -1333,13 +1157,12 @@ channel_mark_for_close(channel_t *chan) } /** - * Mark a channel listener for closure + * Mark a channel listener for closure. * * This function tries to close a channel_listener_t; it will go into the * CLOSING state, and eventually the lower layer should put it into the CLOSED * or ERROR state. Then, channel_run_cleanup() will eventually free it. */ - void channel_listener_mark_for_close(channel_listener_t *chan_l) { @@ -1374,13 +1197,12 @@ channel_listener_mark_for_close(channel_listener_t *chan_l) } /** - * Close a channel from the lower layer + * Close a channel from the lower layer. * * Notify the channel code that the channel is being closed due to a non-error * condition in the lower layer. This does not call the close() method, since * the lower layer already knows. */ - void channel_close_from_lower_layer(channel_t *chan) { @@ -1403,43 +1225,12 @@ channel_close_from_lower_layer(channel_t *chan) } /** - * Close a channel listener from the lower layer - * - * Notify the channel code that the channel listener is being closed due to a - * non-error condition in the lower layer. This does not call the close() - * method, since the lower layer already knows. - */ - -void -channel_listener_close_from_lower_layer(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer event", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** - * Notify that the channel is being closed due to an error condition + * Notify that the channel is being closed due to an error condition. * * This function is called by the lower layer implementing the transport * when a channel must be closed due to an error condition. This does not * call the channel's close method, since the lower layer already knows. */ - void channel_close_for_error(channel_t *chan) { @@ -1461,44 +1252,12 @@ channel_close_for_error(channel_t *chan) } /** - * Notify that the channel listener is being closed due to an error condition - * - * This function is called by the lower layer implementing the transport - * when a channel listener must be closed due to an error condition. This - * does not call the channel listener's close method, since the lower layer - * already knows. - */ - -void -channel_listener_close_for_error(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer error", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** - * Notify that the lower layer is finished closing the channel + * Notify that the lower layer is finished closing the channel. * * This function should be called by the lower layer when a channel * is finished closing and it should be regarded as inactive and * freed by the channel code. */ - void channel_closed(channel_t *chan) { @@ -1525,39 +1284,11 @@ channel_closed(channel_t *chan) } /** - * Notify that the lower layer is finished closing the channel listener - * - * This function should be called by the lower layer when a channel listener - * is finished closing and it should be regarded as inactive and - * freed by the channel code. - */ - -void -channel_listener_closed(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR); - - /* No-op if already inactive */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); - } else { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR); - } -} - -/** - * Clear the identity_digest of a channel + * Clear the identity_digest of a channel. * * This function clears the identity digest of the remote endpoint for a * channel; this is intended for use by the lower layer. */ - void channel_clear_identity_digest(channel_t *chan) { @@ -1582,7 +1313,7 @@ channel_clear_identity_digest(channel_t *chan) } /** - * Set the identity_digest of a channel + * Set the identity_digest of a channel. * * This function sets the identity digest of the remote endpoint for a * channel; this is intended for use by the lower layer. @@ -1641,12 +1372,11 @@ channel_set_identity_digest(channel_t *chan, } /** - * Clear the remote end metadata (identity_digest/nickname) of a channel + * Clear the remote end metadata (identity_digest) of a channel. * * This function clears all the remote end info from a channel; this is * intended for use by the lower layer. */ - void channel_clear_remote_end(channel_t *chan) { @@ -1668,429 +1398,103 @@ channel_clear_remote_end(channel_t *chan) memset(chan->identity_digest, 0, sizeof(chan->identity_digest)); - tor_free(chan->nickname); } /** - * Set the remote end metadata (identity_digest/nickname) of a channel + * Write to a channel the given packed cell. * - * This function sets new remote end info on a channel; this is intended - * for use by the lower layer. + * Two possible errors can happen. Either the channel is not opened or the + * lower layer (specialized channel) failed to write it. In both cases, it is + * the caller responsibility to free the cell. */ - -void -channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname) -{ - int was_in_digest_map, should_be_in_digest_map, state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Setting remote endpoint identity on channel %p with " - "global ID " U64_FORMAT " to nickname %s, digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - nickname ? nickname : "(null)", - identity_digest ? - hex_str(identity_digest, DIGEST_LEN) : "(null)"); - - state_not_in_map = CHANNEL_CONDEMNED(chan); - - was_in_digest_map = - !state_not_in_map && - chan->registered && - !tor_digest_is_zero(chan->identity_digest); - should_be_in_digest_map = - !state_not_in_map && - chan->registered && - (identity_digest && - !tor_digest_is_zero(identity_digest)); - - if (was_in_digest_map) - /* We should always remove it; we'll add it back if we're writing - * in a new digest. - */ - channel_remove_from_digest_map(chan); - - if (identity_digest) { - memcpy(chan->identity_digest, - identity_digest, - sizeof(chan->identity_digest)); - - } else { - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - } - - tor_free(chan->nickname); - if (nickname) - chan->nickname = tor_strdup(nickname); - - /* Put it in the digest map if we should */ - if (should_be_in_digest_map) - channel_add_to_digest_map(chan); -} - -/** - * Duplicate a cell queue entry; this is a shallow copy intended for use - * in channel_write_cell_queue_entry(). - */ - -static cell_queue_entry_t * -cell_queue_entry_dup(cell_queue_entry_t *q) -{ - cell_queue_entry_t *rv = NULL; - - tor_assert(q); - - rv = tor_malloc(sizeof(*rv)); - memcpy(rv, q, sizeof(*rv)); - - return rv; -} - -/** - * Free a cell_queue_entry_t; the handed_off parameter indicates whether - * the contents were passed to the lower layer (it is responsible for - * them) or not (we should free). - */ - -STATIC void -cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) -{ - if (!q) return; - - if (!handed_off) { - /* - * If we handed it off, the recipient becomes responsible (or - * with packed cells the channel_t subclass calls packed_cell - * free after writing out its contents; see, e.g., - * channel_tls_write_packed_cell_method(). Otherwise, we have - * to take care of it here if possible. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - /* - * There doesn't seem to be a cell_free() function anywhere in the - * pre-channel code; just use tor_free() - */ - tor_free(q->u.fixed.cell); - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - /* - * This one's in connection_or.c; it'd be nice to figure out the - * whole flow of cells from one end to the other and factor the - * cell memory management functions like this out of the specific - * TLS lower layer. - */ - var_cell_free(q->u.var.var_cell); - } - break; - default: - /* - * Nothing we can do if we don't know the type; this will - * have been warned about elsewhere. - */ - break; - } - } - tor_free(q); -} - -#if 0 -/** - * Check whether a cell queue entry is padding; this is a helper function - * for channel_write_cell_queue_entry() - */ - static int -cell_queue_entry_is_padding(cell_queue_entry_t *q) -{ - tor_assert(q); - - if (q->type == CELL_QUEUE_FIXED) { - if (q->u.fixed.cell) { - if (q->u.fixed.cell->command == CELL_PADDING || - q->u.fixed.cell->command == CELL_VPADDING) { - return 1; - } - } - } else if (q->type == CELL_QUEUE_VAR) { - if (q->u.var.var_cell) { - if (q->u.var.var_cell->command == CELL_PADDING || - q->u.var.var_cell->command == CELL_VPADDING) { - return 1; - } - } - } - - return 0; -} -#endif /* 0 */ - -/** - * Allocate a new cell queue entry for a fixed-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; - - return q; -} - -/** - * Allocate a new cell queue entry for a variable-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(var_cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; - - return q; -} - -/** - * Ask how big the cell contained in a cell_queue_entry_t is - */ - -static size_t -channel_get_cell_queue_entry_size(channel_t *chan, cell_queue_entry_t *q) +write_packed_cell(channel_t *chan, packed_cell_t *cell) { - size_t rv = 0; - - tor_assert(chan); - tor_assert(q); - - switch (q->type) { - case CELL_QUEUE_FIXED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - case CELL_QUEUE_VAR: - rv = get_var_cell_header_size(chan->wide_circ_ids) + - (q->u.var.var_cell ? q->u.var.var_cell->payload_len : 0); - break; - case CELL_QUEUE_PACKED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - default: - tor_assert_nonfatal_unreached_once(); - } - - return rv; -} - -/** - * Write to a channel based on a cell_queue_entry_t - * - * Given a cell_queue_entry_t filled out by the caller, try to send the cell - * and queue it if we can't. - */ - -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) -{ - int result = 0, sent = 0; - cell_queue_entry_t *tmp = NULL; + int ret = -1; size_t cell_bytes; tor_assert(chan); - tor_assert(q); + tor_assert(cell); /* Assert that the state makes sense for a cell write */ tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); { circid_t circ_id; - if (is_destroy_cell(chan, q, &circ_id)) { + if (packed_cell_is_destroy(chan, cell, &circ_id)) { channel_note_destroy_not_pending(chan, circ_id); } } /* For statistical purposes, figure out how big this cell is */ - cell_bytes = channel_get_cell_queue_entry_size(chan, q); + cell_bytes = get_cell_network_size(chan->wide_circ_ids); /* Can we send it right out? If so, try */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) && - CHANNEL_IS_OPEN(chan)) { - /* Pick the right write function for this cell type and save the result */ - switch (q->type) { - case CELL_QUEUE_FIXED: - tor_assert(chan->write_cell); - tor_assert(q->u.fixed.cell); - result = chan->write_cell(chan, q->u.fixed.cell); - break; - case CELL_QUEUE_PACKED: - tor_assert(chan->write_packed_cell); - tor_assert(q->u.packed.packed_cell); - result = chan->write_packed_cell(chan, q->u.packed.packed_cell); - break; - case CELL_QUEUE_VAR: - tor_assert(chan->write_var_cell); - tor_assert(q->u.var.var_cell); - result = chan->write_var_cell(chan, q->u.var.var_cell); - break; - default: - tor_assert(1); - } - - /* Check if we got it out */ - if (result > 0) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_bytes; - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_passed_to_lower_layer; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_passed_to_lower_layer += cell_bytes; - channel_assert_counter_consistency(); - } + if (!CHANNEL_IS_OPEN(chan)) { + goto done; } - if (!sent) { - /* Not sent, queue it */ - /* - * We have to copy the queue entry passed in, since the caller probably - * used the stack. - */ - tmp = cell_queue_entry_dup(q); - TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next); - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_in_queues; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_in_queues += cell_bytes; - channel_assert_counter_consistency(); - /* Update channel queue size */ - chan->bytes_in_queue += cell_bytes; - /* Try to process the queue? */ - if (CHANNEL_IS_OPEN(chan)) channel_flush_cells(chan); + /* Write the cell on the connection's outbuf. */ + if (chan->write_packed_cell(chan, cell) < 0) { + goto done; } + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* Update the counter */ + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_bytes; + /* Successfully sent the cell. */ + ret = 0; + + done: + return ret; } -/** Write a generic cell type to a channel +/** + * Write a packed cell to a channel. * - * Write a generic cell to a channel. It is called by channel_write_cell(), - * channel_write_var_cell() and channel_write_packed_cell() in order to reduce - * code duplication. Notice that it takes cell as pointer of type void, - * this can be dangerous because no type check is performed. + * Write a packed cell to a channel using the write_cell() method. This is + * called by the transport-independent code to deliver a packed cell to a + * channel for transmission. + * + * Return 0 on success else a negative value. In both cases, the caller should + * not access the cell anymore, it is freed both on success and error. */ - -void -channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q) +int +channel_write_packed_cell(channel_t *chan, packed_cell_t *cell) { + int ret = -1; tor_assert(chan); tor_assert(cell); if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding %c %p on closing channel %p with " - "global ID "U64_FORMAT, *cell_type, cell, chan, + log_debug(LD_CHANNEL, "Discarding %p on closing channel %p with " + "global ID "U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - tor_free(cell); - return; + goto end; } log_debug(LD_CHANNEL, - "Writing %c %p to channel %p with global ID " - U64_FORMAT, *cell_type, - cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - - channel_write_cell_queue_entry(chan, q); - /* Update the queue size estimate */ - channel_update_xmit_queue_size(chan); -} - -/** - * Write a cell to a channel - * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); - * it is called by the transport-independent code to deliver a cell to a - * channel for transmission. - */ - -void -channel_write_cell(channel_t *chan, cell_t *cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_FIXED; - q.u.fixed.cell = cell; - channel_write_cell_generic_(chan, "cell_t", cell, &q); -} + "Writing %p to channel %p with global ID " + U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); -/** - * Write a packed cell to a channel - * - * Write a packed cell to a channel using the write_cell() method. This is - * called by the transport-independent code to deliver a packed cell to a - * channel for transmission. - */ + ret = write_packed_cell(chan, cell); -void -channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_PACKED; - q.u.packed.packed_cell = packed_cell; - channel_write_cell_generic_(chan, "packed_cell_t", packed_cell, &q); + end: + /* Whatever happens, we free the cell. Either an error occurred or the cell + * was put on the connection outbuf, both cases we have ownership of the + * cell and we free it. */ + packed_cell_free(cell); + return ret; } /** - * Write a variable-length cell to a channel - * - * Write a variable-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels - * connection_or_write_var_cell_to_buf(); it's called by the transport- - * independent code to deliver a var_cell to a channel for transmission. - */ - -void -channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_VAR; - q.u.var.var_cell = var_cell; - channel_write_cell_generic_(chan, "var_cell_t", var_cell, &q); -} - -/** - * Change channel state + * Change channel state. * * This internal and subclass use only function is used to change channel * state, performing all transition validity checks and whatever actions * are appropriate to the state transition in question. */ - static void channel_change_state_(channel_t *chan, channel_state_t to_state) { @@ -2122,15 +1526,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel %p (global ID " U64_FORMAT ") from \"%s\" to \"%s\"", @@ -2187,36 +1582,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) } else if (to_state == CHANNEL_STATE_MAINT) { scheduler_channel_doesnt_want_writes(chan); } - - /* - * If we're closing, this channel no longer counts toward the global - * estimated queue size; if we're open, it now does. - */ - if ((to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) && - (from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size -= chan->bytes_in_queue; - } - - /* - * If we're opening, this channel now does count toward the global - * estimated queue size. - */ - if ((to_state == CHANNEL_STATE_OPEN || - to_state == CHANNEL_STATE_MAINT) && - !(from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size += chan->bytes_in_queue; - } - - if (to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) { - /* Assert that all queues are empty */ - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)); - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)); - } } /** @@ -2240,22 +1605,15 @@ channel_change_state_open(channel_t *chan) /* Tell circuits if we opened and stuff */ channel_do_open_actions(chan); chan->has_been_open = 1; - - /* Check for queued cells to process */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - channel_process_cells(chan); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) - channel_flush_cells(chan); } /** - * Change channel listener state + * Change channel listener state. * * This internal and subclass use only function is used to change channel * listener state, performing all transition validity checks and whatever * actions are appropriate to the state transition in question. */ - void channel_listener_change_state(channel_listener_t *chan_l, channel_listener_state_t to_state) @@ -2287,15 +1645,6 @@ channel_listener_change_state(channel_listener_t *chan_l, tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel listener %p (global ID " U64_FORMAT "from \"%s\" to \"%s\"", @@ -2328,30 +1677,39 @@ channel_listener_change_state(channel_listener_t *chan_l, if (to_state == CHANNEL_LISTENER_STATE_CLOSED || to_state == CHANNEL_LISTENER_STATE_ERROR) { - /* Assert that the queue is empty */ tor_assert(!(chan_l->incoming_list) || smartlist_len(chan_l->incoming_list) == 0); } } -/** - * Try to flush cells to the lower layer - * - * this is called by the lower layer to indicate that it wants more cells; - * it will try to write up to num_cells cells from the channel's cell queue or - * from circuits active on that channel, or as many as it has available if - * num_cells == -1. - */ - +/* Maximum number of cells that is allowed to flush at once within + * channel_flush_some_cells(). */ #define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 +/** + * Try to flush cells of the given channel chan up to a maximum of num_cells. + * + * This is called by the scheduler when it wants to flush cells from the + * channel's circuit queue(s) to the connection outbuf (not yet on the wire). + * + * If the channel is not in state CHANNEL_STATE_OPEN, this does nothing and + * will return 0 meaning no cells were flushed. + * + * If num_cells is -1, we'll try to flush up to the maximum cells allowed + * defined in MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED. + * + * On success, the number of flushed cells are returned and it can never be + * above num_cells. If 0 is returned, no cells were flushed either because the + * channel was not opened or we had no cells on the channel. A negative number + * can NOT be sent back. + * + * This function is part of the fast path. */ MOCK_IMPL(ssize_t, channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) { unsigned int unlimited = 0; ssize_t flushed = 0; - int num_cells_from_circs, clamped_num_cells; - int q_len_before, q_len_after; + int clamped_num_cells; tor_assert(chan); @@ -2360,11 +1718,6 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ if (CHANNEL_IS_OPEN(chan)) { - /* Try to flush as much as we can that's already queued */ - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - if (!unlimited && num_cells <= flushed) goto done; - if (circuitmux_num_cells(chan->cmux) > 0) { /* Calculate number of cells, including clamp */ if (unlimited) { @@ -2378,45 +1731,9 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } } - /* - * Keep track of the change in queue size; we have to count cells - * channel_flush_from_first_active_circuit() writes out directly, - * but not double-count ones we might get later in - * channel_flush_some_cells_from_outgoing_queue() - */ - q_len_before = chan_cell_queue_len(&(chan->outgoing_queue)); - /* Try to get more cells from any active circuits */ - num_cells_from_circs = channel_flush_from_first_active_circuit( + flushed = channel_flush_from_first_active_circuit( chan, clamped_num_cells); - - q_len_after = chan_cell_queue_len(&(chan->outgoing_queue)); - - /* - * If it claims we got some, adjust the flushed counter and consider - * processing the queue again - */ - if (num_cells_from_circs > 0) { - /* - * Adjust flushed by the number of cells counted in - * num_cells_from_circs that didn't go to the cell queue. - */ - - if (q_len_after > q_len_before) { - num_cells_from_circs -= (q_len_after - q_len_before); - if (num_cells_from_circs < 0) num_cells_from_circs = 0; - } - - flushed += num_cells_from_circs; - - /* Now process the queue if necessary */ - - if ((q_len_after > q_len_before) && - (unlimited || (flushed < num_cells))) { - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - } - } } } @@ -2425,197 +1742,16 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } /** - * Flush cells from just the channel's outgoing cell queue + * Check if any cells are available. * - * This gets called from channel_flush_some_cells() above to flush cells - * just from the queue without trying for active_circuits. + * This is used by the scheduler to know if the channel has more to flush + * after a scheduling round. */ - -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells) -{ - unsigned int unlimited = 0; - ssize_t flushed = 0; - cell_queue_entry_t *q = NULL; - size_t cell_size; - int free_q = 0, handed_off = 0; - - tor_assert(chan); - tor_assert(chan->write_cell); - tor_assert(chan->write_packed_cell); - tor_assert(chan->write_var_cell); - - if (num_cells < 0) unlimited = 1; - if (!unlimited && num_cells <= flushed) return 0; - - /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ - if (CHANNEL_IS_OPEN(chan)) { - while ((unlimited || num_cells > flushed) && - NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) { - free_q = 0; - handed_off = 0; - - /* Figure out how big it is for statistical purposes */ - cell_size = channel_get_cell_queue_entry_size(chan, q); - /* - * Okay, we have a good queue entry, try to give it to the lower - * layer. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - if (chan->write_cell(chan, - q->u.fixed.cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - if (chan->write_packed_cell(chan, - q->u.packed.packed_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - if (chan->write_var_cell(chan, - q->u.var.var_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - default: - /* Unknown type, log and free it */ - log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p " - "(global ID " U64_FORMAT "; ignoring it." - " Someone should fix this.", - q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); - free_q = 1; - handed_off = 0; - } - - /* - * if free_q is set, we used it and should remove the queue entry; - * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't - * accessing freed memory - */ - if (free_q) { - TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); - /* - * ...and we handed a cell off to the lower layer, so we should - * update the counters. - */ - ++n_channel_cells_passed_to_lower_layer; - --n_channel_cells_in_queues; - n_channel_bytes_passed_to_lower_layer += cell_size; - n_channel_bytes_in_queues -= cell_size; - channel_assert_counter_consistency(); - /* Update the channel's queue size too */ - chan->bytes_in_queue -= cell_size; - /* Finally, free q */ - cell_queue_entry_free(q, handed_off); - q = NULL; - } else { - /* No cell removed from list, so we can't go on any further */ - break; - } - } - } - - /* Did we drain the queue? */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - channel_timestamp_drained(chan); - } - - /* Update the estimate queue size */ - channel_update_xmit_queue_size(chan); - - return flushed; -} - -/** - * Flush as many cells as we possibly can from the queue - * - * This tries to flush as many cells from the queue as the lower layer - * will take. It just calls channel_flush_some_cells_from_outgoing_queue() - * in unlimited mode. - */ - -void -channel_flush_cells(channel_t *chan) -{ - channel_flush_some_cells_from_outgoing_queue(chan, -1); -} - -/** - * Check if any cells are available - * - * This gets used from the lower layer to check if any more cells are - * available. - */ - MOCK_IMPL(int, channel_more_to_flush, (channel_t *chan)) { tor_assert(chan); - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - - /* Check if any circuits would like to queue some */ if (circuitmux_num_cells(chan->cmux) > 0) return 1; /* Else no */ @@ -2623,12 +1759,11 @@ channel_more_to_flush, (channel_t *chan)) } /** - * Notify the channel we're done flushing the output in the lower layer + * Notify the channel we're done flushing the output in the lower layer. * * Connection.c will call this when we've flushed the output; there's some * dirreq-related maintenance to do. */ - void channel_notify_flushed(channel_t *chan) { @@ -2641,12 +1776,11 @@ channel_notify_flushed(channel_t *chan) } /** - * Process the queue of incoming channels on a listener + * Process the queue of incoming channels on a listener. * * Use a listener's registered callback to process as many entries in the * queue of incoming channels as possible. */ - void channel_listener_process_incoming(channel_listener_t *listener) { @@ -2688,18 +1822,17 @@ channel_listener_process_incoming(channel_listener_t *listener) } /** - * Take actions required when a channel becomes open + * Take actions required when a channel becomes open. * * Handle actions we should do when we know a channel is open; a lot of * this comes from the old connection_or_set_state_open() of connection_or.c. * * Because of this mechanism, future channel_t subclasses should take care - * not to change a channel to from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN + * not to change a channel from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN * until there is positive confirmation that the network is operational. * In particular, anything UDP-based should not make this transition until a * packet is received from the other side. */ - void channel_do_open_actions(channel_t *chan) { @@ -2714,7 +1847,6 @@ channel_do_open_actions(channel_t *chan) if (started_here) { circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - rep_hist_note_connect_succeeded(chan->identity_digest, now); router_set_status(chan->identity_digest, 1); } else { /* only report it to the geoip module if it's not a known router */ @@ -2755,7 +1887,7 @@ channel_do_open_actions(channel_t *chan) CHANNELPADDING_SOS_PARAM, CHANNELPADDING_SOS_DEFAULT, 0, 1)) { /* Disable if we're using RSOS and the consensus disabled padding - * for RSOS*/ + * for RSOS */ channelpadding_disable_padding_on_channel(chan); } else if (get_options()->ReducedConnectionPadding) { /* Padding can be forced and/or reduced by clients, regardless of if @@ -2768,13 +1900,12 @@ channel_do_open_actions(channel_t *chan) } /** - * Queue an incoming channel on a listener + * Queue an incoming channel on a listener. * * Internal and subclass use only function to queue an incoming channel from * a listener. A subclass of channel_listener_t should call this when a new * incoming channel is created. */ - void channel_listener_queue_incoming(channel_listener_t *listener, channel_t *incoming) @@ -2824,207 +1955,31 @@ channel_listener_queue_incoming(channel_listener_t *listener, } /** - * Process queued incoming cells - * - * Process as many queued cells as we can from the incoming - * cell queue. + * Process a cell from the given channel. */ - void -channel_process_cells(channel_t *chan) +channel_process_cell(channel_t *chan, cell_t *cell) { - cell_queue_entry_t *q; tor_assert(chan); tor_assert(CHANNEL_IS_CLOSING(chan) || CHANNEL_IS_MAINT(chan) || CHANNEL_IS_OPEN(chan)); - - log_debug(LD_CHANNEL, - "Processing as many incoming cells as we can for channel %p", - chan); - - /* Nothing we can do if we have no registered cell handlers */ - if (!(chan->cell_handler || - chan->var_cell_handler)) return; - /* Nothing we can do if we have no cells */ - if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return; - - /* - * Process cells until we're done or find one we have no current handler - * for. - * - * We must free the cells here after calling the handler, since custody - * of the buffer was given to the channel layer when they were queued; - * see comments on memory management in channel_queue_cell() and in - * channel_queue_var_cell() below. - */ - while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) { - tor_assert(q); - tor_assert(q->type == CELL_QUEUE_FIXED || - q->type == CELL_QUEUE_VAR); - - if (q->type == CELL_QUEUE_FIXED && - chan->cell_handler) { - /* Handle a fixed-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.fixed.cell); - log_debug(LD_CHANNEL, - "Processing incoming cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.fixed.cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, q->u.fixed.cell); - tor_free(q->u.fixed.cell); - tor_free(q); - } else if (q->type == CELL_QUEUE_VAR && - chan->var_cell_handler) { - /* Handle a variable-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.var.var_cell); - log_debug(LD_CHANNEL, - "Processing incoming var_cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.var.var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, q->u.var.var_cell); - tor_free(q->u.var.var_cell); - tor_free(q); - } else { - /* Can't handle this one */ - break; - } - } -} - -/** - * Queue incoming cell - * - * This should be called by a channel_t subclass to queue an incoming fixed- - * length cell for processing, and process it if possible. - */ - -void -channel_queue_cell(channel_t *chan, cell_t *cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - cell_t *cell_copy = NULL; - - tor_assert(chan); tor_assert(cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; + /* Nothing we can do if we have no registered cell handlers */ + if (!chan->cell_handler) + return; /* Timestamp for receiving */ channel_timestamp_recv(chan); - - /* Update the counters */ + /* Update received counter. */ ++(chan->n_cells_recved); chan->n_bytes_recved += get_cell_network_size(chan->wide_circ_ids); - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = tor_malloc_zero(sizeof(cell_t)); - memcpy(cell_copy, cell, sizeof(cell_t)); - q = cell_queue_entry_new_fixed(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } -} - -/** - * Queue incoming variable-length cell - * - * This should be called by a channel_t subclass to queue an incoming - * variable-length cell for processing, and process it if possible. - */ - -void -channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - var_cell_t *cell_copy = NULL; - - tor_assert(chan); - tor_assert(var_cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->var_cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; - - /* Timestamp for receiving */ - channel_timestamp_recv(chan); - - /* Update the counter */ - ++(chan->n_cells_recved); - chan->n_bytes_recved += get_var_cell_header_size(chan->wide_circ_ids) + - var_cell->payload_len; - - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->var_cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, var_cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = var_cell_copy(var_cell); - q = cell_queue_entry_new_var(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } + log_debug(LD_CHANNEL, + "Processing incoming cell_t %p for channel %p (global ID " + U64_FORMAT ")", cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->cell_handler(chan, cell); } /** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set @@ -3051,51 +2006,12 @@ packed_cell_is_destroy(channel_t *chan, } /** - * Assert that the global channel stats counters are internally consistent - */ - -static void -channel_assert_counter_consistency(void) -{ - tor_assert(n_channel_cells_queued == - (n_channel_cells_in_queues + n_channel_cells_passed_to_lower_layer)); - tor_assert(n_channel_bytes_queued == - (n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer)); -} - -/* DOCDOC */ -static int -is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out) -{ - *circid_out = 0; - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell->command == CELL_DESTROY) { - *circid_out = q->u.fixed.cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell->command == CELL_DESTROY) { - *circid_out = q->u.var.var_cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_PACKED: - return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out); - } - return 0; -} - -/** - * Send destroy cell on a channel + * Send destroy cell on a channel. * * Write a destroy cell with circ ID <b>circ_id</b> and reason <b>reason</b> * onto channel <b>chan</b>. Don't perform range-checking on reason: * we may want to propagate reasons from other cells. */ - int channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) { @@ -3131,30 +2047,16 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) } /** - * Dump channel statistics to the log + * Dump channel statistics to the log. * * This is called from dumpstats() in main.c and spams the log with * statistics on channels. */ - void channel_dumpstats(int severity) { if (all_channels && smartlist_len(all_channels) > 0) { tor_log(severity, LD_GENERAL, - "Channels have queued " U64_FORMAT " bytes in " U64_FORMAT " cells, " - "and handed " U64_FORMAT " bytes in " U64_FORMAT " cells to the lower" - " layer.", - U64_PRINTF_ARG(n_channel_bytes_queued), - U64_PRINTF_ARG(n_channel_cells_queued), - U64_PRINTF_ARG(n_channel_bytes_passed_to_lower_layer), - U64_PRINTF_ARG(n_channel_cells_passed_to_lower_layer)); - tor_log(severity, LD_GENERAL, - "There are currently " U64_FORMAT " bytes in " U64_FORMAT " cells " - "in channel queues.", - U64_PRINTF_ARG(n_channel_bytes_in_queues), - U64_PRINTF_ARG(n_channel_cells_in_queues)); - tor_log(severity, LD_GENERAL, "Dumping statistics about %d channels:", smartlist_len(all_channels)); tor_log(severity, LD_GENERAL, @@ -3176,12 +2078,11 @@ channel_dumpstats(int severity) } /** - * Dump channel listener statistics to the log + * Dump channel listener statistics to the log. * * This is called from dumpstats() in main.c and spams the log with * statistics on channel listeners. */ - void channel_listener_dumpstats(int severity) { @@ -3208,9 +2109,8 @@ channel_listener_dumpstats(int severity) } /** - * Set the cmux policy on all active channels + * Set the cmux policy on all active channels. */ - void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol) { @@ -3224,12 +2124,11 @@ channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol) } /** - * Clean up channels + * Clean up channels. * * This gets called periodically from run_scheduled_events() in main.c; * it cleans up after closed channels. */ - void channel_run_cleanup(void) { @@ -3251,12 +2150,11 @@ channel_run_cleanup(void) } /** - * Clean up channel listeners + * Clean up channel listeners. * * This gets called periodically from run_scheduled_events() in main.c; * it cleans up after closed channel listeners. */ - void channel_listener_run_cleanup(void) { @@ -3278,9 +2176,8 @@ channel_listener_run_cleanup(void) } /** - * Free a list of channels for channel_free_all() + * Free a list of channels for channel_free_all(). */ - static void channel_free_list(smartlist_t *channels, int mark_for_close) { @@ -3304,15 +2201,14 @@ channel_free_list(smartlist_t *channels, int mark_for_close) if (!CHANNEL_CONDEMNED(curr)) { channel_mark_for_close(curr); } - channel_force_free(curr); + channel_force_xfree(curr); } else channel_free(curr); } SMARTLIST_FOREACH_END(curr); } /** - * Free a list of channel listeners for channel_free_all() + * Free a list of channel listeners for channel_free_all(). */ - static void channel_listener_free_list(smartlist_t *listeners, int mark_for_close) { @@ -3333,20 +2229,19 @@ channel_listener_free_list(smartlist_t *listeners, int mark_for_close) curr->state == CHANNEL_LISTENER_STATE_ERROR)) { channel_listener_mark_for_close(curr); } - channel_listener_force_free(curr); + channel_listener_force_xfree(curr); } else channel_listener_free(curr); } SMARTLIST_FOREACH_END(curr); } /** - * Close all channels and free everything + * Close all channels and free everything. * * This gets called from tor_free_all() in main.c to clean up on exit. * It will close all registered channels and free associated storage, * then free the all_channels, active_channels, listening_channels and * finished_channels lists and also channel_identity_map. */ - void channel_free_all(void) { @@ -3411,7 +2306,7 @@ channel_free_all(void) } /** - * Connect to a given addr/port/digest + * Connect to a given addr/port/digest. * * This sets up a new outgoing channel; in the future if multiple * channel_t subclasses are available, this is where the selection policy @@ -3420,7 +2315,6 @@ channel_free_all(void) * single abstract object encapsulating all the protocol details of * how to contact an OR. */ - channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, const char *id_digest, @@ -3430,7 +2324,7 @@ channel_connect(const tor_addr_t *addr, uint16_t port, } /** - * Decide which of two channels to prefer for extending a circuit + * Decide which of two channels to prefer for extending a circuit. * * This function is called while extending a circuit and returns true iff * a is 'better' than b. The most important criterion here is that a @@ -3439,7 +2333,6 @@ channel_connect(const tor_addr_t *addr, uint16_t port, * * This is based on the former connection_or_is_better() of connection_or.c */ - int channel_is_better(channel_t *a, channel_t *b) { @@ -3491,7 +2384,7 @@ channel_is_better(channel_t *a, channel_t *b) } /** - * Get a channel to extend a circuit + * Get a channel to extend a circuit. * * Pick a suitable channel to extend a circuit to given the desired digest * the address we believe is correct for that digest; this tries to see @@ -3500,7 +2393,6 @@ channel_is_better(channel_t *a, channel_t *b) * and our next action, and set *launch_out to a boolean indicated whether * the caller should try to launch a new channel with channel_connect(). */ - channel_t * channel_get_for_extend(const char *rsa_id_digest, const ed25519_public_key_t *ed_id, @@ -3605,12 +2497,11 @@ channel_get_for_extend(const char *rsa_id_digest, } /** - * Describe the transport subclass for a channel + * Describe the transport subclass for a channel. * * Invoke a method to get a string description of the lower-layer * transport for this channel. */ - const char * channel_describe_transport(channel_t *chan) { @@ -3621,12 +2512,11 @@ channel_describe_transport(channel_t *chan) } /** - * Describe the transport subclass for a channel listener + * Describe the transport subclass for a channel listener. * * Invoke a method to get a string description of the lower-layer * transport for this channel listener. */ - const char * channel_listener_describe_transport(channel_listener_t *chan_l) { @@ -3637,24 +2527,10 @@ channel_listener_describe_transport(channel_listener_t *chan_l) } /** - * Return the number of entries in <b>queue</b> - */ -STATIC int -chan_cell_queue_len(const chan_cell_queue_t *queue) -{ - int r = 0; - cell_queue_entry_t *cell; - TOR_SIMPLEQ_FOREACH(cell, queue, next) - ++r; - return r; -} - -/** - * Dump channel statistics + * Dump channel statistics. * - * Dump statistics for one channel to the log + * Dump statistics for one channel to the log. */ - MOCK_IMPL(void, channel_dump_statistics, (channel_t *chan, int severity)) { @@ -3684,35 +2560,18 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_active), U64_PRINTF_ARG(now - chan->timestamp_active)); - /* Handle digest and nickname */ + /* Handle digest. */ if (!tor_digest_is_zero(chan->identity_digest)) { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and nickname %s", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and no known nickname", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " says it is connected " + "to an OR with digest %s", + U64_PRINTF_ARG(chan->global_identifier), + hex_str(chan->identity_digest, DIGEST_LEN)); } else { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " of the OR it is connected to, but reports its nickname is %s", - U64_PRINTF_ARG(chan->global_identifier), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " or the nickname of the OR it is connected to", - U64_PRINTF_ARG(chan->global_identifier)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know the digest" + " of the OR it is connected to", + U64_PRINTF_ARG(chan->global_identifier)); } /* Handle remote address and descriptions */ @@ -3761,14 +2620,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) channel_is_incoming(chan) ? "incoming" : "outgoing"); - /* Describe queues */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has %d queued incoming cells" - " and %d queued outgoing cells", - U64_PRINTF_ARG(chan->global_identifier), - chan_cell_queue_len(&chan->incoming_queue), - chan_cell_queue_len(&chan->outgoing_queue)); - /* Describe circuits */ tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " has %d active circuits out of" @@ -3787,12 +2638,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_client), U64_PRINTF_ARG(now - chan->timestamp_client)); tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was last drained at " - U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_drained), - U64_PRINTF_ARG(now - chan->timestamp_drained)); - tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " last received a cell " "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", U64_PRINTF_ARG(chan->global_identifier), @@ -3868,11 +2713,10 @@ channel_dump_statistics, (channel_t *chan, int severity)) } /** - * Dump channel listener statistics + * Dump channel listener statistics. * - * Dump statistics for one channel listener to the log + * Dump statistics for one channel listener to the log. */ - void channel_listener_dump_statistics(channel_listener_t *chan_l, int severity) { @@ -3935,11 +2779,10 @@ channel_listener_dump_statistics(channel_listener_t *chan_l, int severity) } /** - * Invoke transport-specific stats dump for channel + * Invoke transport-specific stats dump for channel. * - * If there is a lower-layer statistics dump method, invoke it + * If there is a lower-layer statistics dump method, invoke it. */ - void channel_dump_transport_statistics(channel_t *chan, int severity) { @@ -3949,11 +2792,10 @@ channel_dump_transport_statistics(channel_t *chan, int severity) } /** - * Invoke transport-specific stats dump for channel listener + * Invoke transport-specific stats dump for channel listener. * - * If there is a lower-layer statistics dump method, invoke it + * If there is a lower-layer statistics dump method, invoke it. */ - void channel_listener_dump_transport_statistics(channel_listener_t *chan_l, int severity) @@ -3964,7 +2806,7 @@ channel_listener_dump_transport_statistics(channel_listener_t *chan_l, } /** - * Return text description of the remote endpoint + * Return text description of the remote endpoint. * * This function return a test provided by the lower layer of the remote * endpoint for this channel; it should specify the actual address connected @@ -3997,7 +2839,7 @@ channel_get_actual_remote_address(channel_t *chan) } /** - * Return text description of the remote endpoint canonical address + * Return text description of the remote endpoint canonical address. * * This function return a test provided by the lower layer of the remote * endpoint for this channel; it should use the known canonical address for @@ -4036,37 +2878,25 @@ channel_get_addr_if_possible,(channel_t *chan, tor_addr_t *addr_out)) } /** - * Check if there are outgoing queue writes on this channel - * - * Indicate if either we have queued cells, or if not, whether the underlying - * lower-layer transport thinks it has an output queue. + * Return true iff the channel has any cells on the connection outbuf waiting + * to be sent onto the network. */ - int channel_has_queued_writes(channel_t *chan) { - int has_writes = 0; - tor_assert(chan); tor_assert(chan->has_queued_writes); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - has_writes = 1; - } else { - /* Check with the lower layer */ - has_writes = chan->has_queued_writes(chan); - } - - return has_writes; + /* Check with the lower layer */ + return chan->has_queued_writes(chan); } /** - * Check the is_bad_for_new_circs flag + * Check the is_bad_for_new_circs flag. * * This function returns the is_bad_for_new_circs flag of the specified * channel. */ - int channel_is_bad_for_new_circs(channel_t *chan) { @@ -4076,11 +2906,10 @@ channel_is_bad_for_new_circs(channel_t *chan) } /** - * Mark a channel as bad for new circuits + * Mark a channel as bad for new circuits. * * Set the is_bad_for_new_circs_flag on chan. */ - void channel_mark_bad_for_new_circs(channel_t *chan) { @@ -4090,13 +2919,12 @@ channel_mark_bad_for_new_circs(channel_t *chan) } /** - * Get the client flag + * Get the client flag. * * This returns the client flag of a channel, which will be set if * command_process_create_cell() in command.c thinks this is a connection * from a client. */ - int channel_is_client(const channel_t *chan) { @@ -4106,11 +2934,10 @@ channel_is_client(const channel_t *chan) } /** - * Set the client flag + * Set the client flag. * - * Mark a channel as being from a client + * Mark a channel as being from a client. */ - void channel_mark_client(channel_t *chan) { @@ -4120,11 +2947,10 @@ channel_mark_client(channel_t *chan) } /** - * Clear the client flag + * Clear the client flag. * - * Mark a channel as being _not_ from a client + * Mark a channel as being _not_ from a client. */ - void channel_clear_client(channel_t *chan) { @@ -4134,12 +2960,11 @@ channel_clear_client(channel_t *chan) } /** - * Get the canonical flag for a channel + * Get the canonical flag for a channel. * * This returns the is_canonical for a channel; this flag is determined by * the lower layer and can't be set in a transport-independent way. */ - int channel_is_canonical(channel_t *chan) { @@ -4150,12 +2975,11 @@ channel_is_canonical(channel_t *chan) } /** - * Test if the canonical flag is reliable + * Test if the canonical flag is reliable. * * This function asks if the lower layer thinks it's safe to trust the - * result of channel_is_canonical() + * result of channel_is_canonical(). */ - int channel_is_canonical_is_reliable(channel_t *chan) { @@ -4166,12 +2990,11 @@ channel_is_canonical_is_reliable(channel_t *chan) } /** - * Test incoming flag + * Test incoming flag. * * This function gets the incoming flag; this is set when a listener spawns * a channel. If this returns true the channel was remotely initiated. */ - int channel_is_incoming(channel_t *chan) { @@ -4181,12 +3004,11 @@ channel_is_incoming(channel_t *chan) } /** - * Set the incoming flag + * Set the incoming flag. * * This function is called when a channel arrives on a listening channel * to mark it as incoming. */ - void channel_mark_incoming(channel_t *chan) { @@ -4196,7 +3018,7 @@ channel_mark_incoming(channel_t *chan) } /** - * Test local flag + * Test local flag. * * This function gets the local flag; the lower layer should set this when * setting up the channel if is_local_addr() is true for all of the @@ -4204,7 +3026,6 @@ channel_mark_incoming(channel_t *chan) * used to decide whether to declare the network reachable when seeing incoming * traffic on the channel. */ - int channel_is_local(channel_t *chan) { @@ -4214,13 +3035,12 @@ channel_is_local(channel_t *chan) } /** - * Set the local flag + * Set the local flag. * * This internal-only function should be called by the lower layer if the * channel is to a local address. See channel_is_local() above or the - * description of the is_local bit in channel.h + * description of the is_local bit in channel.h. */ - void channel_mark_local(channel_t *chan) { @@ -4230,14 +3050,13 @@ channel_mark_local(channel_t *chan) } /** - * Mark a channel as remote + * Mark a channel as remote. * * This internal-only function should be called by the lower layer if the * channel is not to a local address but has previously been marked local. * See channel_is_local() above or the description of the is_local bit in * channel.h */ - void channel_mark_remote(channel_t *chan) { @@ -4247,13 +3066,12 @@ channel_mark_remote(channel_t *chan) } /** - * Test outgoing flag + * Test outgoing flag. * * This function gets the outgoing flag; this is the inverse of the incoming * bit set when a listener spawns a channel. If this returns true the channel * was locally initiated. */ - int channel_is_outgoing(channel_t *chan) { @@ -4263,12 +3081,11 @@ channel_is_outgoing(channel_t *chan) } /** - * Mark a channel as outgoing + * Mark a channel as outgoing. * * This function clears the incoming flag and thus marks a channel as * outgoing. */ - void channel_mark_outgoing(channel_t *chan) { @@ -4281,24 +3098,11 @@ channel_mark_outgoing(channel_t *chan) * Flow control queries * ***********************/ -/* - * Get the latest estimate for the total queue size of all open channels - */ - -uint64_t -channel_get_global_queue_estimate(void) -{ - return estimated_total_queue_size; -} - -/* - * Estimate the number of writeable cells +/** + * Estimate the number of writeable cells. * - * Ask the lower layer for an estimate of how many cells it can accept, and - * then subtract the length of our outgoing_queue, if any, to produce an - * estimate of the number of cells this channel can accept for writes. + * Ask the lower layer for an estimate of how many cells it can accept. */ - int channel_num_cells_writeable(channel_t *chan) { @@ -4310,8 +3114,6 @@ channel_num_cells_writeable(channel_t *chan) if (chan->state == CHANNEL_STATE_OPEN) { /* Query lower layer */ result = chan->num_cells_writeable(chan); - /* Subtract cell queue length, if any */ - result -= chan_cell_queue_len(&chan->outgoing_queue); if (result < 0) result = 0; } else { /* No cells are writeable in any other state */ @@ -4326,12 +3128,11 @@ channel_num_cells_writeable(channel_t *chan) ********************/ /** - * Update the created timestamp for a channel + * Update the created timestamp for a channel. * * This updates the channel's created timestamp and should only be called * from channel_init(). */ - void channel_timestamp_created(channel_t *chan) { @@ -4343,12 +3144,11 @@ channel_timestamp_created(channel_t *chan) } /** - * Update the created timestamp for a channel listener + * Update the created timestamp for a channel listener. * * This updates the channel listener's created timestamp and should only be * called from channel_init_listener(). */ - void channel_listener_timestamp_created(channel_listener_t *chan_l) { @@ -4360,7 +3160,7 @@ channel_listener_timestamp_created(channel_listener_t *chan_l) } /** - * Update the last active timestamp for a channel + * Update the last active timestamp for a channel. * * This function updates the channel's last active timestamp; it should be * called by the lower layer whenever there is activity on the channel which @@ -4369,25 +3169,23 @@ channel_listener_timestamp_created(channel_listener_t *chan_l) * but it should be updated for things like the v3 handshake and stuff that * produce activity only visible to the lower layer. */ - void channel_timestamp_active(channel_t *chan) { time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** - * Update the last active timestamp for a channel listener + * Update the last active timestamp for a channel listener. */ - void channel_listener_timestamp_active(channel_listener_t *chan_l) { @@ -4405,7 +3203,6 @@ channel_listener_timestamp_active(channel_listener_t *chan_l) * should be called whenever a new incoming channel is accepted on a * listener. */ - void channel_listener_timestamp_accepted(channel_listener_t *chan_l) { @@ -4418,12 +3215,11 @@ channel_listener_timestamp_accepted(channel_listener_t *chan_l) } /** - * Update client timestamp + * Update client timestamp. * * This function is called by relay.c to timestamp a channel that appears to * be used as a client. */ - void channel_timestamp_client(channel_t *chan) { @@ -4435,64 +3231,44 @@ channel_timestamp_client(channel_t *chan) } /** - * Update the last drained timestamp - * - * This is called whenever we transmit a cell which leaves the outgoing cell - * queue completely empty. It also updates the xmit time and the active time. - */ - -void -channel_timestamp_drained(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_drained = now; - chan->timestamp_xmit = now; -} - -/** - * Update the recv timestamp + * Update the recv timestamp. * * This is called whenever we get an incoming cell from the lower layer. * This also updates the active timestamp. */ - void channel_timestamp_recv(channel_t *chan) { time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_recv = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** - * Update the xmit timestamp + * Update the xmit timestamp. + * * This is called whenever we pass an outgoing cell to the lower layer. This * also updates the active timestamp. */ - void channel_timestamp_xmit(channel_t *chan) { time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_xmit = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /*************************************************************** @@ -4500,9 +3276,8 @@ channel_timestamp_xmit(channel_t *chan) **************************************************************/ /** - * Query created timestamp for a channel + * Query created timestamp for a channel. */ - time_t channel_when_created(channel_t *chan) { @@ -4512,57 +3287,8 @@ channel_when_created(channel_t *chan) } /** - * Query created timestamp for a channel listener - */ - -time_t -channel_listener_when_created(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_created; -} - -/** - * Query last active timestamp for a channel - */ - -time_t -channel_when_last_active(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_active; -} - -/** - * Query last active timestamp for a channel listener - */ - -time_t -channel_listener_when_last_active(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_active; -} - -/** - * Query last accepted timestamp for a channel listener - */ - -time_t -channel_listener_when_last_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_accepted; -} - -/** - * Query client timestamp + * Query client timestamp. */ - time_t channel_when_last_client(channel_t *chan) { @@ -4572,33 +3298,8 @@ channel_when_last_client(channel_t *chan) } /** - * Query drained timestamp - */ - -time_t -channel_when_last_drained(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_drained; -} - -/** - * Query recv timestamp - */ - -time_t -channel_when_last_recv(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_recv; -} - -/** - * Query xmit timestamp + * Query xmit timestamp. */ - time_t channel_when_last_xmit(channel_t *chan) { @@ -4608,48 +3309,11 @@ channel_when_last_xmit(channel_t *chan) } /** - * Query accepted counter - */ - -uint64_t -channel_listener_count_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->n_accepted; -} - -/** - * Query received cell counter - */ - -uint64_t -channel_count_recved(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_recved; -} - -/** - * Query transmitted cell counter - */ - -uint64_t -channel_count_xmitted(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_xmitted; -} - -/** - * Check if a channel matches an extend_info_t + * Check if a channel matches an extend_info_t. * * This function calls the lower layer and asks if this channel matches a * given extend_info_t. */ - int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) { @@ -4666,7 +3330,6 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) * This function calls into the lower layer and asks if this channel thinks * it matches a given target address for circuit extension purposes. */ - int channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target) @@ -4679,12 +3342,11 @@ channel_matches_target_addr_for_extend(channel_t *chan, } /** - * Return the total number of circuits used by a channel + * Return the total number of circuits used by a channel. * * @param chan Channel to query * @return Number of circuits using this as n_chan or p_chan */ - unsigned int channel_num_circuits(channel_t *chan) { @@ -4695,10 +3357,10 @@ channel_num_circuits(channel_t *chan) } /** - * Set up circuit ID generation + * Set up circuit ID generation. * * This is called when setting up a channel and replaces the old - * connection_or_set_circid_type() + * connection_or_set_circid_type(). */ MOCK_IMPL(void, channel_set_circid_type,(channel_t *chan, @@ -4734,6 +3396,16 @@ channel_set_circid_type,(channel_t *chan, } } +static int +channel_sort_by_ed25519_identity(const void **a_, const void **b_) +{ + const channel_t *a = *a_, + *b = *b_; + return fast_memcmp(&a->ed25519_identity.pubkey, + &b->ed25519_identity.pubkey, + sizeof(a->ed25519_identity.pubkey)); +} + /** Helper for channel_update_bad_for_new_circs(): Perform the * channel_update_bad_for_new_circs operation on all channels in <b>lst</b>, * all of which MUST have the same RSA ID. (They MAY have different @@ -4742,44 +3414,52 @@ static void channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force) { /*XXXX This function should really be about channels. 15056 */ - channel_t *chan; + channel_t *chan = TOR_LIST_FIRST(lst); + + if (!chan) + return; + + /* if there is only one channel, don't bother looping */ + if (PREDICT_LIKELY(!TOR_LIST_NEXT(chan, next_with_same_id))) { + connection_or_single_set_badness_( + time(NULL), BASE_CHAN_TO_TLS(chan)->conn, force); + return; + } + + smartlist_t *channels = smartlist_new(); - /* First, get a minimal list of the ed25519 identites */ - smartlist_t *ed_identities = smartlist_new(); TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - uint8_t *id_copy = - tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN); - smartlist_add(ed_identities, id_copy); + if (BASE_CHAN_TO_TLS(chan)->conn) { + smartlist_add(channels, chan); + } } - smartlist_sort_digests256(ed_identities); - smartlist_uniq_digests256(ed_identities); - /* Now, for each Ed identity, build a smartlist and find the best entry on - * it. */ + smartlist_sort(channels, channel_sort_by_ed25519_identity); + + const ed25519_public_key_t *common_ed25519_identity = NULL; + /* it would be more efficient to do a slice, but this case is rare */ smartlist_t *or_conns = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) { - TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan); - if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN)) - continue; - or_connection_t *orconn = chantls->conn; - if (orconn) { - tor_assert(orconn->chan == chantls); - smartlist_add(or_conns, orconn); - } + SMARTLIST_FOREACH_BEGIN(channels, channel_t *, channel) { + if (!common_ed25519_identity) + common_ed25519_identity = &channel->ed25519_identity; + + if (! ed25519_pubkey_eq(&channel->ed25519_identity, + common_ed25519_identity)) { + connection_or_group_set_badness_(or_conns, force); + smartlist_clear(or_conns); + common_ed25519_identity = &channel->ed25519_identity; } - connection_or_group_set_badness_(or_conns, force); - smartlist_clear(or_conns); - } SMARTLIST_FOREACH_END(ed_id); + smartlist_add(or_conns, BASE_CHAN_TO_TLS(channel)->conn); + } SMARTLIST_FOREACH_END(channel); + + connection_or_group_set_badness_(or_conns, force); /* XXXX 15056 we may want to do something special with connections that have * no set Ed25519 identity! */ smartlist_free(or_conns); - - SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id)); - smartlist_free(ed_identities); + smartlist_free(channels); } /** Go through all the channels (or if <b>digest</b> is non-NULL, just @@ -4809,83 +3489,3 @@ channel_update_bad_for_new_circs(const char *digest, int force) } } -/** - * Update the estimated number of bytes queued to transmit for this channel, - * and notify the scheduler. The estimate includes both the channel queue and - * the queue size reported by the lower layer, and an overhead estimate - * optionally provided by the lower layer. - */ - -void -channel_update_xmit_queue_size(channel_t *chan) -{ - uint64_t queued, adj; - double overhead; - - tor_assert(chan); - tor_assert(chan->num_bytes_queued); - - /* - * First, get the number of bytes we have queued without factoring in - * lower-layer overhead. - */ - queued = chan->num_bytes_queued(chan) + chan->bytes_in_queue; - /* Next, adjust by the overhead factor, if any is available */ - if (chan->get_overhead_estimate) { - overhead = chan->get_overhead_estimate(chan); - if (overhead >= 1.0) { - queued = (uint64_t)(queued * overhead); - } else { - /* Ignore silly overhead factors */ - log_notice(LD_CHANNEL, "Ignoring silly overhead factor %f", overhead); - } - } - - /* Now, compare to the previous estimate */ - if (queued > chan->bytes_queued_for_xmit) { - adj = queued - chan->bytes_queued_for_xmit; - log_debug(LD_CHANNEL, - "Increasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size += adj; - log_debug(LD_CHANNEL, - "Increasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } else if (queued < chan->bytes_queued_for_xmit) { - adj = chan->bytes_queued_for_xmit - queued; - log_debug(LD_CHANNEL, - "Decreasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size -= adj; - log_debug(LD_CHANNEL, - "Decreasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } -} - diff --git a/src/or/channel.h b/src/or/channel.h index 074cc86fe7..0af5aed414 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -19,10 +19,6 @@ typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); -struct cell_queue_entry_s; -TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s); -typedef struct chan_cell_queue chan_cell_queue_t; - /** * This enum is used by channelpadding to decide when to pad channels. * Don't add values to it without updating the checks in @@ -42,7 +38,6 @@ typedef enum { * to a particular node, and once constructed support the abstract operations * defined below. */ - struct channel_s { /** Magic number for type-checking cast macros */ uint32_t magic; @@ -92,10 +87,9 @@ struct channel_s { * Used to decide what channels to pad, and when. */ channel_usage_info_t channel_usage; - /** When should we send a cell for netflow padding, in absolute - * milliseconds since monotime system start. 0 means no padding - * is scheduled. */ - uint64_t next_padding_time_ms; + /** When should we send a cell for netflow padding? 0 means no padding is + * scheduled. */ + monotime_coarse_t next_padding_time; /** The callback pointer for the padding callbacks */ tor_timer_t *padding_timer; @@ -162,7 +156,7 @@ struct channel_s { time_t timestamp_active; /* Any activity */ /** - * This is a high-resolution monotonic timestamp that marks when we + * This is a monotonic timestamp that marks when we * believe the channel has actually sent or received data to/from * the wire. Right now, it is used to determine when we should send * a padding cell for channelpadding. @@ -171,7 +165,7 @@ struct channel_s { * accurately reflect actual network data transfer? Or might this be * very wrong wrt when bytes actually go on the wire? */ - uint64_t timestamp_xfer_ms; + monotime_coarse_t timestamp_xfer; /* Methods implemented by the lower layer */ @@ -259,21 +253,12 @@ struct channel_s { */ ed25519_public_key_t ed25519_identity; - /** Nickname of the OR on the other side, or NULL if none. */ - char *nickname; - /** * Linked list of channels with the same RSA identity digest, for use with * the digest->channel map */ TOR_LIST_ENTRY(channel_s) next_with_same_id; - /** List of incoming cells to handle */ - chan_cell_queue_t incoming_queue; - - /** List of queued outgoing cells */ - chan_cell_queue_t outgoing_queue; - /** Circuit mux for circuits sending on this channel */ circuitmux_t *cmux; @@ -320,7 +305,6 @@ struct channel_s { /** Channel timestamps for cell channels */ time_t timestamp_client; /* Client used this, according to relay.c */ - time_t timestamp_drained; /* Output queue empty */ time_t timestamp_recv; /* Cell received from lower layer */ time_t timestamp_xmit; /* Cell sent to lower layer */ @@ -337,14 +321,6 @@ struct channel_s { /** Channel counters for cell channels */ uint64_t n_cells_recved, n_bytes_recved; uint64_t n_cells_xmitted, n_bytes_xmitted; - - /** Our current contribution to the scheduler's total xmit queue */ - uint64_t bytes_queued_for_xmit; - - /** Number of bytes in this channel's cell queue; does not include - * lower-layer queueing. - */ - uint64_t bytes_in_queue; }; struct channel_listener_s { @@ -412,18 +388,13 @@ channel_listener_state_to_string(channel_listener_state_t state); /* Abstract channel operations */ void channel_mark_for_close(channel_t *chan); -void channel_write_cell(channel_t *chan, cell_t *cell); -void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); -void channel_write_var_cell(channel_t *chan, var_cell_t *cell); +int channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); void channel_listener_mark_for_close(channel_listener_t *chan_l); /* Channel callback registrations */ /* Listener callback */ -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan); - void channel_listener_set_listener_fn(channel_listener_t *chan, channel_listener_fn_ptr listener); @@ -457,36 +428,9 @@ void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol); #ifdef TOR_CHANNEL_INTERNAL_ #ifdef CHANNEL_PRIVATE_ -/* Cell queue structure (here rather than channel.c for test suite use) */ -typedef struct cell_queue_entry_s cell_queue_entry_t; -struct cell_queue_entry_s { - TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next; - enum { - CELL_QUEUE_FIXED, - CELL_QUEUE_VAR, - CELL_QUEUE_PACKED - } type; - union { - struct { - cell_t *cell; - } fixed; - struct { - var_cell_t *var_cell; - } var; - struct { - packed_cell_t *packed_cell; - } packed; - } u; -}; - -/* Cell queue functions for benefit of test suite */ -STATIC int chan_cell_queue_len(const chan_cell_queue_t *queue); - -STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); +STATIC void channel_add_to_digest_map(channel_t *chan); -void channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q); #endif /* defined(CHANNEL_PRIVATE_) */ /* Channel operations for subclasses and internal use only */ @@ -511,13 +455,12 @@ void channel_close_from_lower_layer(channel_t *chan); void channel_close_for_error(channel_t *chan); void channel_closed(channel_t *chan); -void channel_listener_close_from_lower_layer(channel_listener_t *chan_l); -void channel_listener_close_for_error(channel_listener_t *chan_l); -void channel_listener_closed(channel_listener_t *chan_l); - /* Free a channel */ -void channel_free(channel_t *chan); -void channel_listener_free(channel_listener_t *chan_l); +void channel_free_(channel_t *chan); +#define channel_free(chan) FREE_AND_NULL(channel_t, channel_free_, (chan)) +void channel_listener_free_(channel_listener_t *chan_l); +#define channel_listener_free(chan_l) \ + FREE_AND_NULL(channel_listener_t, channel_listener_free_, (chan_l)) /* State/metadata setters */ @@ -532,9 +475,6 @@ void channel_mark_remote(channel_t *chan); void channel_set_identity_digest(channel_t *chan, const char *identity_digest, const ed25519_public_key_t *ed_identity); -void channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname); void channel_listener_change_state(channel_listener_t *chan_l, channel_listener_state_t to_state); @@ -542,7 +482,6 @@ void channel_listener_change_state(channel_listener_t *chan_l, /* Timestamp updates */ void channel_timestamp_created(channel_t *chan); void channel_timestamp_active(channel_t *chan); -void channel_timestamp_drained(channel_t *chan); void channel_timestamp_recv(channel_t *chan); void channel_timestamp_xmit(channel_t *chan); @@ -556,12 +495,7 @@ void channel_listener_queue_incoming(channel_listener_t *listener, channel_t *incoming); /* Incoming cell handling */ -void channel_process_cells(channel_t *chan); -void channel_queue_cell(channel_t *chan, cell_t *cell); -void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell); - -/* Outgoing cell handling */ -void channel_flush_cells(channel_t *chan); +void channel_process_cell(channel_t *chan, cell_t *cell); /* Request from lower layer for more cells if available */ MOCK_DECL(ssize_t, channel_flush_some_cells, @@ -576,10 +510,6 @@ void channel_notify_flushed(channel_t *chan); /* Handle stuff we need to do on open like notifying circuits */ void channel_do_open_actions(channel_t *chan); -#ifdef TOR_UNIT_TESTS -extern uint64_t estimated_total_queue_size; -#endif - #endif /* defined(TOR_CHANNEL_INTERNAL_) */ /* Helper functions to perform operations on channels */ @@ -681,7 +611,6 @@ MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, crypto_pk_t *identity_rcvd, int consider_identity)); void channel_timestamp_client(channel_t *chan); -void channel_update_xmit_queue_size(channel_t *chan); const char * channel_listener_describe_transport(channel_listener_t *chan_l); void channel_listener_dump_statistics(channel_listener_t *chan_l, @@ -693,33 +622,22 @@ void channel_check_for_duplicates(void); void channel_update_bad_for_new_circs(const char *digest, int force); /* Flow control queries */ -uint64_t channel_get_global_queue_estimate(void); int channel_num_cells_writeable(channel_t *chan); /* Timestamp queries */ time_t channel_when_created(channel_t *chan); -time_t channel_when_last_active(channel_t *chan); time_t channel_when_last_client(channel_t *chan); -time_t channel_when_last_drained(channel_t *chan); -time_t channel_when_last_recv(channel_t *chan); time_t channel_when_last_xmit(channel_t *chan); -time_t channel_listener_when_created(channel_listener_t *chan_l); -time_t channel_listener_when_last_active(channel_listener_t *chan_l); -time_t channel_listener_when_last_accepted(channel_listener_t *chan_l); - /* Counter queries */ -uint64_t channel_count_recved(channel_t *chan); -uint64_t channel_count_xmitted(channel_t *chan); - -uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); - int packed_cell_is_destroy(channel_t *chan, const packed_cell_t *packed_cell, circid_t *circid_out); /* Declare the handle helpers */ HANDLE_DECL(channel, channel_s,) +#define channel_handle_free(h) \ + FREE_AND_NULL(channel_handle_t, channel_handle_free_, (h)) #endif /* !defined(TOR_CHANNEL_H) */ diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c index 435436c45c..5da3009e67 100644 --- a/src/or/channelpadding.c +++ b/src/or/channelpadding.c @@ -23,7 +23,8 @@ #include <event2/event.h> #include "rendservice.h" -STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *); +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms( + const channel_t *); STATIC int channelpadding_send_disable_command(channel_t *); STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *); @@ -68,7 +69,7 @@ static int consensus_nf_pad_single_onion; /** * This macro tells us if either end of the channel is connected to a client. * (If we're not a server, we're definitely a client. If the channel thinks - * its a client, use that. Then finally verify in the consensus). + * it's a client, use that. Then finally verify in the consensus). */ #define CHANNEL_IS_CLIENT(chan, options) \ (!public_server_mode((options)) || channel_is_client(chan) || \ @@ -165,7 +166,7 @@ channelpadding_new_consensus_params(networkstatus_t *ns) * Returns the next timeout period (in milliseconds) after which we should * send a padding packet, or 0 if padding is disabled. */ -STATIC int +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan) { int low_timeout = consensus_nf_ito_low; @@ -377,15 +378,16 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) chan->pending_padding_callback = 0; - if (!chan->next_padding_time_ms || + if (monotime_coarse_is_zero(&chan->next_padding_time) || chan->has_queued_writes(chan)) { /* We must have been active before the timer fired */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); return; } { - uint64_t now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); log_fn(LOG_INFO,LD_OR, "Sending netflow keepalive on "U64_FORMAT" to %s (%s) after " @@ -393,12 +395,13 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) U64_PRINTF_ARG(chan->global_identifier), safe_str_client(chan->get_remote_descr(chan, 0)), safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)), - U64_PRINTF_ARG(now - chan->timestamp_xfer_ms), - U64_PRINTF_ARG(now - chan->next_padding_time_ms)); + I64_PRINTF_ARG(monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)), + I64_PRINTF_ARG( + monotime_coarse_diff_msec(&chan->next_padding_time,&now))); } /* Clear the timer */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); /* Send the padding cell. This will cause the channel to get a * fresh timestamp_active */ @@ -500,38 +503,42 @@ channelpadding_schedule_padding(channel_t *chan, int in_ms) STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) { - uint64_t long_now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); - if (!chan->next_padding_time_ms) { + if (monotime_coarse_is_zero(&chan->next_padding_time)) { /* If the below line or crypto_rand_int() shows up on a profile, * we can avoid getting a timeout until we're at least nf_ito_lo * from a timeout window. That will prevent us from setting timers * on connections that were active up to 1.5 seconds ago. * Idle connections should only call this once every 5.5s on average * though, so that might be a micro-optimization for little gain. */ - int64_t padding_timeout = + int32_t padding_timeout = channelpadding_get_netflow_inactive_timeout_ms(chan); if (!padding_timeout) return CHANNELPADDING_TIME_DISABLED; - chan->next_padding_time_ms = padding_timeout - + chan->timestamp_xfer_ms; + monotime_coarse_add_msec(&chan->next_padding_time, + &chan->timestamp_xfer, + padding_timeout); } + const int64_t ms_till_pad = + monotime_coarse_diff_msec(&now, &chan->next_padding_time); + /* If the next padding time is beyond the maximum possible consensus value, * then this indicates a clock jump, so just send padding now. This is * better than using monotonic time because we want to avoid the situation * where we wait around forever for monotonic time to move forward after * a clock jump far into the past. */ - if (chan->next_padding_time_ms > long_now + - DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { + if (ms_till_pad > DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { tor_fragile_assert(); log_warn(LD_BUG, "Channel padding timeout scheduled "I64_FORMAT"ms in the future. " "Did the monotonic clock just jump?", - I64_PRINTF_ARG(chan->next_padding_time_ms - long_now)); + I64_PRINTF_ARG(ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } @@ -540,11 +547,8 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) from now which we should send padding, so we can schedule a callback then. */ - if (long_now + - (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC) - >= chan->next_padding_time_ms) { - int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms - - long_now; + if (ms_till_pad < (TOR_HOUSEKEEPING_CALLBACK_MSEC + + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)) { /* If the padding time is in the past, that means that libevent delayed * calling the once-per-second callback due to other work taking too long. * See https://bugs.torproject.org/22212 and @@ -554,16 +558,16 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) * and allowed a router to emit a netflow frame, just so we don't forget * about it entirely.. */ #define NETFLOW_MISSED_WINDOW (150000 - DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH) - if (ms_until_pad_for_netflow < 0) { - int severity = (ms_until_pad_for_netflow < -NETFLOW_MISSED_WINDOW) + if (ms_till_pad < 0) { + int severity = (ms_till_pad < -NETFLOW_MISSED_WINDOW) ? LOG_NOTICE : LOG_INFO; log_fn(severity, LD_OR, "Channel padding timeout scheduled "I64_FORMAT"ms in the past. ", - I64_PRINTF_ARG(-ms_until_pad_for_netflow)); + I64_PRINTF_ARG(-ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } - return ms_until_pad_for_netflow; + return ms_till_pad; } return CHANNELPADDING_TIME_LATER; } diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 0244871548..9000703b01 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -34,7 +34,6 @@ * Define this so channel.h gives us things only channel_t subclasses * should touch. */ - #define TOR_CHANNEL_INTERNAL_ #define CHANNELTLS_PRIVATE @@ -132,7 +131,6 @@ static void channel_tls_process_padding_negotiate_cell(cell_t *cell, * Do parts of channel_tls_t initialization common to channel_tls_connect() * and channel_tls_handle_incoming(). */ - STATIC void channel_tls_common_init(channel_tls_t *tlschan) { @@ -168,13 +166,12 @@ channel_tls_common_init(channel_tls_t *tlschan) } /** - * Start a new TLS channel + * Start a new TLS channel. * * Launch a new OR connection to <b>addr</b>:<b>port</b> and expect to * handshake with an OR with identity digest <b>id_digest</b>, and wrap * it in a channel_tls_t. */ - channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, const char *id_digest, @@ -233,12 +230,11 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, } /** - * Return the current channel_tls_t listener + * Return the current channel_tls_t listener. * * Returns the current channel listener for incoming TLS connections, or * NULL if none has been established */ - channel_listener_t * channel_tls_get_listener(void) { @@ -246,12 +242,11 @@ channel_tls_get_listener(void) } /** - * Start a channel_tls_t listener if necessary + * Start a channel_tls_t listener if necessary. * * Return the current channel_tls_t listener, or start one if we haven't yet, * and return that. */ - channel_listener_t * channel_tls_start_listener(void) { @@ -278,12 +273,11 @@ channel_tls_start_listener(void) } /** - * Free everything on shutdown + * Free everything on shutdown. * * Not much to do here, since channel_free_all() takes care of a lot, but let's * get rid of the listener. */ - void channel_tls_free_all(void) { @@ -314,9 +308,8 @@ channel_tls_free_all(void) } /** - * Create a new channel around an incoming or_connection_t + * Create a new channel around an incoming or_connection_t. */ - channel_t * channel_tls_handle_incoming(or_connection_t *orconn) { @@ -359,7 +352,6 @@ channel_tls_handle_incoming(or_connection_t *orconn) /** * Cast a channel_tls_t to a channel_t. */ - channel_t * channel_tls_to_base(channel_tls_t *tlschan) { @@ -372,7 +364,6 @@ channel_tls_to_base(channel_tls_t *tlschan) * Cast a channel_t to a channel_tls_t, with appropriate type-checking * asserts. */ - channel_tls_t * channel_tls_from_base(channel_t *chan) { @@ -388,11 +379,10 @@ channel_tls_from_base(channel_t *chan) *******************************************/ /** - * Close a channel_tls_t + * Close a channel_tls_t. * - * This implements the close method for channel_tls_t + * This implements the close method for channel_tls_t. */ - static void channel_tls_close_method(channel_t *chan) { @@ -411,12 +401,11 @@ channel_tls_close_method(channel_t *chan) } /** - * Describe the transport for a channel_tls_t + * Describe the transport for a channel_tls_t. * * This returns the string "TLS channel on connection <id>" to the upper * layer. */ - static const char * channel_tls_describe_transport_method(channel_t *chan) { @@ -446,7 +435,7 @@ channel_tls_describe_transport_method(channel_t *chan) } /** - * Free a channel_tls_t + * Free a channel_tls_t. * * This is called by the generic channel layer when freeing a channel_tls_t; * this happens either on a channel which has already reached @@ -455,7 +444,6 @@ channel_tls_describe_transport_method(channel_t *chan) * have an orconn active (which connection_free_all() will get to later), * so we should null out its channel pointer now. */ - static void channel_tls_free_method(channel_t *chan) { @@ -470,9 +458,8 @@ channel_tls_free_method(channel_t *chan) } /** - * Get an estimate of the average TLS overhead for the upper layer + * Get an estimate of the average TLS overhead for the upper layer. */ - static double channel_tls_get_overhead_estimate_method(channel_t *chan) { @@ -505,13 +492,12 @@ channel_tls_get_overhead_estimate_method(channel_t *chan) } /** - * Get the remote address of a channel_tls_t + * Get the remote address of a channel_tls_t. * * This implements the get_remote_addr method for channel_tls_t; copy the * remote endpoint of the channel to addr_out and return 1 (always * succeeds for this transport). */ - static int channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) { @@ -535,8 +521,8 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) * This implements the get_transport_name for channel_tls_t. If the * channel uses a pluggable transport, copy its name to * <b>transport_out</b> and return 0. If the channel did not use a - * pluggable transport, return -1. */ - + * pluggable transport, return -1. + */ static int channel_tls_get_transport_name_method(channel_t *chan, char **transport_out) { @@ -554,14 +540,13 @@ channel_tls_get_transport_name_method(channel_t *chan, char **transport_out) } /** - * Get endpoint description of a channel_tls_t + * Get endpoint description of a channel_tls_t. * * This implements the get_remote_descr method for channel_tls_t; it returns * a text description of the remote endpoint of the channel suitable for use - * in log messages. The req parameter is 0 for the canonical address or 1 for + * in log messages. The req parameter is 0 for the canonical address or 1 for * the actual address seen. */ - static const char * channel_tls_get_remote_descr_method(channel_t *chan, int flags) { @@ -617,12 +602,11 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags) } /** - * Tell the upper layer if we have queued writes + * Tell the upper layer if we have queued writes. * * This implements the has_queued_writes method for channel_tls t_; it returns * 1 iff we have queued writes on the outbuf of the underlying or_connection_t. */ - static int channel_tls_has_queued_writes_method(channel_t *chan) { @@ -645,13 +629,12 @@ channel_tls_has_queued_writes_method(channel_t *chan) } /** - * Tell the upper layer if we're canonical + * Tell the upper layer if we're canonical. * * This implements the is_canonical method for channel_tls_t; if req is zero, * it returns whether this is a canonical channel, and if it is one it returns * whether that can be relied upon. */ - static int channel_tls_is_canonical_method(channel_t *chan, int req) { @@ -684,12 +667,11 @@ channel_tls_is_canonical_method(channel_t *chan, int req) } /** - * Check if we match an extend_info_t + * Check if we match an extend_info_t. * * This implements the matches_extend_info method for channel_tls_t; the upper * layer wants to know if this channel matches an extend_info_t. */ - static int channel_tls_matches_extend_info_method(channel_t *chan, extend_info_t *extend_info) @@ -720,7 +702,6 @@ channel_tls_matches_extend_info_method(channel_t *chan, * layer wants to know if this channel matches a target address when extending * a circuit. */ - static int channel_tls_matches_target_method(channel_t *chan, const tor_addr_t *target) @@ -755,7 +736,6 @@ channel_tls_matches_target_method(channel_t *chan, * Tell the upper layer how many bytes we have queued and not yet * sent. */ - static size_t channel_tls_num_bytes_queued_method(channel_t *chan) { @@ -768,13 +748,12 @@ channel_tls_num_bytes_queued_method(channel_t *chan) } /** - * Tell the upper layer how many cells we can accept to write + * Tell the upper layer how many cells we can accept to write. * * This implements the num_cells_writeable method for channel_tls_t; it * returns an estimate of the number of cells we can accept with * channel_tls_write_*_cell(). */ - static int channel_tls_num_cells_writeable_method(channel_t *chan) { @@ -799,12 +778,11 @@ channel_tls_num_cells_writeable_method(channel_t *chan) } /** - * Write a cell to a channel_tls_t + * Write a cell to a channel_tls_t. * * This implements the write_cell method for channel_tls_t; given a * channel_tls_t and a cell_t, transmit the cell_t. */ - static int channel_tls_write_cell_method(channel_t *chan, cell_t *cell) { @@ -828,12 +806,14 @@ channel_tls_write_cell_method(channel_t *chan, cell_t *cell) } /** - * Write a packed cell to a channel_tls_t + * Write a packed cell to a channel_tls_t. * * This implements the write_packed_cell method for channel_tls_t; given a * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. + * + * Return 0 on success or negative value on error. The caller must free the + * packed cell. */ - static int channel_tls_write_packed_cell_method(channel_t *chan, packed_cell_t *packed_cell) @@ -841,7 +821,6 @@ channel_tls_write_packed_cell_method(channel_t *chan, tor_assert(chan); channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids); - int written = 0; tor_assert(tlschan); tor_assert(packed_cell); @@ -849,27 +828,23 @@ channel_tls_write_packed_cell_method(channel_t *chan, if (tlschan->conn) { connection_buf_add(packed_cell->body, cell_network_size, TO_CONN(tlschan->conn)); - - /* This is where the cell is finished; used to be done from relay.c */ - packed_cell_free(packed_cell); - ++written; } else { log_info(LD_CHANNEL, "something called write_packed_cell on a tlschan " "(%p with ID " U64_FORMAT " but no conn", chan, U64_PRINTF_ARG(chan->global_identifier)); + return -1; } - return written; + return 0; } /** - * Write a variable-length cell to a channel_tls_t + * Write a variable-length cell to a channel_tls_t. * * This implements the write_var_cell method for channel_tls_t; given a * channel_tls_t and a var_cell_t, transmit the var_cell_t. */ - static int channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) { @@ -897,11 +872,10 @@ channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) ************************************************/ /** - * Close a channel_listener_t + * Close a channel_listener_t. * - * This implements the close method for channel_listener_t + * This implements the close method for channel_listener_t. */ - static void channel_tls_listener_close_method(channel_listener_t *chan_l) { @@ -937,12 +911,11 @@ channel_tls_listener_close_method(channel_listener_t *chan_l) } /** - * Describe the transport for a channel_listener_t + * Describe the transport for a channel_listener_t. * * This returns the string "TLS channel (listening)" to the upper * layer. */ - static const char * channel_tls_listener_describe_transport_method(channel_listener_t *chan_l) { @@ -956,12 +929,11 @@ channel_tls_listener_describe_transport_method(channel_listener_t *chan_l) ******************************************************/ /** - * Handle an orconn state change + * Handle an orconn state change. * * This function will be called by connection_or.c when the or_connection_t * associated with this channel_tls_t changes state. */ - void channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, or_connection_t *conn, @@ -1012,13 +984,12 @@ channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, #ifdef KEEP_TIMING_STATS /** - * Timing states wrapper + * Timing states wrapper. * * This is a wrapper function around the actual function that processes the * <b>cell</b> that just arrived on <b>chan</b>. Increment <b>*time</b> * by the number of microseconds used by the call to <b>*func(cell, chan)</b>. */ - static void channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, void (*func)(cell_t *, channel_tls_t *)) @@ -1047,7 +1018,7 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, #endif /* defined(KEEP_TIMING_STATS) */ /** - * Handle an incoming cell on a channel_tls_t + * Handle an incoming cell on a channel_tls_t. * * This is called from connection_or.c to handle an arriving cell; it checks * for cell types specific to the handshake for this transport protocol and @@ -1059,7 +1030,6 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, * for copying in the case that it queues; we merely pass pointers through * which we get from connection_or_process_cells_from_inbuf(). */ - void channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) { @@ -1149,7 +1119,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * These are all transport independent and we pass them up through the * channel_t mechanism. They are ultimately handled in command.c. */ - channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell); + channel_process_cell(TLS_CHAN_TO_BASE(chan), cell); break; default: log_fn(LOG_INFO, LD_PROTOCOL, @@ -1161,7 +1131,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) } /** - * Handle an incoming variable-length cell on a channel_tls_t + * Handle an incoming variable-length cell on a channel_tls_t. * * Process a <b>var_cell</b> that was just received on <b>conn</b>. Keep * internal statistics about how many of each cell we've processed so far @@ -1177,7 +1147,6 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * caller always frees them after this function returns, so this function * should never free var_cell. */ - void channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) { @@ -1342,7 +1311,7 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) } /** - * Update channel marks after connection_or.c has changed an address + * Update channel marks after connection_or.c has changed an address. * * This is called from connection_or_init_conn_from_address() after the * connection's _base.addr or real_addr fields have potentially been changed @@ -1351,7 +1320,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) * remote router by looking it up in the consensus after we finish the * handshake and know an authenticated identity digest. */ - void channel_tls_update_marks(or_connection_t *conn) { @@ -1380,12 +1348,11 @@ channel_tls_update_marks(or_connection_t *conn) } /** - * Check if this cell type is allowed before the handshake is finished + * Check if this cell type is allowed before the handshake is finished. * * Return true if <b>command</b> is a cell command that's allowed to start a * V3 handshake. */ - static int command_allowed_before_handshake(uint8_t command) { @@ -1400,14 +1367,13 @@ command_allowed_before_handshake(uint8_t command) } /** - * Start a V3 handshake on an incoming connection + * Start a V3 handshake on an incoming connection. * * Called when we as a server receive an appropriate cell while waiting * either for a cell or a TLS handshake. Set the connection's state to * "handshaking_v3', initializes the or_handshake_state field as needed, * and add the cell to the hash of incoming cells.) */ - static int enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) { @@ -1448,7 +1414,6 @@ enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) * we support, pick the highest version we have in common, and continue the * negotiation from there. */ - static void channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) { @@ -1589,7 +1554,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) } } - /* We set this after sending the verions cell. */ + /* We set this after sending the versions cell. */ /*XXXXX symbolic const.*/ TLS_CHAN_TO_BASE(chan)->wide_circ_ids = chan->conn->link_proto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS; @@ -1623,7 +1588,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) } /** - * Process a 'padding_negotiate' cell + * Process a 'padding_negotiate' cell. * * This function is called to handle an incoming PADDING_NEGOTIATE cell; * enable or disable padding accordingly, and read and act on its timeout @@ -1660,12 +1625,11 @@ channel_tls_process_padding_negotiate_cell(cell_t *cell, channel_tls_t *chan) } /** - * Process a 'netinfo' cell + * Process a 'netinfo' cell. * * This function is called to handle an incoming NETINFO cell; read and act * on its contents, and set the connection state to "open". */ - static void channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) { @@ -2168,7 +2132,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } /** - * Process an AUTH_CHALLENGE cell from a channel_tls_t + * Process an AUTH_CHALLENGE cell from a channel_tls_t. * * This function is called to handle an incoming AUTH_CHALLENGE cell on a * channel_tls_t; if we weren't supposed to get one (for example, because we're @@ -2177,7 +2141,6 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * want to authenticate, just drop it. If the cell is well-formed *and* we * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ - STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) { @@ -2271,7 +2234,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) } /** - * Process an AUTHENTICATE cell from a channel_tls_t + * Process an AUTHENTICATE cell from a channel_tls_t. * * If it's ill-formed or we weren't supposed to get one or we're not doing a * v3 handshake, then mark the connection. If it does not authenticate the @@ -2279,7 +2242,6 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept * the identity of the router on the other side of the connection. */ - STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) { diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c index f4bd5ea2fa..c1c1ca31be 100644 --- a/src/or/circpathbias.c +++ b/src/or/circpathbias.c @@ -1467,7 +1467,7 @@ pathbias_measure_close_rate(entry_guard_t *guard) * * XXX: The attempt count transfer stuff here might be done * better by keeping separate pending counters that get - * transfered at circuit close. See ticket #8160. + * transferred at circuit close. See ticket #8160. */ static void pathbias_scale_close_rates(entry_guard_t *guard) @@ -1527,7 +1527,7 @@ pathbias_scale_close_rates(entry_guard_t *guard) * * XXX: The attempt count transfer stuff here might be done * better by keeping separate pending counters that get - * transfered at circuit close. See ticket #8160. + * transferred at circuit close. See ticket #8160. */ void pathbias_scale_use_rates(entry_guard_t *guard) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b36fed63b3..8fe6ba0e60 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -75,11 +75,15 @@ static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit, int is_hs_v3_rp_circuit); static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); -static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ, crypt_path_t *hop); +static const node_t *choose_good_middle_server(uint8_t purpose, + cpath_build_state_t *state, + crypt_path_t *head, + int cur_len); /** This function tries to get a channel to the specified endpoint, * and then calls command_setup_channel() to give it the right @@ -343,45 +347,6 @@ circuit_log_path(int severity, unsigned int domain, origin_circuit_t *circ) tor_free(s); } -/** Tell the rep(utation)hist(ory) module about the status of the links - * in <b>circ</b>. Hops that have become OPEN are marked as successfully - * extended; the _first_ hop that isn't open (if any) is marked as - * unable to extend. - */ -/* XXXX Someday we should learn from OR circuits too. */ -void -circuit_rep_hist_note_result(origin_circuit_t *circ) -{ - crypt_path_t *hop; - const char *prev_digest = NULL; - hop = circ->cpath; - if (!hop) /* circuit hasn't started building yet. */ - return; - if (server_mode(get_options())) { - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return; - prev_digest = me->cache_info.identity_digest; - } - do { - const node_t *node = node_get_by_id(hop->extend_info->identity_digest); - if (node) { /* Why do we check this? We know the identity. -NM XXXX */ - if (prev_digest) { - if (hop->state == CPATH_STATE_OPEN) - rep_hist_note_extend_succeeded(prev_digest, node->identity); - else { - rep_hist_note_extend_failed(prev_digest, node->identity); - break; - } - } - prev_digest = node->identity; - } else { - prev_digest = NULL; - } - hop=hop->next; - } while (hop!=circ->cpath); -} - /** Return 1 iff every node in circ's cpath definitely supports ntor. */ static int circuit_cpath_supports_ntor(const origin_circuit_t *circ) @@ -631,8 +596,7 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits) tor_assert(chan); - log_debug(LD_CIRC,"chan to %s/%s, status=%d", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"chan to %s, status=%d", channel_get_canonical_remote_descr(chan), status); pending_circs = smartlist_new(); @@ -825,16 +789,25 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) return networkstatus_get_param(NULL, "usecreatefast", 0, 0, 1); } -/** Return true if <b>circ</b> is the type of circuit we want to count - * timeouts from. In particular, we want it to have not completed yet - * (already completing indicates we cannibalized it), and we want it to - * have exactly three hops. +/** + * Return true if <b>circ</b> is the type of circuit we want to count + * timeouts from. + * + * In particular, we want to consider any circuit that plans to build + * at least 3 hops (but maybe more), but has 3 or fewer hops built + * so far. + * + * We still want to consider circuits before 3 hops, because we need + * to decide if we should convert them to a measurement circuit in + * circuit_build_times_handle_completed_hop(), rather than letting + * slow circuits get killed right away. */ int -circuit_timeout_want_to_count_circ(origin_circuit_t *circ) +circuit_timeout_want_to_count_circ(const origin_circuit_t *circ) { return !circ->has_opened - && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; + && circ->build_state->desired_path_len >= DEFAULT_ROUTE_LEN + && circuit_get_cpath_opened_len(circ) <= DEFAULT_ROUTE_LEN; } /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> @@ -938,7 +911,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) tor_assert(circ->cpath->state == CPATH_STATE_OPEN); tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING); + crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath); + circuit_build_times_handle_completed_hop(circ); if (hop) { /* Case two: we're on a hop after the first. */ @@ -1053,38 +1028,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ) * I think I got them right, but more checking would be wise. -NM */ - if (circuit_timeout_want_to_count_circ(circ)) { - struct timeval end; - long timediff; - tor_gettimeofday(&end); - timediff = tv_mdiff(&circ->base_.timestamp_began, &end); - - /* - * If the circuit build time is much greater than we would have cut - * it off at, we probably had a suspend event along this codepath, - * and we should discard the value. - */ - if (timediff < 0 || - timediff > 2*get_circuit_build_close_time_ms()+1000) { - log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " - "Assuming clock jump. Purpose %d (%s)", timediff, - circ->base_.purpose, - circuit_purpose_to_string(circ->base_.purpose)); - } else if (!circuit_build_times_disabled(get_options())) { - /* Only count circuit times if the network is live */ - if (circuit_build_times_network_check_live( - get_circuit_build_times())) { - circuit_build_times_add_time(get_circuit_build_times_mutable(), - (build_time_t)timediff); - circuit_build_times_set_timeout(get_circuit_build_times_mutable()); - } - - if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - circuit_build_times_network_circ_success( - get_circuit_build_times_mutable()); - } - } - } log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); @@ -1093,7 +1036,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ) } pathbias_count_build_success(circ); - circuit_rep_hist_note_result(circ); if (is_usable_for_streams) circuit_has_opened(circ); /* do other actions as necessary */ @@ -1290,7 +1232,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) const node_t *node = node_get_by_id((const char*)ec.node_id); const ed25519_public_key_t *node_ed_id = NULL; if (node && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && (node_ed_id = node_get_ed25519_id(node))) { ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id); } @@ -1675,12 +1617,49 @@ onionskin_answer(or_circuit_t *circ, * new_route_len()) in the one-hop tunnel case, so we don't need to * handle that. */ -static int +int route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei) { int routelen = DEFAULT_ROUTE_LEN; int known_purpose = 0; + if (circuit_should_use_vanguards(purpose)) { + /* Clients want an extra hop for rends to avoid linkability. + * Services want it for intro points to avoid publishing their + * layer3 guards. They want it for hsdir posts to use + * their full layer3 guard set for those connections. + * Ex: C - G - L2 - L3 - R + * S - G - L2 - L3 - HSDIR + * S - G - L2 - L3 - I + */ + if (purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || + purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || + purpose == CIRCUIT_PURPOSE_HS_VANGUARDS || + purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) + return routelen+1; + + /* If we only have Layer2 vanguards, then we do not need + * the extra hop for linkabilty reasons (see below). + * This means all hops can be of the form: + * S/C - G - L2 - M - R/HSDir/I + */ + if (get_options()->HSLayer2Nodes && !get_options()->HSLayer3Nodes) + return routelen+1; + + /* For connections to hsdirs, clients want two extra hops + * when using layer3 guards, to avoid linkability. + * Same goes for intro points. Note that the route len + * includes the intro point or hsdir, hence the +2. + * Ex: C - G - L2 - L3 - M - I + * C - G - L2 - L3 - M - HSDIR + * S - G - L2 - L3 - M - R + */ + if (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || + purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + purpose == CIRCUIT_PURPOSE_C_INTRODUCING) + return routelen+2; + } + if (!exit_ei) return routelen; @@ -1697,6 +1676,8 @@ route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei) /* These three purposes connect to a router that someone else * might have chosen, so add an extra hop to protect anonymity. */ case CIRCUIT_PURPOSE_C_GENERAL: + case CIRCUIT_PURPOSE_C_HSDIR_GET: + case CIRCUIT_PURPOSE_S_HSDIR_POST: /* connecting to hidden service directory */ case CIRCUIT_PURPOSE_C_INTRODUCING: /* client connecting to introduction point */ @@ -2145,6 +2126,98 @@ pick_rendezvous_node(router_crn_flags_t flags) return router_choose_random_node(NULL, options->ExcludeNodes, flags); } +/* + * Helper function to pick a configured restricted middle node + * (either HSLayer2Nodes or HSLayer3Nodes). + * + * Make sure that the node we chose is alive, and not excluded, + * and return it. + * + * The exclude_set is a routerset of nodes that the selected node + * must not match, and the exclude_list is a simple list of nodes + * that the selected node must not be in. Either or both may be + * NULL. + * + * Return NULL if no usable nodes could be found. */ +static const node_t * +pick_restricted_middle_node(router_crn_flags_t flags, + const routerset_t *pick_from, + const routerset_t *exclude_set, + const smartlist_t *exclude_list, + int position_hint) +{ + const node_t *middle_node = NULL; + + smartlist_t *whitelisted_live_middles = smartlist_new(); + smartlist_t *all_live_nodes = smartlist_new(); + + tor_assert(pick_from); + + /* Add all running nodes to all_live_nodes */ + router_add_running_nodes_to_smartlist(all_live_nodes, + (flags & CRN_NEED_UPTIME) != 0, + (flags & CRN_NEED_CAPACITY) != 0, + (flags & CRN_NEED_GUARD) != 0, + (flags & CRN_NEED_DESC) != 0, + (flags & CRN_PREF_ADDR) != 0, + (flags & CRN_DIRECT_CONN) != 0); + + /* Filter all_live_nodes to only add live *and* whitelisted middles + * to the list whitelisted_live_middles. */ + SMARTLIST_FOREACH_BEGIN(all_live_nodes, node_t *, live_node) { + if (routerset_contains_node(pick_from, live_node)) { + smartlist_add(whitelisted_live_middles, live_node); + } + } SMARTLIST_FOREACH_END(live_node); + + /* Honor ExcludeNodes */ + if (exclude_set) { + routerset_subtract_nodes(whitelisted_live_middles, exclude_set); + } + + if (exclude_list) { + smartlist_subtract(whitelisted_live_middles, exclude_list); + } + + /** + * Max number of restricted nodes before we alert the user and try + * to load balance for them. + * + * The most aggressive vanguard design had 16 nodes at layer3. + * Let's give a small ceiling above that. */ +#define MAX_SANE_RESTRICTED_NODES 20 + /* If the user (or associated tor controller) selected only a few nodes, + * assume they took load balancing into account and don't do it for them. + * + * If there are a lot of nodes in here, assume they did not load balance + * and do it for them, but also warn them that they may be Doing It Wrong. + */ + if (smartlist_len(whitelisted_live_middles) <= + MAX_SANE_RESTRICTED_NODES) { + middle_node = smartlist_choose(whitelisted_live_middles); + } else { + static ratelim_t pinned_notice_limit = RATELIM_INIT(24*3600); + log_fn_ratelim(&pinned_notice_limit, LOG_NOTICE, LD_CIRC, + "Your _HSLayer%dNodes setting has resulted " + "in %d total nodes. This is a lot of nodes. " + "You may want to consider using a Tor controller " + "to select and update a smaller set of nodes instead.", + position_hint, smartlist_len(whitelisted_live_middles)); + + /* NO_WEIGHTING here just means don't take node flags into account + * (ie: use consensus measurement only). This is done so that + * we don't further surprise the user by not using Exits that they + * specified at all */ + middle_node = node_sl_choose_by_bandwidth(whitelisted_live_middles, + NO_WEIGHTING); + } + + smartlist_free(whitelisted_live_middles); + smartlist_free(all_live_nodes); + + return middle_node; +} + /** Return a pointer to a suitable router to be the exit node for the * circuit of purpose <b>purpose</b> that we're about to build (or NULL * if no router is suitable). @@ -2156,9 +2229,8 @@ pick_rendezvous_node(router_crn_flags_t flags) * toward the preferences in 'options'. */ static const node_t * -choose_good_exit_server(uint8_t purpose, - int need_uptime, int need_capacity, int is_internal, - int need_hs_v3) +choose_good_exit_server(origin_circuit_t *circ, int need_uptime, + int need_capacity, int is_internal, int need_hs_v3) { const or_options_t *options = get_options(); router_crn_flags_t flags = CRN_NEED_DESC; @@ -2169,7 +2241,14 @@ choose_good_exit_server(uint8_t purpose, if (need_hs_v3) flags |= CRN_RENDEZVOUS_V3; - switch (purpose) { + switch (TO_CIRCUIT(circ)->purpose) { + case CIRCUIT_PURPOSE_C_HSDIR_GET: + case CIRCUIT_PURPOSE_S_HSDIR_POST: + case CIRCUIT_PURPOSE_HS_VANGUARDS: + /* For these three, we want to pick the exit like a middle hop, + * since it should be random. */ + tor_assert_nonfatal(is_internal); + /* Falls through */ case CIRCUIT_PURPOSE_C_GENERAL: if (is_internal) /* pick it like a middle hop */ return router_choose_random_node(NULL, options->ExcludeNodes, flags); @@ -2184,7 +2263,7 @@ choose_good_exit_server(uint8_t purpose, return rendezvous_node; } } - log_warn(LD_BUG,"Unhandled purpose %d", purpose); + log_warn(LD_BUG,"Unhandled purpose %d", TO_CIRCUIT(circ)->purpose); tor_fragile_assert(); return NULL; } @@ -2214,6 +2293,8 @@ warn_if_last_router_excluded(origin_circuit_t *circ, (int)purpose, circuit_purpose_to_string(purpose)); return; + case CIRCUIT_PURPOSE_S_HSDIR_POST: + case CIRCUIT_PURPOSE_C_HSDIR_GET: case CIRCUIT_PURPOSE_C_GENERAL: if (circ->build_state->is_internal) return; @@ -2298,7 +2379,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, exit_ei = extend_info_dup(exit_ei); } else { /* we have to decide one */ const node_t *node = - choose_good_exit_server(circ->base_.purpose, state->need_uptime, + choose_good_exit_server(circ, state->need_uptime, state->need_capacity, state->is_internal, is_hs_v3_rp_circuit); if (!node) { @@ -2432,6 +2513,118 @@ cpath_get_n_hops(crypt_path_t **head_ptr) #endif /* defined(TOR_UNIT_TESTS) */ +/** + * Build a list of nodes to exclude from the choice of this middle + * hop, based on already chosen nodes. + * + * XXX: At present, this function does not exclude any nodes from + * the vanguard circuits. See + * https://trac.torproject.org/projects/tor/ticket/24487 + */ +static smartlist_t * +build_middle_exclude_list(uint8_t purpose, + cpath_build_state_t *state, + crypt_path_t *head, + int cur_len) +{ + smartlist_t *excluded; + const node_t *r; + crypt_path_t *cpath; + int i; + + excluded = smartlist_new(); + + /* Add the exit to the exclude list (note that the exit/last hop is always + * chosen first in circuit_establish_circuit()). */ + if ((r = build_state_get_exit_node(state))) { + nodelist_add_node_and_family(excluded, r); + } + + /* XXX: We don't apply any other previously selected node restrictions for + * vanguards, and allow nodes to be reused for those hop positions in the + * same circuit. This is because after many rotations, you get to learn + * inner guard nodes through the nodes that are not selected for outer + * hops. + * + * The alternative is building the circuit in reverse. Reverse calls to + * onion_extend_cpath() (ie: select outer hops first) would then have the + * property that you don't gain information about inner hops by observing + * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487 + * for this. + * + * (Note further that we can and do still exclude the exit in the block + * above, because it is chosen first in circuit_establish_circuit()..) */ + if (circuit_should_use_vanguards(purpose)) { + return excluded; + } + + for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) { + if ((r = node_get_by_id(cpath->extend_info->identity_digest))) { + nodelist_add_node_and_family(excluded, r); + } + } + + return excluded; +} + +/** Return true if we MUST use vanguards for picking this middle node. */ +static int +middle_node_must_be_vanguard(const or_options_t *options, + uint8_t purpose, int cur_len) +{ + /* If this is not a hidden service circuit, don't use vanguards */ + if (!circuit_purpose_is_hidden_service(purpose)) { + return 0; + } + + /* If we have sticky L2 nodes, and this is an L2 pick, use vanguards */ + if (options->HSLayer2Nodes && cur_len == 1) { + return 1; + } + + /* If we have sticky L3 nodes, and this is an L3 pick, use vanguards */ + if (options->HSLayer3Nodes && cur_len == 2) { + return 1; + } + + return 0; +} + +/** Pick a sticky vanguard middle node or return NULL if not found. + * See doc of pick_restricted_middle_node() for argument details. */ +static const node_t * +pick_vanguard_middle_node(const or_options_t *options, + router_crn_flags_t flags, int cur_len, + const smartlist_t *excluded) +{ + const routerset_t *vanguard_routerset = NULL; + const node_t *node = NULL; + + /* Pick the right routerset based on the current hop */ + if (cur_len == 1) { + vanguard_routerset = options->HSLayer2Nodes; + } else if (cur_len == 2) { + vanguard_routerset = options->HSLayer3Nodes; + } else { + /* guaranteed by middle_node_should_be_vanguard() */ + tor_assert_nonfatal_unreached(); + return NULL; + } + + node = pick_restricted_middle_node(flags, vanguard_routerset, + options->ExcludeNodes, excluded, + cur_len+1); + + if (!node) { + static ratelim_t pinned_warning_limit = RATELIM_INIT(300); + log_fn_ratelim(&pinned_warning_limit, LOG_WARN, LD_CIRC, + "Could not find a node that matches the configured " + "_HSLayer%dNodes set", cur_len+1); + } + + return node; +} + /** A helper function used by onion_extend_cpath(). Use <b>purpose</b> * and <b>state</b> and the cpath <b>head</b> (currently populated only * to length <b>cur_len</b> to decide a suitable middle hop for a @@ -2444,9 +2637,7 @@ choose_good_middle_server(uint8_t purpose, crypt_path_t *head, int cur_len) { - int i; - const node_t *r, *choice; - crypt_path_t *cpath; + const node_t *choice; smartlist_t *excluded; const or_options_t *options = get_options(); router_crn_flags_t flags = CRN_NEED_DESC; @@ -2455,20 +2646,20 @@ choose_good_middle_server(uint8_t purpose, log_debug(LD_CIRC, "Contemplating intermediate hop #%d: random choice.", cur_len+1); - excluded = smartlist_new(); - if ((r = build_state_get_exit_node(state))) { - nodelist_add_node_and_family(excluded, r); - } - for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) { - if ((r = node_get_by_id(cpath->extend_info->identity_digest))) { - nodelist_add_node_and_family(excluded, r); - } - } + + excluded = build_middle_exclude_list(purpose, state, head, cur_len); if (state->need_uptime) flags |= CRN_NEED_UPTIME; if (state->need_capacity) flags |= CRN_NEED_CAPACITY; + + /** If a hidden service circuit wants a specific middle node, pin it. */ + if (middle_node_must_be_vanguard(options, purpose, cur_len)) { + log_debug(LD_GENERAL, "Picking a sticky node (cur_len = %d)", cur_len); + return pick_vanguard_middle_node(options, flags, cur_len, excluded); + } + choice = router_choose_random_node(excluded, options->ExcludeNodes, flags); smartlist_free(excluded); return choice; @@ -2607,7 +2798,7 @@ onion_extend_cpath(origin_circuit_t *circ) /** Create a new hop, annotate it with information about its * corresponding router <b>choice</b>, and append it to the * end of the cpath <b>head_ptr</b>. */ -static int +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) { crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); @@ -2698,7 +2889,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /* Don't send the ed25519 pubkey unless the target node actually supports * authenticating with it. */ - if (node_supports_ed25519_link_authentication(node)) { + if (node_supports_ed25519_link_authentication(node, 0)) { log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); ed_pubkey = node_get_ed25519_id(node); } else if (node_get_ed25519_id(node)) { @@ -2707,12 +2898,16 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node_describe(node)); } + /* Retrieve the curve25519 pubkey. */ + const curve25519_public_key_t *curve_pubkey = + node_get_curve25519_onion_key(node); + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, ed_pubkey, node->ri->onion_pkey, - node->ri->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else if (valid_addr && node->rs && node->md) @@ -2720,7 +2915,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node->identity, ed_pubkey, node->md->onion_pkey, - node->md->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else @@ -2729,7 +2924,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /** Release storage held by an extend_info_t struct. */ void -extend_info_free(extend_info_t *info) +extend_info_free_(extend_info_t *info) { if (!info) return; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index b8a651e055..bea31ad0dd 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -12,11 +12,11 @@ #ifndef TOR_CIRCUITBUILD_H #define TOR_CIRCUITBUILD_H +int route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei); char *circuit_list_path(origin_circuit_t *circ, int verbose); char *circuit_list_path_for_controller(origin_circuit_t *circ); void circuit_log_path(int severity, unsigned int domain, origin_circuit_t *circ); -void circuit_rep_hist_note_result(origin_circuit_t *circ); origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags); origin_circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, @@ -27,7 +27,7 @@ int circuit_handle_first_hop(origin_circuit_t *circ); void circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits); int inform_testing_reachability(void); -int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); +int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); void circuit_note_clock_jumped(int seconds_elapsed); int circuit_extend(cell_t *cell, circuit_t *circ); @@ -58,7 +58,9 @@ extend_info_t *extend_info_new(const char *nickname, const tor_addr_t *addr, uint16_t port); extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free(extend_info_t *info); +void extend_info_free_(extend_info_t *info); +#define extend_info_free(info) \ + FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) int extend_info_addr_is_allowed(const tor_addr_t *addr); int extend_info_supports_tap(const extend_info_t* ei); int extend_info_supports_ntor(const extend_info_t* ei); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d442887c9e..29ad9a8ee5 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -51,6 +51,8 @@ * logic, which was originally circuit-focused. **/ #define CIRCUITLIST_PRIVATE +#include "torint.h" /* TOR_PRIuSZ */ + #include "or.h" #include "channel.h" #include "circpathbias.h" @@ -108,6 +110,14 @@ static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); static void circuit_about_to_free_atexit(circuit_t *circ); static void circuit_about_to_free(circuit_t *circ); +/** + * A cached value of the current state of the origin circuit list. Has the + * value 1 if we saw any opened circuits recently (since the last call to + * circuit_any_opened_circuits(), which gets called around once a second by + * circuit_expire_building). 0 otherwise. + */ +static int any_opened_circs_cached_val = 0; + /********* END VARIABLES ************/ /** A map from channel and circuit ID to circuit. (Lookup performance is @@ -504,8 +514,7 @@ circuit_count_pending_on_channel(channel_t *chan) circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); - log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"or_conn to %s, %d pending circs", channel_get_canonical_remote_descr(chan), cnt); return cnt; @@ -596,6 +605,56 @@ circuit_get_global_origin_circuit_list(void) return global_origin_circuit_list; } +/** + * Return true if we have any opened general-purpose 3 hop + * origin circuits. + * + * The result from this function is cached for use by + * circuit_any_opened_circuits_cached(). + */ +int +circuit_any_opened_circuits(void) +{ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(), + const origin_circuit_t *, next_circ) { + if (!TO_CIRCUIT(next_circ)->marked_for_close && + next_circ->has_opened && + TO_CIRCUIT(next_circ)->state == CIRCUIT_STATE_OPEN && + TO_CIRCUIT(next_circ)->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT && + next_circ->build_state && + next_circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN) { + circuit_cache_opened_circuit_state(1); + return 1; + } + } SMARTLIST_FOREACH_END(next_circ); + + circuit_cache_opened_circuit_state(0); + return 0; +} + +/** + * Cache the "any circuits opened" state, as specified in param + * circuits_are_opened. This is a helper function to update + * the circuit opened status whenever we happen to look at the + * circuit list. + */ +void +circuit_cache_opened_circuit_state(int circuits_are_opened) +{ + any_opened_circs_cached_val = circuits_are_opened; +} + +/** + * Return true if there were any opened circuits since the last call to + * circuit_any_opened_circuits(), or since circuit_expire_building() last + * ran (it runs roughly once per second). + */ +int +circuit_any_opened_circuits_cached(void) +{ + return any_opened_circs_cached_val; +} + /** Function to make circ-\>state human-readable */ const char * circuit_state_to_string(int state) @@ -630,6 +689,10 @@ circuit_purpose_to_controller_string(uint8_t purpose) case CIRCUIT_PURPOSE_C_GENERAL: return "GENERAL"; + + case CIRCUIT_PURPOSE_C_HSDIR_GET: + return "HS_CLIENT_HSDIR"; + case CIRCUIT_PURPOSE_C_INTRODUCING: case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: @@ -641,6 +704,9 @@ circuit_purpose_to_controller_string(uint8_t purpose) case CIRCUIT_PURPOSE_C_REND_JOINED: return "HS_CLIENT_REND"; + case CIRCUIT_PURPOSE_S_HSDIR_POST: + return "HS_SERVICE_HSDIR"; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: case CIRCUIT_PURPOSE_S_INTRO: return "HS_SERVICE_INTRO"; @@ -657,6 +723,8 @@ circuit_purpose_to_controller_string(uint8_t purpose) return "CONTROLLER"; case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: return "PATH_BIAS_TESTING"; + case CIRCUIT_PURPOSE_HS_VANGUARDS: + return "HS_VANGUARDS"; default: tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); @@ -685,6 +753,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose) case CIRCUIT_PURPOSE_TESTING: case CIRCUIT_PURPOSE_CONTROLLER: case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: + case CIRCUIT_PURPOSE_HS_VANGUARDS: return NULL; case CIRCUIT_PURPOSE_INTRO_POINT: @@ -694,6 +763,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose) case CIRCUIT_PURPOSE_REND_ESTABLISHED: return "OR_HS_R_JOINED"; + case CIRCUIT_PURPOSE_C_HSDIR_GET: case CIRCUIT_PURPOSE_C_INTRODUCING: return "HSCI_CONNECTING"; case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: @@ -710,6 +780,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose) case CIRCUIT_PURPOSE_C_REND_JOINED: return "HSCR_JOINED"; + case CIRCUIT_PURPOSE_S_HSDIR_POST: case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: return "HSSI_CONNECTING"; case CIRCUIT_PURPOSE_S_INTRO: @@ -735,9 +806,9 @@ circuit_purpose_to_string(uint8_t purpose) case CIRCUIT_PURPOSE_INTRO_POINT: return "Acting as intro point"; case CIRCUIT_PURPOSE_REND_POINT_WAITING: - return "Acting as rendevous (pending)"; + return "Acting as rendezvous (pending)"; case CIRCUIT_PURPOSE_REND_ESTABLISHED: - return "Acting as rendevous (established)"; + return "Acting as rendezvous (established)"; case CIRCUIT_PURPOSE_C_GENERAL: return "General-purpose client"; case CIRCUIT_PURPOSE_C_INTRODUCING: @@ -754,6 +825,9 @@ circuit_purpose_to_string(uint8_t purpose) return "Hidden service client: Pending rendezvous point (ack received)"; case CIRCUIT_PURPOSE_C_REND_JOINED: return "Hidden service client: Active rendezvous point"; + case CIRCUIT_PURPOSE_C_HSDIR_GET: + return "Hidden service client: Fetching HS descriptor"; + case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: return "Measuring circuit timeout"; @@ -765,6 +839,8 @@ circuit_purpose_to_string(uint8_t purpose) return "Hidden service: Connecting to rendezvous point"; case CIRCUIT_PURPOSE_S_REND_JOINED: return "Hidden service: Active rendezvous point"; + case CIRCUIT_PURPOSE_S_HSDIR_POST: + return "Hidden service: Uploading HS descriptor"; case CIRCUIT_PURPOSE_TESTING: return "Testing circuit"; @@ -775,6 +851,9 @@ circuit_purpose_to_string(uint8_t purpose) case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: return "Path-bias testing circuit"; + case CIRCUIT_PURPOSE_HS_VANGUARDS: + return "Hidden service: Pre-built vanguard circuit"; + default: tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); return buf; @@ -818,8 +897,10 @@ init_circuit_base(circuit_t *circ) /** If we haven't yet decided on a good timeout value for circuit * building, we close idle circuits aggressively so we can get more - * data points. */ -#define IDLE_TIMEOUT_WHILE_LEARNING (1*60) + * data points. These are the default, min, and max consensus values */ +#define DFLT_IDLE_TIMEOUT_WHILE_LEARNING (3*60) +#define MIN_IDLE_TIMEOUT_WHILE_LEARNING (10) +#define MAX_IDLE_TIMEOUT_WHILE_LEARNING (1000*60) /** Allocate space for a new circuit, initializing with <b>p_circ_id</b> * and <b>p_conn</b>. Add it to the global circuit list. @@ -852,7 +933,11 @@ origin_circuit_new(void) circuit_build_times_needs_circuits(get_circuit_build_times())) { /* Circuits should be shorter lived if we need more of them * for learning a good build timeout */ - circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING; + circ->circuit_idle_timeout = + networkstatus_get_param(NULL, "cbtlearntimeout", + DFLT_IDLE_TIMEOUT_WHILE_LEARNING, + MIN_IDLE_TIMEOUT_WHILE_LEARNING, + MAX_IDLE_TIMEOUT_WHILE_LEARNING); } else { // This should always be larger than the current port prediction time // remaining, or else we'll end up with the case where a circuit times out @@ -872,7 +957,11 @@ origin_circuit_new(void) "%d seconds of predictive building remaining.", circ->circuit_idle_timeout, prediction_time_remaining); - circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING; + circ->circuit_idle_timeout = + networkstatus_get_param(NULL, "cbtlearntimeout", + DFLT_IDLE_TIMEOUT_WHILE_LEARNING, + MIN_IDLE_TIMEOUT_WHILE_LEARNING, + MAX_IDLE_TIMEOUT_WHILE_LEARNING); } log_info(LD_CIRC, @@ -923,7 +1012,7 @@ circuit_clear_testing_cell_stats(circuit_t *circ) /** Deallocate space associated with circ. */ STATIC void -circuit_free(circuit_t *circ) +circuit_free_(circuit_t *circ) { circid_t n_circ_id = 0; void *mem; @@ -1091,7 +1180,7 @@ circuit_free_all(void) while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; - connection_free(TO_CONN(or_circ->resolving_streams)); + connection_free_(TO_CONN(or_circ->resolving_streams)); or_circ->resolving_streams = next_conn; } } @@ -1646,14 +1735,29 @@ circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ) return 0; } +/** We are trying to create a circuit of purpose <b>purpose</b> and we are + * looking for cannibalizable circuits. Return the circuit purpose we would be + * willing to cannibalize. */ +static uint8_t +get_circuit_purpose_needed_to_cannibalize(uint8_t purpose) +{ + if (circuit_should_use_vanguards(purpose)) { + /* If we are using vanguards, then we should only cannibalize vanguard + * circuits so that we get the same path construction logic. */ + return CIRCUIT_PURPOSE_HS_VANGUARDS; + } else { + /* If no vanguards are used just get a general circuit! */ + return CIRCUIT_PURPOSE_C_GENERAL; + } +} + /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL, * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_* * flags in <b>flags</b>, and if info is defined, does not already use info * as any of its hops; or NULL if no circuit fits this description. * - * The <b>purpose</b> argument (currently ignored) refers to the purpose of - * the circuit we want to create, not the purpose of the circuit we want to - * cannibalize. + * The <b>purpose</b> argument refers to the purpose of the circuit we want to + * create, not the purpose of the circuit we want to cannibalize. * * If !CIRCLAUNCH_NEED_UPTIME, prefer returning non-uptime circuits. * @@ -1666,7 +1770,7 @@ circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ) * a new circuit.) */ origin_circuit_t * -circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, +circuit_find_to_cannibalize(uint8_t purpose_to_produce, extend_info_t *info, int flags) { origin_circuit_t *best=NULL; @@ -1674,29 +1778,53 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0; int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0; const or_options_t *options = get_options(); + /* We want the circuit we are trying to cannibalize to have this purpose */ + int purpose_to_search_for; /* Make sure we're not trying to create a onehop circ by * cannibalization. */ tor_assert(!(flags & CIRCLAUNCH_ONEHOP_TUNNEL)); + purpose_to_search_for = get_circuit_purpose_needed_to_cannibalize( + purpose_to_produce); + + tor_assert_nonfatal(purpose_to_search_for == CIRCUIT_PURPOSE_C_GENERAL || + purpose_to_search_for == CIRCUIT_PURPOSE_HS_VANGUARDS); + log_debug(LD_CIRC, "Hunting for a circ to cannibalize: purpose %d, uptime %d, " "capacity %d, internal %d", - purpose, need_uptime, need_capacity, internal); + purpose_to_produce, need_uptime, need_capacity, internal); SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ_) { if (CIRCUIT_IS_ORIGIN(circ_) && circ_->state == CIRCUIT_STATE_OPEN && !circ_->marked_for_close && - circ_->purpose == CIRCUIT_PURPOSE_C_GENERAL && + circ_->purpose == purpose_to_search_for && !circ_->timestamp_dirty) { origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(circ_); + + /* Only cannibalize from reasonable length circuits. If we + * want C_GENERAL, then only choose 3 hop circs. If we want + * HS_VANGUARDS, only choose 4 hop circs. + */ + if (circ->build_state->desired_path_len != + route_len_for_purpose(purpose_to_search_for, NULL)) { + goto next; + } + + /* Ignore any circuits for which we can't use the Guard. It is possible + * that the Guard was removed from the samepled set after the circuit + * was created so avoid using it. */ + if (!entry_guard_could_succeed(circ->guard_state)) { + goto next; + } + if ((!need_uptime || circ->build_state->need_uptime) && (!need_capacity || circ->build_state->need_capacity) && (internal == circ->build_state->is_internal) && !circ->unusable_for_new_conns && circ->remaining_relay_early_cells && - circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN && !circ->build_state->onehop_tunnel && !circ->isolation_values_set) { if (info) { @@ -1793,6 +1921,25 @@ circuit_get_cpath_len(origin_circuit_t *circ) return n; } +/** Return the number of opened hops in circuit's path. + * If circ has no entries, or is NULL, returns 0. */ +int +circuit_get_cpath_opened_len(const origin_circuit_t *circ) +{ + int n = 0; + if (circ && circ->cpath) { + crypt_path_t *cpath, *cpath_next = NULL; + for (cpath = circ->cpath; + cpath->state == CPATH_STATE_OPEN + && cpath_next != circ->cpath; + cpath = cpath_next) { + cpath_next = cpath->next; + ++n; + } + } + return n; +} + /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there * aren't that many hops in the list. <b>hopnum</b> starts at 1. * Returns NULL if <b>hopnum</b> is 0 or negative. */ @@ -1849,8 +1996,7 @@ circuit_mark_all_dirty_circs_as_unusable(void) * - If state is onionskin_pending, remove circ from the onion_pending * list. * - If circ isn't open yet: call circuit_build_failed() if we're - * the origin, and in either case call circuit_rep_hist_note_result() - * to note stats. + * the origin. * - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point * failure we just had to the hidden service client module. * - If purpose is C_INTRODUCING and <b>reason</b> isn't TIMEOUT, @@ -1986,7 +2132,6 @@ circuit_about_to_free(circuit_t *circ) if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); circuit_build_failed(ocirc); /* take actions if necessary */ - circuit_rep_hist_note_result(ocirc); } } if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { @@ -2176,12 +2321,12 @@ n_cells_in_circ_queues(const circuit_t *c) } /** - * Return the age of the oldest cell queued on <b>c</b>, in milliseconds. + * Return the age of the oldest cell queued on <b>c</b>, in timestamp units. * Return 0 if there are no cells queued on c. Requires that <b>now</b> be - * the current time in milliseconds since the epoch, truncated. + * the current coarse timestamp. * * This function will return incorrect results if the oldest cell queued on - * the circuit is older than 2**32 msec (about 49 days) old. + * the circuit is older than about 2**32 msec (about 49 days) old. */ STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) @@ -2190,12 +2335,12 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) packed_cell_t *cell; if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head))) - age = now - cell->inserted_time; + age = now - cell->inserted_timestamp; if (! CIRCUIT_IS_ORIGIN(c)) { const or_circuit_t *orcirc = CONST_TO_OR_CIRCUIT(c); if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) { - uint32_t age2 = now - cell->inserted_time; + uint32_t age2 = now - cell->inserted_timestamp; if (age2 > age) return age2; } @@ -2203,31 +2348,30 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>, - * where age is taken in milliseconds before the time <b>now</b> (in truncated - * absolute monotonic msec). If the connection has no data, treat - * it as having age zero. +/** Return the age of the oldest buffer chunk on <b>conn</b>, where age is + * taken in timestamp units before the time <b>now</b>. If the connection has + * no data, treat it as having age zero. **/ static uint32_t -conn_get_buffer_age(const connection_t *conn, uint32_t now) +conn_get_buffer_age(const connection_t *conn, uint32_t now_ts) { uint32_t age = 0, age2; if (conn->outbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now_ts); if (age2 > age) age = age2; } if (conn->inbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now_ts); if (age2 > age) age = age2; } return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream in - * the linked list <b>stream</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * in the linked list <b>stream</b>, where age is taken in timestamp units + * before the timestamp <b>now</b>. */ static uint32_t circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) { @@ -2246,9 +2390,9 @@ circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream - * attached to the circuit <b>c</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * attached to the circuit <b>c</b>, where age is taken before the timestamp + * <b>now</b>. */ STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now) { @@ -2262,8 +2406,8 @@ circuit_max_queued_data_age(const circuit_t *c, uint32_t now) } /** Return the age of the oldest cell or stream buffer chunk on the circuit - * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in - * truncated milliseconds since the epoch). */ + * <b>c</b>, where age is taken in timestamp units before the timestamp + * <b>now</b> */ STATIC uint32_t circuit_max_queued_item_age(const circuit_t *c, uint32_t now) { @@ -2293,7 +2437,7 @@ circuits_compare_by_oldest_queued_item_(const void **a_, const void **b_) return -1; } -static uint32_t now_ms_for_buf_cmp; +static uint32_t now_ts_for_buf_cmp; /** Helper to sort a list of circuit_t by age of oldest item, in descending * order. */ @@ -2302,8 +2446,8 @@ conns_compare_by_buffer_age_(const void **a_, const void **b_) { const connection_t *a = *a_; const connection_t *b = *b_; - time_t age_a = conn_get_buffer_age(a, now_ms_for_buf_cmp); - time_t age_b = conn_get_buffer_age(b, now_ms_for_buf_cmp); + time_t age_a = conn_get_buffer_age(a, now_ts_for_buf_cmp); + time_t age_b = conn_get_buffer_age(b, now_ts_for_buf_cmp); if (age_a < age_b) return 1; @@ -2328,10 +2472,17 @@ circuits_handle_oom(size_t current_allocation) size_t mem_recovered=0; int n_circuits_killed=0; int n_dirconns_killed=0; - uint32_t now_ms; - log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " - "over-long queues. (This behavior is controlled by " - "MaxMemInQueues.)"); + uint32_t now_ts; + log_notice(LD_GENERAL, "We're low on memory (cell queues total alloc:" + " %"TOR_PRIuSZ" buffer total alloc: %" TOR_PRIuSZ "," + " tor compress total alloc: %" TOR_PRIuSZ + " rendezvous cache total alloc: %" TOR_PRIuSZ "). Killing" + " circuits withover-long queues. (This behavior is controlled by" + " MaxMemInQueues.)", + cell_queues_get_total_allocation(), + buf_get_total_allocation(), + tor_compress_get_total_allocation(), + rend_cache_get_total_allocation()); { size_t mem_target = (size_t)(get_options()->MaxMemInQueues * @@ -2341,11 +2492,11 @@ circuits_handle_oom(size_t current_allocation) mem_to_recover = current_allocation - mem_target; } - now_ms = (uint32_t)monotime_coarse_absolute_msec(); + now_ts = monotime_coarse_get_stamp(); circlist = circuit_get_global_list(); SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { - circ->age_tmp = circuit_max_queued_item_age(circ, now_ms); + circ->age_tmp = circuit_max_queued_item_age(circ, now_ts); } SMARTLIST_FOREACH_END(circ); /* This is O(n log n); there are faster algorithms we could use instead. @@ -2358,9 +2509,9 @@ circuits_handle_oom(size_t current_allocation) } SMARTLIST_FOREACH_END(circ); /* Now sort the connection array ... */ - now_ms_for_buf_cmp = now_ms; + now_ts_for_buf_cmp = now_ts; smartlist_sort(connection_array, conns_compare_by_buffer_age_); - now_ms_for_buf_cmp = 0; + now_ts_for_buf_cmp = 0; /* Fix up the connection array to its new order. */ SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { @@ -2379,7 +2530,7 @@ circuits_handle_oom(size_t current_allocation) * data older than this circuit. */ while (conn_idx < smartlist_len(connection_array)) { connection_t *conn = smartlist_get(connection_array, conn_idx); - uint32_t conn_age = conn_get_buffer_age(conn, now_ms); + uint32_t conn_age = conn_get_buffer_age(conn, now_ts); if (conn_age < circ->age_tmp) { break; } diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 4190c1f82e..246f0c8815 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -17,6 +17,10 @@ MOCK_DECL(smartlist_t *, circuit_get_global_list, (void)); smartlist_t *circuit_get_global_origin_circuit_list(void); +int circuit_any_opened_circuits(void); +int circuit_any_opened_circuits_cached(void); +void circuit_cache_opened_circuit_state(int circuits_are_opened); + const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); @@ -58,6 +62,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void); MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason, int line, const char *file)); int circuit_get_cpath_len(origin_circuit_t *circ); +int circuit_get_cpath_opened_len(const origin_circuit_t *); void circuit_clear_cpath(origin_circuit_t *circ); crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum); void circuit_get_all_pending_on_channel(smartlist_t *out, @@ -81,7 +86,8 @@ MOCK_DECL(void, channel_note_destroy_not_pending, smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void); #ifdef CIRCUITLIST_PRIVATE -STATIC void circuit_free(circuit_t *circ); +STATIC void circuit_free_(circuit_t *circ); +#define circuit_free(circ) FREE_AND_NULL(circuit_t, circuit_free_, (circ)) STATIC size_t n_cells_in_circ_queues(const circuit_t *c); STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now); STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now); diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 2cc0e8fc44..fe3d8f1332 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -537,7 +537,7 @@ circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan) */ void -circuitmux_free(circuitmux_t *cmux) +circuitmux_free_(circuitmux_t *cmux) { if (!cmux) return; diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index a95edb99d8..336e128c76 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -104,7 +104,9 @@ void circuitmux_assert_okay(circuitmux_t *cmux); circuitmux_t * circuitmux_alloc(void); void circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out); -void circuitmux_free(circuitmux_t *cmux); +void circuitmux_free_(circuitmux_t *cmux); +#define circuitmux_free(cmux) \ + FREE_AND_NULL(circuitmux_t, circuitmux_free_, (cmux)) /* Policy control */ void circuitmux_clear_policy(circuitmux_t *cmux); diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index b8421a3c7e..6438319273 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -36,6 +36,8 @@ #include "rendclient.h" #include "rendservice.h" #include "statefile.h" +#include "circuitlist.h" +#include "circuituse.h" #undef log #include <math.h> @@ -43,6 +45,7 @@ static void cbt_control_event_buildtimeout_set( const circuit_build_times_t *cbt, buildtimeout_set_event_t type); +static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt); #define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) @@ -164,7 +167,7 @@ circuit_build_times_disabled_(const or_options_t *options, } /** - * Retrieve and bounds-check the cbtmaxtimeouts consensus paramter. + * Retrieve and bounds-check the cbtmaxtimeouts consensus parameter. * * Effect: When this many timeouts happen in the last 'cbtrecentcount' * circuit attempts, the client should discard all of its history and @@ -191,7 +194,7 @@ circuit_build_times_max_timeouts(void) } /** - * Retrieve and bounds-check the cbtnummodes consensus paramter. + * Retrieve and bounds-check the cbtnummodes consensus parameter. * * Effect: This value governs how many modes to use in the weighted * average calculation of Pareto parameter Xm. A value of 3 introduces @@ -218,7 +221,7 @@ circuit_build_times_default_num_xm_modes(void) } /** - * Retrieve and bounds-check the cbtmincircs consensus paramter. + * Retrieve and bounds-check the cbtmincircs consensus parameter. * * Effect: This is the minimum number of circuits to build before * computing a timeout. @@ -250,7 +253,7 @@ circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt) } /** - * Retrieve and bounds-check the cbtquantile consensus paramter. + * Retrieve and bounds-check the cbtquantile consensus parameter. * * Effect: This is the position on the quantile curve to use to set the * timeout value. It is a percent (10-99). @@ -274,7 +277,7 @@ circuit_build_times_quantile_cutoff(void) } /** - * Retrieve and bounds-check the cbtclosequantile consensus paramter. + * Retrieve and bounds-check the cbtclosequantile consensus parameter. * * Effect: This is the position on the quantile curve to use to set the * timeout value to use to actually close circuits. It is a percent @@ -306,7 +309,7 @@ circuit_build_times_close_quantile(void) } /** - * Retrieve and bounds-check the cbttestfreq consensus paramter. + * Retrieve and bounds-check the cbttestfreq consensus parameter. * * Effect: Describes how often in seconds to build a test circuit to * gather timeout values. Only applies if less than 'cbtmincircs' @@ -353,7 +356,7 @@ circuit_build_times_min_timeout(void) } /** - * Retrieve and bounds-check the cbtinitialtimeout consensus paramter. + * Retrieve and bounds-check the cbtinitialtimeout consensus parameter. * * Effect: This is the timeout value to use before computing a timeout, * in milliseconds. @@ -383,7 +386,7 @@ circuit_build_times_initial_timeout(void) } /** - * Retrieve and bounds-check the cbtrecentcount consensus paramter. + * Retrieve and bounds-check the cbtrecentcount consensus parameter. * * Effect: This is the number of circuit build times to keep track of * for deciding if we hit cbtmaxtimeouts and need to reset our state @@ -540,6 +543,11 @@ circuit_build_times_reset(circuit_build_times_t *cbt) cbt->total_build_times = 0; cbt->build_times_idx = 0; cbt->have_computed_timeout = 0; + + // Reset timeout and close counts + cbt->num_circ_succeeded = 0; + cbt->num_circ_closed = 0; + cbt->num_circ_timeouts = 0; } /** @@ -616,6 +624,123 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) #endif /* 0 */ /** + * Mark this circuit as timed out, but change its purpose + * so that it continues to build, allowing us to measure + * its full build time. + */ +void +circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ) +{ + control_event_circuit_status(circ, + CIRC_EVENT_FAILED, + END_CIRC_REASON_TIMEOUT); + circuit_change_purpose(TO_CIRCUIT(circ), + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + /* Record this event to check for too many timeouts + * in a row. This function does not record a time value yet + * (we do that later); it only counts the fact that we did + * have a timeout. We also want to avoid double-counting + * already "relaxed" circuits, which are counted in + * circuit_expire_building(). */ + if (!circ->relaxed_timeout) { + int first_hop_succeeded = circ->cpath && + circ->cpath->state == CPATH_STATE_OPEN; + + circuit_build_times_count_timeout( + get_circuit_build_times_mutable(), + first_hop_succeeded); + } +} + +/** + * Perform the build time work that needs to be done when a circuit + * completes a hop. + * + * This function decides if we should record a circuit's build time + * in our histogram data and other statistics, and if so, records it. + * It also will mark circuits that have already timed out as + * measurement-only circuits, so they can continue to build but + * not get used. + * + * For this, we want to consider circuits that will eventually make + * it to the third hop. For circuits longer than 3 hops, we want to + * record their build time when they reach the third hop, but let + * them continue (and not count them later). For circuits that are + * exactly 3 hops, this will count them when they are completed. We + * do this so that CBT is always gathering statistics on circuits + * of the same length, regardless of their type. + */ +void +circuit_build_times_handle_completed_hop(origin_circuit_t *circ) +{ + struct timeval end; + long timediff; + + /* If circuit build times are disabled, let circuit_expire_building() + * handle it.. */ + if (circuit_build_times_disabled(get_options())) { + return; + } + + /* Is this a circuit for which the timeout applies in a straight-forward + * way? If so, handle it below. If not, just return (and let + * circuit_expire_building() eventually take care of it). + */ + if (!circuit_timeout_want_to_count_circ(circ)) { + return; + } + + tor_gettimeofday(&end); + timediff = tv_mdiff(&circ->base_.timestamp_began, &end); + + /* Check if we would have timed out already. If so, change the + * purpose here. But don't do any timeout handling here if there + * are no circuits opened yet. Save it for circuit_expire_building() + * (to allow it to handle timeout "relaxing" over there). */ + if (timediff > get_circuit_build_timeout_ms() && + circuit_any_opened_circuits_cached()) { + + /* Circuits are allowed to last longer for measurement. + * Switch their purpose and wait. */ + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + log_info(LD_CIRC, + "Deciding to timeout circuit "U64_FORMAT"\n", + U64_PRINTF_ARG(circ->global_identifier)); + circuit_build_times_mark_circ_as_measurement_only(circ); + } + } + + /* If the circuit is built to exactly the DEFAULT_ROUTE_LEN, + * add it to our buildtimes. */ + if (circuit_get_cpath_opened_len(circ) == DEFAULT_ROUTE_LEN) { + /* If the circuit build time is much greater than we would have cut + * it off at, we probably had a suspend event along this codepath, + * and we should discard the value. + */ + if (timediff < 0 || + timediff > 2*get_circuit_build_close_time_ms()+1000) { + log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " + "Assuming clock jump. Purpose %d (%s)", timediff, + circ->base_.purpose, + circuit_purpose_to_string(circ->base_.purpose)); + } else { + /* Only count circuit times if the network is live */ + if (circuit_build_times_network_check_live( + get_circuit_build_times())) { + circuit_build_times_add_time(get_circuit_build_times_mutable(), + (build_time_t)timediff); + circuit_build_times_set_timeout(get_circuit_build_times_mutable()); + } + + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + circuit_build_times_network_circ_success( + get_circuit_build_times_mutable()); + } + } + } +} + +/** * Add a new build time value <b>time</b> to the set of build times. Time * units are milliseconds. * @@ -1290,9 +1415,32 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) } /** - * Called to indicate that we completed a circuit. Because this circuit + * Non-destructively scale all of our circuit success, timeout, and close + * counts down by a factor of two. Scaling in this way preserves the + * ratios between succeeded vs timed out vs closed circuits, so that + * our statistics don't change when we scale. + * + * This is used only in the rare event that we build more than + * INT32_MAX circuits. Since the num_circ_* variables are + * uint32_t, we won't even be close to overflowing them. + */ +void +circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt) +{ + cbt->num_circ_succeeded /= 2; + cbt->num_circ_timeouts /= 2; + cbt->num_circ_closed /= 2; +} + +/** + * Called to indicate that we "completed" a circuit. Because this circuit * succeeded, it doesn't count as a timeout-after-the-first-hop. * + * (For the purposes of the cbt code, we consider a circuit "completed" if + * it has 3 hops, regardless of its final hop count. We do this because + * we're trying to answer the question, "how long should a circuit take to + * reach the 3-hop count".) + * * This is used by circuit_build_times_network_check_changed() to determine * if we had too many recent timeouts and need to reset our learned timeout * to something higher. @@ -1300,6 +1448,14 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) void circuit_build_times_network_circ_success(circuit_build_times_t *cbt) { + // Count circuit success + cbt->num_circ_succeeded++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_succeeded >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1322,6 +1478,14 @@ static void circuit_build_times_network_timeout(circuit_build_times_t *cbt, int did_onehop) { + // Count circuit timeout + cbt->num_circ_timeouts++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_timeouts >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1347,6 +1511,15 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, int did_onehop, time_t start_time) { time_t now = time(NULL); + + // Count circuit close + cbt->num_circ_closed++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_closed >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* * Check if this is a timeout that was for a circuit that spent its * entire existence during a time where we have had no network activity. @@ -1701,6 +1874,8 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, { char *args = NULL; double qnt; + double timeout_rate = 0.0; + double close_rate = 0.0; switch (type) { case BUILDTIMEOUT_SET_EVENT_RESET: @@ -1715,15 +1890,29 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, break; } + /* The timeout rate is the ratio of the timeout count over + * the total number of circuits attempted. The total number of + * circuits is (timeouts+succeeded+closed), since a circuit can + * either timeout, close, or succeed. We cast the denominator + * to promote it to double before the addition, to avoid int32 + * overflow. */ + const double total_circuits = + ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded + + cbt->num_circ_closed; + if (total_circuits >= 1.0) { + timeout_rate = cbt->num_circ_timeouts / total_circuits; + close_rate = cbt->num_circ_closed / total_circuits; + } + tor_asprintf(&args, "TOTAL_TIMES=%lu " "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", (unsigned long)cbt->total_build_times, (unsigned long)cbt->timeout_ms, (unsigned long)cbt->Xm, cbt->alpha, qnt, - circuit_build_times_timeout_rate(cbt), + timeout_rate, (unsigned long)cbt->close_ms, - circuit_build_times_close_rate(cbt)); + close_rate); control_event_buildtimeout_set(type, args); diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h index 92dc6405ba..86116cb7f8 100644 --- a/src/or/circuitstats.h +++ b/src/or/circuitstats.h @@ -34,6 +34,7 @@ void circuit_build_times_set_timeout(circuit_build_times_t *cbt); int circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time); int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt); +void circuit_build_times_handle_completed_hop(origin_circuit_t *circ); int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); @@ -44,6 +45,7 @@ double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); +void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ); #ifdef CIRCUITSTATS_PRIVATE STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, @@ -94,6 +96,16 @@ struct circuit_build_times_s { double timeout_ms; /** How long we wait before actually closing the circuit. */ double close_ms; + /** Total succeeded counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_succeeded; + /** Total timeout counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_timeouts; + /** Total closed counts. Old measurements may be scaled downward if + * we've seen a lot of circuits.*/ + uint32_t num_circ_closed; + }; #endif /* defined(CIRCUITSTATS_PRIVATE) */ diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 7baa035696..bc9c4bccbd 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -45,6 +45,7 @@ #include "hs_client.h" #include "hs_circuit.h" #include "hs_ident.h" +#include "hs_stats.h" #include "nodelist.h" #include "networkstatus.h" #include "policies.h" @@ -54,6 +55,7 @@ #include "rephist.h" #include "router.h" #include "routerlist.h" +#include "config.h" static void circuit_expire_old_circuits_clientside(void); static void circuit_increment_failure_count(void); @@ -133,6 +135,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, } if (purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || + purpose == CIRCUIT_PURPOSE_HS_VANGUARDS || purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { if (circ->timestamp_dirty && circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now) @@ -156,7 +161,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, if (need_internal != build_state->is_internal) return 0; - if (purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if (purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || + purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) { tor_addr_t addr; const int family = tor_addr_parse(&addr, conn->socks_request->address); if (!exitnode && !build_state->onehop_tunnel) { @@ -238,6 +245,8 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob, return 1; /* oa is better. It's not relaxed. */ switch (purpose) { + case CIRCUIT_PURPOSE_S_HSDIR_POST: + case CIRCUIT_PURPOSE_C_HSDIR_GET: case CIRCUIT_PURPOSE_C_GENERAL: /* if it's used but less dirty it's best; * else if it's more recently created it's best @@ -323,6 +332,9 @@ circuit_get_best(const entry_connection_t *conn, tor_assert(conn); tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_HS_VANGUARDS || + purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || purpose == CIRCUIT_PURPOSE_C_REND_JOINED); @@ -412,8 +424,17 @@ circuit_conforms_to_options(const origin_circuit_t *circ, } #endif /* 0 */ -/** Close all circuits that start at us, aren't open, and were born +/** + * Close all circuits that start at us, aren't open, and were born * at least CircuitBuildTimeout seconds ago. + * + * TODO: This function is now partially redundant to + * circuit_build_times_handle_completed_hop(), but that function only + * covers circuits up to and including 3 hops that are still actually + * completing hops. However, circuit_expire_building() also handles longer + * circuits, as well as circuits that are completely stalled. + * In the future (after prop247/other path selection revamping), we probably + * want to eliminate this rats nest in favor of a simpler approach. */ void circuit_expire_building(void) @@ -435,21 +456,7 @@ circuit_expire_building(void) * we want to be more lenient with timeouts, in case the * user has relocated and/or changed network connections. * See bug #3443. */ - SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, next_circ) { - if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */ - next_circ->marked_for_close) { /* don't mess with marked circs */ - continue; - } - - if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened && - next_circ->state == CIRCUIT_STATE_OPEN && - TO_ORIGIN_CIRCUIT(next_circ)->build_state && - TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len - == DEFAULT_ROUTE_LEN) { - any_opened_circs = 1; - break; - } - } SMARTLIST_FOREACH_END(next_circ); + any_opened_circs = circuit_any_opened_circuits(); #define SET_CUTOFF(target, msec) do { \ long ms = tor_lround(msec); \ @@ -493,6 +500,10 @@ circuit_expire_building(void) SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms()); SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms()); + // TODO: We should probably use route_len_for_purpose() here instead, + // except that does not count the extra round trip for things like server + // intros and rends. + /* > 3hop circs seem to have a 1.0 second delay on their cannibalized * 4th hop. */ SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000); @@ -585,7 +596,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state)); + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none"); /* We count the timeout here for CBT, because technically this * was a timeout, and the timeout value needs to reset if we @@ -610,7 +622,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none", (long)build_close_ms); } } @@ -661,7 +674,7 @@ circuit_expire_building(void) break; case CIRCUIT_PURPOSE_C_INTRODUCING: /* That purpose means that the intro point circuit has been opened - * succesfully but the INTRODUCE1 cell hasn't been sent yet because + * successfully but the INTRODUCE1 cell hasn't been sent yet because * the client is waiting for the rendezvous point circuit to open. * Keep this circuit open while waiting for the rendezvous circuit. * We let the circuit idle timeout take care of cleaning this @@ -694,23 +707,17 @@ circuit_expire_building(void) if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) && circuit_build_times_enough_to_compute(get_circuit_build_times())) { + + log_info(LD_CIRC, + "Deciding to count the timeout for circuit "U64_FORMAT"\n", + U64_PRINTF_ARG( + TO_ORIGIN_CIRCUIT(victim)->global_identifier)); + /* Circuits are allowed to last longer for measurement. * Switch their purpose and wait. */ if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim), - CIRC_EVENT_FAILED, - END_CIRC_REASON_TIMEOUT); - circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); - /* Record this failure to check for too many timeouts - * in a row. This function does not record a time value yet - * (we do that later); it only counts the fact that we did - * have a timeout. We also want to avoid double-counting - * already "relaxed" circuits, which are counted above. */ - if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) { - circuit_build_times_count_timeout( - get_circuit_build_times_mutable(), - first_hop_succeeded); - } + circuit_build_times_mark_circ_as_measurement_only(TO_ORIGIN_CIRCUIT( + victim)); continue; } @@ -868,10 +875,10 @@ circuit_log_ancient_one_hop_circuits(int age) if (circ->timestamp_created.tv_sec >= cutoff) continue; /* Single Onion Services deliberately make long term one-hop intro - * connections. We only ignore active intro point connections, if we take - * a long time establishing, that's worth logging. */ + * and rendezvous connections. Don't log the established ones. */ if (rend_service_allow_non_anonymous_connection(options) && - circ->purpose == CIRCUIT_PURPOSE_S_INTRO) + (circ->purpose == CIRCUIT_PURPOSE_S_INTRO || + circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED)) continue; /* Tor2web deliberately makes long term one-hop rend connections, * particularly when Tor2webRendezvousPoints is used. We only ignore @@ -1085,7 +1092,8 @@ circuit_is_available_for_use(const circuit_t *circ) return 0; /* Don't mess with marked circs */ if (circ->timestamp_dirty) return 0; /* Only count clean circs */ - if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) + if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL && + circ->purpose != CIRCUIT_PURPOSE_HS_VANGUARDS) return 0; /* We only pay attention to general purpose circuits. General purpose circuits are always origin circuits. */ @@ -1171,15 +1179,11 @@ needs_hs_client_circuits(time_t now, int *needs_uptime, int *needs_capacity, router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN); } -/* The minimum number of open slots we should keep in order to preemptively - * build circuits. */ -#define CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS 2 - -/* Check to see if we need more circuits to have a good build timeout. However, - * leave a couple slots open so that we can still build circuits preemptively - * as needed. */ -#define CBT_MAX_UNUSED_OPEN_CIRCUITS (MAX_UNUSED_OPEN_CIRCUITS - \ - CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS) +/* This is how many circuits can be opened concurrently during the cbt learning + * phase. This number cannot exceed the tor-wide MAX_UNUSED_OPEN_CIRCUITS. */ +#define DFLT_CBT_UNUSED_OPEN_CIRCS (10) +#define MIN_CBT_UNUSED_OPEN_CIRCS 0 +#define MAX_CBT_UNUSED_OPEN_CIRCS MAX_UNUSED_OPEN_CIRCUITS /* Return true if we need more circuits for a good build timeout. * XXXX make the assumption that build timeout streams should be @@ -1188,7 +1192,10 @@ STATIC int needs_circuits_for_build(int num) { if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { - if (num < CBT_MAX_UNUSED_OPEN_CIRCUITS && + if (num < networkstatus_get_param(NULL, "cbtmaxopencircs", + DFLT_CBT_UNUSED_OPEN_CIRCS, + MIN_CBT_UNUSED_OPEN_CIRCS, + MAX_CBT_UNUSED_OPEN_CIRCS) && !circuit_build_times_disabled(get_options()) && circuit_build_times_needs_circuits_now(get_circuit_build_times())) { return 1; @@ -1197,6 +1204,25 @@ needs_circuits_for_build(int num) return 0; } +/** + * Launch the appropriate type of predicted circuit for hidden + * services, depending on our options. + */ +static void +circuit_launch_predicted_hs_circ(int flags) +{ + /* K.I.S.S. implementation of bug #23101: If we are using + * vanguards or pinned middles, pre-build a specific purpose + * for HS circs. */ + if (circuit_should_use_vanguards(CIRCUIT_PURPOSE_HS_VANGUARDS)) { + circuit_launch(CIRCUIT_PURPOSE_HS_VANGUARDS, flags); + } else { + /* If no vanguards, then no HS-specific prebuilt circuits are needed. + * Normal GENERAL circs are fine */ + circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); + } +} + /** Determine how many circuits we have open that are clean, * Make sure it's enough for all the upcoming behaviors we predict we'll have. * But put an upper bound on the total number of circuits. @@ -1250,7 +1276,7 @@ circuit_predict_and_launch_new(void) "Have %d clean circs (%d internal), need another internal " "circ for my hidden service.", num, num_internal); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); + circuit_launch_predicted_hs_circ(flags); return; } @@ -1268,7 +1294,8 @@ circuit_predict_and_launch_new(void) "Have %d clean circs (%d uptime-internal, %d internal), need" " another hidden service circ.", num, num_uptime_internal, num_internal); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); + + circuit_launch_predicted_hs_circ(flags); return; } @@ -1463,6 +1490,9 @@ circuit_expire_old_circuits_clientside(void) } else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) { if (timercmp(&circ->timestamp_began, &cutoff, OP_LT)) { if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL || + circ->purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + circ->purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || + circ->purpose == CIRCUIT_PURPOSE_HS_VANGUARDS || circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT || circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || circ->purpose == CIRCUIT_PURPOSE_TESTING || @@ -1655,6 +1685,8 @@ circuit_has_opened(origin_circuit_t *circ) hs_client_circuit_has_opened(circ); break; case CIRCUIT_PURPOSE_C_GENERAL: + case CIRCUIT_PURPOSE_C_HSDIR_GET: + case CIRCUIT_PURPOSE_S_HSDIR_POST: /* Tell any AP connections that have been waiting for a new * circuit that one is ready. */ circuit_try_attaching_streams(circ); @@ -1732,13 +1764,16 @@ circuit_build_failed(origin_circuit_t *circ) circ->cpath->prev->prev->state == CPATH_STATE_OPEN) { failed_at_last_hop = 1; } + + /* Check if we failed at first hop */ if (circ->cpath && circ->cpath->state != CPATH_STATE_OPEN && ! circ->base_.received_destroy) { /* We failed at the first hop for some reason other than a DESTROY cell. * If there's an OR connection to blame, blame it. Also, avoid this relay * for a while, and fail any one-hop directory fetches destined for it. */ - const char *n_chan_id = circ->cpath->extend_info->identity_digest; + const char *n_chan_ident = circ->cpath->extend_info->identity_digest; + tor_assert(n_chan_ident); int already_marked = 0; if (circ->base_.n_chan) { n_chan = circ->base_.n_chan; @@ -1766,17 +1801,33 @@ circuit_build_failed(origin_circuit_t *circ) "with no connection", TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier); } - if (n_chan_id && !already_marked) { - /* New guard API: we failed. */ - if (circ->guard_state) + if (!already_marked) { + /* + * If we have guard state (new guard API) and our path selection + * code actually chose a full path, then blame the failure of this + * circuit on the guard. + * + * Note that we deliberately use circuit_get_cpath_len() (and not + * circuit_get_cpath_opened_len()) because we only want to ensure + * that a full path is *chosen*. This is different than a full path + * being *built*. We only want to blame *build* failures on this + * guard. Path selection failures can happen spuriously for a number + * of reasons (such as aggressive/invalid user-specified path + * restrictions in the torrc, as well as non-user reasons like + * exitpolicy issues), and so should not be counted here. + */ + if (circ->guard_state && + circuit_get_cpath_len(circ) >= circ->build_state->desired_path_len) entry_guard_failed(&circ->guard_state); /* if there are any one-hop streams waiting on this circuit, fail * them now so they can retry elsewhere. */ - connection_ap_fail_onehop(n_chan_id, circ->build_state); + connection_ap_fail_onehop(n_chan_ident, circ->build_state); } } switch (circ->base_.purpose) { + case CIRCUIT_PURPOSE_C_HSDIR_GET: + case CIRCUIT_PURPOSE_S_HSDIR_POST: case CIRCUIT_PURPOSE_C_GENERAL: /* If we never built the circuit, note it as a failure. */ circuit_increment_failure_count(); @@ -1861,6 +1912,106 @@ have_enough_path_info(int need_exit) return router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN; } +/** + * Tell us if a circuit is a hidden service circuit. + */ +int +circuit_purpose_is_hidden_service(uint8_t purpose) +{ + if (purpose == CIRCUIT_PURPOSE_HS_VANGUARDS) { + return 1; + } + + /* Client-side purpose */ + if (purpose >= CIRCUIT_PURPOSE_C_HS_MIN_ && + purpose <= CIRCUIT_PURPOSE_C_HS_MAX_) { + return 1; + } + + /* Service-side purpose */ + if (purpose >= CIRCUIT_PURPOSE_S_HS_MIN_ && + purpose <= CIRCUIT_PURPOSE_S_HS_MAX_) { + return 1; + } + + return 0; +} + +/** + * Return true if this circuit purpose should use vanguards + * or pinned Layer2 or Layer3 guards. + * + * This function takes both the circuit purpose and the + * torrc options for pinned middles/vanguards into account + * (ie: the circuit must be a hidden service circuit and + * vanguards/pinned middles must be enabled for it to return + * true). + */ +int +circuit_should_use_vanguards(uint8_t purpose) +{ + const or_options_t *options = get_options(); + + /* Only hidden service circuits use vanguards */ + if (!circuit_purpose_is_hidden_service(purpose)) + return 0; + + /* Pinned middles are effectively vanguards */ + if (options->HSLayer2Nodes || options->HSLayer3Nodes) + return 1; + + return 0; +} + +/** + * Return true for the set of conditions for which it is OK to use + * a cannibalized circuit. + * + * Don't cannibalize for onehops, or tor2web, or certain purposes. + */ +static int +circuit_should_cannibalize_to_build(uint8_t purpose_to_build, + int has_extend_info, + int onehop_tunnel, + int need_specific_rp) +{ + + /* Do not try to cannibalize if this is a one hop circuit, or + * is a tor2web/special rp. */ + if (onehop_tunnel || need_specific_rp) { + return 0; + } + + /* Don't try to cannibalize for general purpose circuits that do not + * specify a custom exit. */ + if (purpose_to_build == CIRCUIT_PURPOSE_C_GENERAL && !has_extend_info) { + return 0; + } + + /* Don't cannibalize for testing circuits. We want to see if they + * complete normally. Also don't cannibalize for vanguard-purpose + * circuits, since those are specially pre-built for later + * cannibalization by the actual specific circuit types that need + * vanguards. + */ + if (purpose_to_build == CIRCUIT_PURPOSE_TESTING || + purpose_to_build == CIRCUIT_PURPOSE_HS_VANGUARDS) { + return 0; + } + + /* For vanguards, the server-side intro circ is not cannibalized + * because we pre-build 4 hop HS circuits, and it only needs a 3 hop + * circuit. It is also long-lived, so it is more important that + * it have lower latency than get built fast. + */ + if (circuit_should_use_vanguards(purpose_to_build) && + purpose_to_build == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) { + return 0; + } + + return 1; +} + /** Launch a new circuit with purpose <b>purpose</b> and exit node * <b>extend_info</b> (or NULL to select a random exit node). If flags * contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If @@ -1878,6 +2029,11 @@ circuit_launch_by_extend_info(uint8_t purpose, int have_path = have_enough_path_info(! (flags & CIRCLAUNCH_IS_INTERNAL) ); int need_specific_rp = 0; + /* Keep some stats about our attempts to launch HS rendezvous circuits */ + if (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) { + hs_stats_note_service_rendezvous_launch(); + } + if (!onehop_tunnel && (!router_have_minimum_dir_info() || !have_path)) { log_debug(LD_CIRC,"Haven't %s yet; canceling " "circuit launch.", @@ -1895,9 +2051,12 @@ circuit_launch_by_extend_info(uint8_t purpose, need_specific_rp = 1; } - if ((extend_info || purpose != CIRCUIT_PURPOSE_C_GENERAL) && - purpose != CIRCUIT_PURPOSE_TESTING && - !onehop_tunnel && !need_specific_rp) { + /* If we can/should cannibalize another circuit to build this one, + * then do so. */ + if (circuit_should_cannibalize_to_build(purpose, + extend_info != NULL, + onehop_tunnel, + need_specific_rp)) { /* see if there are appropriate circs available to cannibalize. */ /* XXX if we're planning to add a hop, perhaps we want to look for * internal circs rather than exit circs? -RD */ @@ -1952,6 +2111,8 @@ circuit_launch_by_extend_info(uint8_t purpose, case CIRCUIT_PURPOSE_C_INTRODUCING: case CIRCUIT_PURPOSE_S_CONNECT_REND: case CIRCUIT_PURPOSE_C_GENERAL: + case CIRCUIT_PURPOSE_S_HSDIR_POST: + case CIRCUIT_PURPOSE_C_HSDIR_GET: case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* need to add a new hop */ tor_assert(extend_info); @@ -2216,7 +2377,9 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, /* If we have specified a particular exit node for our * connection, then be sure to open a circuit to that exit node. */ - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL || + desired_circuit_purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || + desired_circuit_purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) { if (conn->chosen_exit_name) { const node_t *r; int opt = conn->chosen_exit_optional; @@ -2324,7 +2487,9 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, /* Now trigger things that need to happen when we launch circuits */ - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL || + desired_circuit_purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + desired_circuit_purpose == CIRCUIT_PURPOSE_S_HSDIR_POST) { /* We just caused a circuit to get built because of this stream. * If this stream has caused a _lot_ of circuits to be built, that's * a bad sign: we should tell the user. */ @@ -2453,6 +2618,8 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ, /* See if we can use optimistic data on this circuit */ if (optimistic_data_enabled() && (circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL || + circ->base_.purpose == CIRCUIT_PURPOSE_C_HSDIR_GET || + circ->base_.purpose == CIRCUIT_PURPOSE_S_HSDIR_POST || circ->base_.purpose == CIRCUIT_PURPOSE_C_REND_JOINED)) apconn->may_use_optimistic_data = 1; else @@ -2573,6 +2740,39 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, return 1; } +/** + * Return an appropriate circuit purpose for non-rend streams. + * We don't handle rends here because a rend stream triggers two + * circuit builds with different purposes, so it is handled elsewhere. + * + * This function just figures out what type of hsdir activity this is, + * and tells us. Everything else is general. + */ +static int +connection_ap_get_nonrend_circ_purpose(const entry_connection_t *conn) +{ + const connection_t *base_conn = ENTRY_TO_CONN(conn); + tor_assert_nonfatal(!connection_edge_is_rendezvous_stream( + ENTRY_TO_EDGE_CONN(conn))); + + if (base_conn->linked_conn && + base_conn->linked_conn->type == CONN_TYPE_DIR) { + /* Set a custom purpose for hsdir activity */ + if (base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2 || + base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_HSDESC) { + return CIRCUIT_PURPOSE_S_HSDIR_POST; + } else if (base_conn->linked_conn->purpose + == DIR_PURPOSE_FETCH_RENDDESC_V2 || + base_conn->linked_conn->purpose + == DIR_PURPOSE_FETCH_HSDESC) { + return CIRCUIT_PURPOSE_C_HSDIR_GET; + } + } + + /* All other purposes are general for now */ + return CIRCUIT_PURPOSE_C_GENERAL; +} + /** Try to find a safe live circuit for stream <b>conn</b>. If we find one, * attach the stream, send appropriate cells, and return 1. Otherwise, * try to launch new circuit(s) for the stream. If we can launch @@ -2671,9 +2871,12 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) } /* Find the circuit that we should use, if there is one. Otherwise - * launch it. */ - retval = circuit_get_open_circ_or_launch( - conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); + * launch it + */ + retval = circuit_get_open_circ_or_launch(conn, + connection_ap_get_nonrend_circ_purpose(conn), + &circ); + if (retval < 1) { /* We were either told "-1" (complete failure) or 0 (circuit in * progress); we can't attach this stream yet. */ diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 2b0f983f1a..71c818b978 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -63,6 +63,9 @@ int hostname_in_track_host_exits(const or_options_t *options, const char *address); void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ); +int circuit_purpose_is_hidden_service(uint8_t); +int circuit_should_use_vanguards(uint8_t); + #ifdef TOR_UNIT_TESTS /* Used only by circuituse.c and test_circuituse.c */ diff --git a/src/or/command.c b/src/or/command.c index 185596a65a..7280be1396 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -286,7 +286,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) "Received create cell but we're shutting down. Sending back " "destroy."); channel_send_destroy(cell->circ_id, chan, - END_CIRC_REASON_HIBERNATING); + END_CIRC_REASON_HIBERNATING); return; } diff --git a/src/or/config.c b/src/or/config.c index 73cc7c2732..d76a53a375 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -18,7 +18,7 @@ * inspecting values that are calculated as a result of the * configured options. * - * <h3>How to add new options</h3> + * <h3>How to add new options</h3> * * To add new items to the torrc, there are a minimum of three places to edit: * <ul> @@ -83,6 +83,7 @@ #include "dns.h" #include "dos.h" #include "entrynodes.h" +#include "git_revision.h" #include "geoip.h" #include "hibernate.h" #include "main.h" @@ -170,6 +171,7 @@ static config_abbrev_t option_abbrevs_[] = { { "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0}, { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0}, { "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0}, + { "SocksSocketsGroupWritable", "UnixSocksGroupWritable", 0, 1}, { NULL, NULL, 0, 0}, }; @@ -253,6 +255,8 @@ static config_var_t option_vars_[] = { V(BridgeRecordUsageByCountry, BOOL, "1"), V(BridgeRelay, BOOL, "0"), V(BridgeDistribution, STRING, NULL), + VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL), + V(CacheDirectoryGroupReadable, BOOL, "0"), V(CellStatistics, BOOL, "0"), V(PaddingStatistics, BOOL, "1"), V(LearnCircuitBuildTimeout, BOOL, "1"), @@ -281,12 +285,12 @@ static config_var_t option_vars_[] = { V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(ControlSocketsGroupWritable, BOOL, "0"), - V(SocksSocketsGroupWritable, BOOL, "0"), + V(UnixSocksGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), V(CountPrivateBandwidth, BOOL, "0"), - V(DataDirectory, FILENAME, NULL), + VAR("DataDirectory", FILENAME, DataDirectory_option, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), @@ -378,6 +382,7 @@ static config_var_t option_vars_[] = { V(GuardLifetime, INTERVAL, "0 minutes"), V(HardwareAccel, BOOL, "0"), V(HeartbeatPeriod, INTERVAL, "6 hours"), + V(MainloopStats, BOOL, "0"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@ -412,6 +417,10 @@ static config_var_t option_vars_[] = { V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), V(Socks5ProxyPassword, STRING, NULL), + VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL), + V(KeyDirectoryGroupReadable, BOOL, "0"), + VAR("_HSLayer2Nodes", ROUTERSET, HSLayer2Nodes, NULL), + VAR("_HSLayer3Nodes", ROUTERSET, HSLayer3Nodes, NULL), V(KeepalivePeriod, INTERVAL, "5 minutes"), V(KeepBindCapabilities, AUTOBOOL, "auto"), VAR("Log", LINELIST, Logs, NULL), @@ -419,6 +428,7 @@ static config_var_t option_vars_[] = { V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), V(SyslogIdentityTag, STRING, NULL), + V(AndroidIdentityTag, STRING, NULL), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@ -504,6 +514,7 @@ static config_var_t option_vars_[] = { V(RendPostPeriod, INTERVAL, "1 hour"), V(RephistTrackTime, INTERVAL, "24 hours"), V(RunAsDaemon, BOOL, "0"), + V(ReducedExitPolicy, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused V(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), @@ -578,10 +589,12 @@ static config_var_t option_vars_[] = { VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), + VAR("__DisableSignalHandlers", BOOL, DisableSignalHandlers, "0"), VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), + VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " "300, 900, 2147483647"), @@ -751,7 +764,7 @@ static int parse_ports(or_options_t *options, int validate_only, static int check_server_ports(const smartlist_t *ports, const or_options_t *options, int *num_low_ports_out); -static int validate_data_directory(or_options_t *options); +static int validate_data_directories(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); static int options_init_logs(const or_options_t *old_options, @@ -768,6 +781,8 @@ static int options_validate_cb(void *old_options, void *options, int from_setconf, char **msg); static uint64_t compute_real_max_mem_in_queues(const uint64_t val, int log_guess); +static void cleanup_protocol_warning_severity_level(void); +static void set_protocol_warning_severity_level(int warning_severity); /** Magic value for or_options_t. */ #define OR_OPTIONS_MAGIC 9090909 @@ -796,7 +811,7 @@ static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; /** Name of the most recently read torrc-defaults file.*/ -static char *torrc_defaults_fname; +static char *torrc_defaults_fname = NULL; /** Configuration options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Non-configuration options set by the command line */ @@ -810,6 +825,8 @@ static smartlist_t *configured_ports = NULL; /** True iff we're currently validating options, and any calls to * get_options() are likely to be bugs. */ static int in_option_validation = 0; +/* True iff we've initialized libevent */ +static int libevent_initialized = 0; /** Return the contents of our frontpage string, or NULL if not configured. */ MOCK_IMPL(const char*, @@ -854,9 +871,12 @@ set_options(or_options_t *new_val, char **msg) return -1; } if (options_act(old_options) < 0) { /* acting on the options failed. die. */ - log_err(LD_BUG, - "Acting on config options left us in a broken state. Dying."); - exit(1); + if (! tor_event_loop_shutdown_is_pending()) { + log_err(LD_BUG, + "Acting on config options left us in a broken state. Dying."); + tor_shutdown_event_loop_and_exit(1); + } + return -1; } /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is * just starting up then the old_options will be undefined. */ @@ -898,8 +918,6 @@ set_options(or_options_t *new_val, char **msg) return 0; } -extern const char tor_git_revision[]; /* from tor_main.c */ - /** The version of this Tor process, as parsed. */ static char *the_tor_version = NULL; /** A shorter version of this Tor process's version, for export in our router @@ -939,7 +957,7 @@ get_short_version(void) /** Release additional memory allocated in options */ STATIC void -or_options_free(or_options_t *options) +or_options_free_(or_options_t *options) { if (!options) return; @@ -954,6 +972,13 @@ or_options_free(or_options_t *options) SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i)); smartlist_free(options->SchedulerTypes_); } + if (options->FilesOpenedByIncludes) { + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f)); + smartlist_free(options->FilesOpenedByIncludes); + } + tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); + tor_free(options->KeyDirectory); tor_free(options->BridgePassword_AuthDigest_); tor_free(options->command_arg); tor_free(options->master_key_fname); @@ -990,6 +1015,11 @@ config_free_all(void) tor_free(the_short_tor_version); tor_free(the_tor_version); + + cleanup_protocol_warning_severity_level(); + + have_parsed_cmdline = 0; + libevent_initialized = 0; } /** Make <b>address</b> -- a piece of information related to our operation as @@ -1053,17 +1083,46 @@ escaped_safe_str(const char *address) * The severity level that should be used for warnings of severity * LOG_PROTOCOL_WARN. * - * We keep this outside the options, in case somebody needs to use - * LOG_PROTOCOL_WARN while an option transition is happening. + * We keep this outside the options, and we use an atomic_counter_t, in case + * one thread needs to use LOG_PROTOCOL_WARN while an option transition is + * happening in the main thread. */ -static int protocol_warning_severity_level = LOG_WARN; +static atomic_counter_t protocol_warning_severity_level; /** Return the severity level that should be used for warnings of severity * LOG_PROTOCOL_WARN. */ int get_protocol_warning_severity_level(void) { - return protocol_warning_severity_level; + return (int) atomic_counter_get(&protocol_warning_severity_level); +} + +/** Set the protocol warning severity level to <b>severity</b>. */ +static void +set_protocol_warning_severity_level(int warning_severity) +{ + atomic_counter_exchange(&protocol_warning_severity_level, + warning_severity); +} + +/** + * Initialize the log warning severity level for protocol warnings. Call + * only once at startup. + */ +void +init_protocol_warning_severity_level(void) +{ + atomic_counter_init(&protocol_warning_severity_level); + set_protocol_warning_severity_level(LOG_WARN); +} + +/** + * Tear down protocol_warning_severity_level. + */ +static void +cleanup_protocol_warning_severity_level(void) +{ + atomic_counter_destroy(&protocol_warning_severity_level); } /** List of default directory authorities */ @@ -1263,6 +1322,69 @@ consider_adding_dir_servers(const or_options_t *options, return 0; } +/** + * Make sure that <b>directory</b> exists, with appropriate ownership and + * permissions (as modified by <b>group_readable</b>). If <b>create</b>, + * create the directory if it is missing. Return 0 on success. + * On failure, return -1 and set *<b>msg_out</b>. + */ +static int +check_and_create_data_directory(int create, + const char *directory, + int group_readable, + const char *owner, + char **msg_out) +{ + cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK; + if (group_readable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(directory, + cpd_opts, + owner) < 0) { + tor_asprintf(msg_out, + "Couldn't %s private data directory \"%s\"", + create ? "create" : "access", + directory); + return -1; + } + +#ifndef _WIN32 + if (group_readable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(directory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + directory, strerror(errno)); + } + } +#endif /* !defined(_WIN32) */ + + return 0; +} + +/** + * Ensure that our keys directory exists, with appropriate permissions. + * Return 0 on success, -1 on failure. + */ +int +create_keys_directory(const or_options_t *options) +{ + /* Make sure DataDirectory exists, and is private. */ + cpd_check_t cpd_opts = CPD_CREATE; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + return -1; + } + + /* Check the key directory. */ + if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) { + return -1; + } + return 0; +} + /* Helps determine flags to pass to switch_id. */ static int have_low_ports = -1; @@ -1277,7 +1399,6 @@ options_act_reversible(const or_options_t *old_options, char **msg) { smartlist_t *new_listeners = smartlist_new(); smartlist_t *replaced_listeners = smartlist_new(); - static int libevent_initialized = 0; or_options_t *options = get_options_mutable(); int running_tor = options->command == CMD_RUN_TOR; int set_conn_limit = 0; @@ -1417,29 +1538,30 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Ensure data directory is private; create if possible. */ - cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, - cpd_opts, - options->User)<0) { - tor_asprintf(msg, - "Couldn't access/create private data directory \"%s\"", - options->DataDirectory); - + /* It's okay to do this in "options_act_reversible()" even though it isn't + * actually reversible, since you can't change the DataDirectory while + * Tor is running. */ + if (check_and_create_data_directory(running_tor /* create */, + options->DataDirectory, + options->DataDirectoryGroupReadable, + options->User, + msg) < 0) { goto done; - /* No need to roll back, since you can't change the value. */ } - -#ifndef _WIN32 - if (options->DataDirectoryGroupReadable) { - /* Only new dirs created get new opts, also enforce group read. */ - if (chmod(options->DataDirectory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable: %s", - options->DataDirectory, strerror(errno)); - } + if (check_and_create_data_directory(running_tor /* create */, + options->KeyDirectory, + options->KeyDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + if (check_and_create_data_directory(running_tor /* create */, + options->CacheDirectory, + options->CacheDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; } -#endif /* !defined(_WIN32) */ /* Bail out at this point if we're not going to be a client or server: * we don't run Tor itself. */ @@ -1576,6 +1698,8 @@ options_need_geoip_info(const or_options_t *options, const char **reason_out) routerset_needs_geoip(options->ExitNodes) || routerset_needs_geoip(options->ExcludeExitNodes) || routerset_needs_geoip(options->ExcludeNodes) || + routerset_needs_geoip(options->HSLayer2Nodes) || + routerset_needs_geoip(options->HSLayer3Nodes) || routerset_needs_geoip(options->Tor2webRendezvousPoints); if (routerset_usage && reason_out) { @@ -1616,32 +1740,46 @@ get_effective_bwburst(const or_options_t *options) return (uint32_t)bw; } +/* Used in the various options_transition_affects* functions. */ +#define YES_IF_CHANGED_BOOL(opt) \ + if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_STRING(opt) \ + if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_LINELIST(opt) \ + if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_SMARTLIST(opt) \ + if (!CFG_EQ_SMARTLIST(old_options, new_options, opt)) return 1; +#define YES_IF_CHANGED_ROUTERSET(opt) \ + if (!CFG_EQ_ROUTERSET(old_options, new_options, opt)) return 1; + /** * Return true if changing the configuration from <b>old</b> to <b>new</b> - * affects the guard susbsystem. + * affects the guard subsystem. */ static int -options_transition_affects_guards(const or_options_t *old, - const or_options_t *new) +options_transition_affects_guards(const or_options_t *old_options, + const or_options_t *new_options) { /* NOTE: Make sure this function stays in sync with - * entry_guards_set_filtered_flags */ - - tor_assert(old); - tor_assert(new); - - return - (old->UseEntryGuards != new->UseEntryGuards || - old->UseBridges != new->UseBridges || - old->ClientUseIPv4 != new->ClientUseIPv4 || - old->ClientUseIPv6 != new->ClientUseIPv6 || - old->FascistFirewall != new->FascistFirewall || - !routerset_equal(old->ExcludeNodes, new->ExcludeNodes) || - !routerset_equal(old->EntryNodes, new->EntryNodes) || - !smartlist_strings_eq(old->FirewallPorts, new->FirewallPorts) || - !config_lines_eq(old->Bridges, new->Bridges) || - !config_lines_eq(old->ReachableORAddresses, new->ReachableORAddresses) || - !config_lines_eq(old->ReachableDirAddresses, new->ReachableDirAddresses)); + * node_passes_guard_filter */ + tor_assert(old_options); + tor_assert(new_options); + + YES_IF_CHANGED_BOOL(UseEntryGuards); + YES_IF_CHANGED_BOOL(UseBridges); + YES_IF_CHANGED_BOOL(ClientUseIPv4); + YES_IF_CHANGED_BOOL(ClientUseIPv6); + YES_IF_CHANGED_BOOL(FascistFirewall); + YES_IF_CHANGED_ROUTERSET(ExcludeNodes); + YES_IF_CHANGED_ROUTERSET(EntryNodes); + YES_IF_CHANGED_SMARTLIST(FirewallPorts); + YES_IF_CHANGED_LINELIST(Bridges); + YES_IF_CHANGED_LINELIST(ReachableORAddresses); + YES_IF_CHANGED_LINELIST(ReachableDirAddresses); + + return 0; } /** Fetch the active option list, and take actions based on it. All of the @@ -1704,13 +1842,16 @@ options_act(const or_options_t *old_options) return -1; } - if (options->ProtocolWarnings) - protocol_warning_severity_level = LOG_WARN; - else - protocol_warning_severity_level = LOG_INFO; + { + int warning_severity = options->ProtocolWarnings ? LOG_WARN : LOG_INFO; + set_protocol_warning_severity_level(warning_severity); + } - if (consider_adding_dir_servers(options, old_options) < 0) + if (consider_adding_dir_servers(options, old_options) < 0) { + // XXXX This should get validated earlier, and committed here, to + // XXXX lower opportunities for reaching an error case. return -1; + } if (rend_non_anonymous_mode_enabled(options)) { log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " @@ -1719,6 +1860,7 @@ options_act(const or_options_t *old_options) #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ + // XXXX This should move into options_validate() if (!options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was compiled to run in " "'tor2web mode'. It can only be run with the Tor2webMode torrc " @@ -1727,6 +1869,7 @@ options_act(const or_options_t *old_options) } /* LCOV_EXCL_STOP */ #else /* !(defined(ENABLE_TOR2WEB_MODE)) */ + // XXXX This should move into options_validate() if (options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " "'tor2web mode'. It cannot be run with the Tor2webMode torrc " @@ -1754,9 +1897,11 @@ options_act(const or_options_t *old_options) for (cl = options->Bridges; cl; cl = cl->next) { bridge_line_t *bridge_line = parse_bridge_line(cl->value); if (!bridge_line) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated Bridge line could not be added!"); return -1; + // LCOV_EXCL_STOP } bridge_add_from_config(bridge_line); } @@ -1764,15 +1909,37 @@ options_act(const or_options_t *old_options) } if (running_tor && hs_config_service_all(options, 0)<0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated hidden services line could not be added!"); return -1; + // LCOV_EXCL_STOP } if (running_tor && rend_parse_service_authorization(options, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated client authorization for " "hidden services could not be added!"); return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && !old_options && options->OwningControllerFD != -1) { +#ifdef _WIN32 + log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. " + "If you need it, tell the Tor developers."); + return -1; +#else + const unsigned ctrl_flags = + CC_LOCAL_FD_IS_OWNER | + CC_LOCAL_FD_IS_AUTHENTICATED; + tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD; + if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) { + log_warn(LD_CONFIG, "Could not add local controller connection with " + "given FD."); + return -1; + } +#endif /* defined(_WIN32) */ } /* Load state */ @@ -1795,10 +1962,12 @@ options_act(const or_options_t *old_options) if (options->ClientTransportPlugin) { for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ClientTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1806,10 +1975,12 @@ options_act(const or_options_t *old_options) if (options->ServerTransportPlugin && server_mode(options)) { for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 1) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ServerTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1895,8 +2066,10 @@ options_act(const or_options_t *old_options) /* Set up accounting */ if (accounting_parse_options(options, 0)<0) { - log_warn(LD_CONFIG,"Error in accounting options"); + // LCOV_EXCL_START + log_warn(LD_BUG,"Error in previously validated accounting options"); return -1; + // LCOV_EXCL_STOP } if (accounting_is_enabled(options)) configure_accounting(time(NULL)); @@ -1919,6 +2092,7 @@ options_act(const or_options_t *old_options) char *http_authenticator; http_authenticator = alloc_http_authenticator(options->BridgePassword); if (!http_authenticator) { + // XXXX This should get validated in options_validate(). log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " "BridgePassword."); return -1; @@ -1931,9 +2105,12 @@ options_act(const or_options_t *old_options) } if (parse_outbound_addresses(options, 0, &msg) < 0) { - log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg); + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); tor_free(msg); return -1; + // LCOV_EXCL_STOP } config_maybe_load_geoip_files_(options, old_options); @@ -1964,6 +2141,10 @@ options_act(const or_options_t *old_options) options->ExcludeExitNodes) || !routerset_equal(old_options->EntryNodes, options->EntryNodes) || !routerset_equal(old_options->ExitNodes, options->ExitNodes) || + !routerset_equal(old_options->HSLayer2Nodes, + options->HSLayer2Nodes) || + !routerset_equal(old_options->HSLayer3Nodes, + options->HSLayer3Nodes) || !routerset_equal(old_options->Tor2webRendezvousPoints, options->Tor2webRendezvousPoints) || options->StrictNodes != old_options->StrictNodes) { @@ -2057,6 +2238,10 @@ options_act(const or_options_t *old_options) if (options->PerConnBWRate != old_options->PerConnBWRate || options->PerConnBWBurst != old_options->PerConnBWBurst) connection_or_update_token_buckets(get_connection_array(), options); + + if (options->MainloopStats != old_options->MainloopStats) { + reset_main_loop_counters(); + } } /* Only collect directory-request statistics on relays and bridges. */ @@ -2962,7 +3147,7 @@ warn_if_option_path_is_relative(const char *option, return 0; } -/** Scan <b>options</b> for occurances of relative file/directory +/** Scan <b>options</b> for occurrences of relative file/directory * path and log a warning whenever it is found. * * Return 1 if there were relative paths; 0 otherwise. @@ -3173,12 +3358,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (parse_outbound_addresses(options, 1, msg) < 0) return -1; - if (validate_data_directory(options)<0) + if (validate_data_directories(options)<0) REJECT("Invalid DataDirectory"); if (options->Nickname == NULL) { if (server_mode(options)) { - options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); + options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); } } else { if (!is_legal_nickname(options->Nickname)) { @@ -3198,9 +3383,9 @@ options_validate(or_options_t *old_options, or_options_t *options, /* Special case on first boot if no Log options are given. */ if (!options->Logs && !options->RunAsDaemon && !from_setconf) { if (quiet_level == 0) - config_line_append(&options->Logs, "Log", "notice stdout"); + config_line_append(&options->Logs, "Log", "notice stdout"); else if (quiet_level == 1) - config_line_append(&options->Logs, "Log", "warn stdout"); + config_line_append(&options->Logs, "Log", "warn stdout"); } /* Validate the tor_log(s) */ @@ -4546,125 +4731,61 @@ options_transition_allowed(const or_options_t *old, if (!old) return 0; - if (!opt_streq(old->PidFile, new_val->PidFile)) { - *msg = tor_strdup("PidFile is not allowed to change."); - return -1; - } - - if (old->RunAsDaemon != new_val->RunAsDaemon) { - *msg = tor_strdup("While Tor is running, changing RunAsDaemon " - "is not allowed."); - return -1; - } - - if (old->Sandbox != new_val->Sandbox) { - *msg = tor_strdup("While Tor is running, changing Sandbox " - "is not allowed."); - return -1; - } - - if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) { - tor_asprintf(msg, - "While Tor is running, changing DataDirectory " - "(\"%s\"->\"%s\") is not allowed.", - old->DataDirectory, new_val->DataDirectory); - return -1; - } - - if (!opt_streq(old->User, new_val->User)) { - *msg = tor_strdup("While Tor is running, changing User is not allowed."); - return -1; - } - - if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) { - *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is " - "not allowed."); - return -1; - } - - if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) { - *msg = tor_strdup("While Tor is running, changing " - "SyslogIdentityTag is not allowed."); - return -1; - } - - if ((old->HardwareAccel != new_val->HardwareAccel) - || !opt_streq(old->AccelName, new_val->AccelName) - || !opt_streq(old->AccelDir, new_val->AccelDir)) { - *msg = tor_strdup("While Tor is running, changing OpenSSL hardware " - "acceleration engine is not allowed."); - return -1; - } - - if (old->TestingTorNetwork != new_val->TestingTorNetwork) { - *msg = tor_strdup("While Tor is running, changing TestingTorNetwork " - "is not allowed."); - return -1; - } - - if (old->DisableAllSwap != new_val->DisableAllSwap) { - *msg = tor_strdup("While Tor is running, changing DisableAllSwap " - "is not allowed."); - return -1; - } - - if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) { - *msg = tor_strdup("While Tor is running, changing TokenBucketRefill" - "Interval is not allowed"); - return -1; - } - - if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceSingleHopMode is not allowed."); - return -1; - } - - if (old->HiddenServiceNonAnonymousMode != - new_val->HiddenServiceNonAnonymousMode) { - *msg = tor_strdup("While Tor is running, changing " - "HiddenServiceNonAnonymousMode is not allowed."); - return -1; - } - - if (old->DisableDebuggerAttachment && - !new_val->DisableDebuggerAttachment) { - *msg = tor_strdup("While Tor is running, disabling " - "DisableDebuggerAttachment is not allowed."); - return -1; - } - - if (old->NoExec && !new_val->NoExec) { - *msg = tor_strdup("While Tor is running, disabling " - "NoExec is not allowed."); - return -1; - } +#define BAD_CHANGE_TO(opt, how) do { \ + *msg = tor_strdup("While Tor is running"how", changing " #opt \ + " is not allowed"); \ + return -1; \ + } while (0) + +#define NO_CHANGE_BOOL(opt) \ + if (! CFG_EQ_BOOL(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) BAD_CHANGE_TO(opt,"") +#define NO_CHANGE_STRING(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) BAD_CHANGE_TO(opt,"") + + NO_CHANGE_STRING(PidFile); + NO_CHANGE_BOOL(RunAsDaemon); + NO_CHANGE_BOOL(Sandbox); + NO_CHANGE_STRING(DataDirectory); + NO_CHANGE_STRING(KeyDirectory); + NO_CHANGE_STRING(CacheDirectory); + NO_CHANGE_STRING(User); + NO_CHANGE_BOOL(KeepBindCapabilities); + NO_CHANGE_STRING(SyslogIdentityTag); + NO_CHANGE_STRING(AndroidIdentityTag); + NO_CHANGE_BOOL(HardwareAccel); + NO_CHANGE_STRING(AccelName); + NO_CHANGE_STRING(AccelDir); + NO_CHANGE_BOOL(TestingTorNetwork); + NO_CHANGE_BOOL(DisableAllSwap); + NO_CHANGE_INT(TokenBucketRefillInterval); + NO_CHANGE_BOOL(HiddenServiceSingleHopMode); + NO_CHANGE_BOOL(HiddenServiceNonAnonymousMode); + NO_CHANGE_BOOL(DisableDebuggerAttachment); + NO_CHANGE_BOOL(NoExec); + NO_CHANGE_INT(OwningControllerFD); + NO_CHANGE_BOOL(DisableSignalHandlers); if (sandbox_is_active()) { -#define SB_NOCHANGE_STR(opt) \ - do { \ - if (! opt_streq(old->opt, new_val->opt)) { \ - *msg = tor_strdup("Can't change " #opt " while Sandbox is active"); \ - return -1; \ - } \ - } while (0) +#define SB_NOCHANGE_STR(opt) \ + if (! CFG_EQ_STRING(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_LINELIST(opt) \ + if (! CFG_EQ_LINELIST(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") +#define SB_NOCHANGE_INT(opt) \ + if (! CFG_EQ_INT(old, new_val, opt)) \ + BAD_CHANGE_TO(opt," with Sandbox active") SB_NOCHANGE_STR(Address); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); SB_NOCHANGE_STR(ExtORPortCookieAuthFile); + SB_NOCHANGE_LINELIST(Logs); + SB_NOCHANGE_INT(ConnLimit); -#undef SB_NOCHANGE_STR - - if (! config_lines_eq(old->Logs, new_val->Logs)) { - *msg = tor_strdup("Can't change Logs while Sandbox is active"); - return -1; - } - if (old->ConnLimit != new_val->ConnLimit) { - *msg = tor_strdup("Can't change ConnLimit while Sandbox is active"); - return -1; - } if (server_mode(old) != server_mode(new_val)) { *msg = tor_strdup("Can't start/stop being a server while " "Sandbox is active"); @@ -4672,6 +4793,13 @@ options_transition_allowed(const or_options_t *old, } } +#undef SB_NOCHANGE_LINELIST +#undef SB_NOCHANGE_STR +#undef SB_NOCHANGE_INT +#undef BAD_CHANGE_TO +#undef NO_CHANGE_BOOL +#undef NO_CHANGE_INT +#undef NO_CHANGE_STRING return 0; } @@ -4681,21 +4809,19 @@ static int options_transition_affects_workers(const or_options_t *old_options, const or_options_t *new_options) { - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - old_options->NumCPUs != new_options->NumCPUs || - !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) || - old_options->ServerDNSSearchDomains != - new_options->ServerDNSSearchDomains || - old_options->SafeLogging_ != new_options->SafeLogging_ || - old_options->ClientOnly != new_options->ClientOnly || - server_mode(old_options) != server_mode(new_options) || - public_server_mode(old_options) != public_server_mode(new_options) || - !config_lines_eq(old_options->Logs, new_options->Logs) || - old_options->LogMessageDomains != new_options->LogMessageDomains) + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_INT(NumCPUs); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_BOOL(ServerDNSSearchDomains); + YES_IF_CHANGED_BOOL(SafeLogging_); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(LogMessageDomains); + YES_IF_CHANGED_LINELIST(Logs); + + if (server_mode(old_options) != server_mode(new_options) || + public_server_mode(old_options) != public_server_mode(new_options)) return 1; - /* Check whether log options match. */ - /* Nothing that changed matters. */ return 0; } @@ -4708,37 +4834,34 @@ options_transition_affects_descriptor(const or_options_t *old_options, { /* XXX We can be smarter here. If your DirPort isn't being * published and you just turned it off, no need to republish. Etc. */ - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - !opt_streq(old_options->Nickname,new_options->Nickname) || - !opt_streq(old_options->Address,new_options->Address) || - !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || - old_options->ExitRelay != new_options->ExitRelay || - old_options->ExitPolicyRejectPrivate != - new_options->ExitPolicyRejectPrivate || - old_options->ExitPolicyRejectLocalInterfaces != - new_options->ExitPolicyRejectLocalInterfaces || - old_options->IPv6Exit != new_options->IPv6Exit || - !config_lines_eq(old_options->ORPort_lines, - new_options->ORPort_lines) || - !config_lines_eq(old_options->DirPort_lines, - new_options->DirPort_lines) || - old_options->ClientOnly != new_options->ClientOnly || - old_options->DisableNetwork != new_options->DisableNetwork || - old_options->PublishServerDescriptor_ != - new_options->PublishServerDescriptor_ || - get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || + + YES_IF_CHANGED_STRING(DataDirectory); + YES_IF_CHANGED_STRING(Nickname); + YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(ExitPolicy); + YES_IF_CHANGED_BOOL(ExitRelay); + YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); + YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces); + YES_IF_CHANGED_BOOL(IPv6Exit); + YES_IF_CHANGED_LINELIST(ORPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_LINELIST(DirPort_lines); + YES_IF_CHANGED_BOOL(ClientOnly); + YES_IF_CHANGED_BOOL(DisableNetwork); + YES_IF_CHANGED_BOOL(PublishServerDescriptor_); + YES_IF_CHANGED_STRING(ContactInfo); + YES_IF_CHANGED_STRING(BridgeDistribution); + YES_IF_CHANGED_LINELIST(MyFamily); + YES_IF_CHANGED_STRING(AccountingStart); + YES_IF_CHANGED_INT(AccountingMax); + YES_IF_CHANGED_INT(AccountingRule); + YES_IF_CHANGED_BOOL(DirCache); + YES_IF_CHANGED_BOOL(AssumeReachable); + + if (get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || get_effective_bwburst(old_options) != get_effective_bwburst(new_options) || - !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || - !opt_streq(old_options->BridgeDistribution, - new_options->BridgeDistribution) || - !config_lines_eq(old_options->MyFamily, new_options->MyFamily) || - !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || - old_options->AccountingMax != new_options->AccountingMax || - old_options->AccountingRule != new_options->AccountingRule || - public_server_mode(old_options) != public_server_mode(new_options) || - old_options->DirCache != new_options->DirCache || - old_options->AssumeReachable != new_options->AssumeReachable) + public_server_mode(old_options) != public_server_mode(new_options)) return 1; return 0; @@ -5052,7 +5175,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) /** Read a configuration file into <b>options</b>, finding the configuration * file location based on the command line. After loading the file * call options_init_from_string() to load the config. - * Return 0 if success, -1 if failure. */ + * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit + * anyway. */ int options_init_from_torrc(int argc, char **argv) { @@ -5079,22 +5203,22 @@ options_init_from_torrc(int argc, char **argv) if (config_line_find(cmdline_only_options, "-h") || config_line_find(cmdline_only_options, "--help")) { print_usage(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-torrc-options")) { /* For validating whether we've documented everything. */ list_torrc_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-deprecated-options")) { /* For validating whether what we have deprecated really exists. */ list_deprecated_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--library-versions")) { @@ -5122,7 +5246,7 @@ options_init_from_torrc(int argc, char **argv) tor_compress_header_version_str(ZSTD_METHOD)); } //TODO: Hex versions? - exit(0); + return 1; } command = CMD_RUN_TOR; @@ -5183,7 +5307,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; } else { log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5192,7 +5317,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->change_key_passphrase = 1; } else { log_err(LD_CONFIG, "--newpass specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5202,17 +5328,20 @@ options_init_from_torrc(int argc, char **argv) if (fd_line) { if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); - exit(1); + retval = -1; + goto err; } else if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); - exit(1); + retval = -1; + goto err; } else { const char *v = fd_line->value; int ok = 1; long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); if (fd < 0 || ok == 0) { log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); - exit(1); + retval = -1; + goto err; } get_options_mutable()->keygen_passphrase_fd = (int)fd; get_options_mutable()->use_keygen_passphrase_fd = 1; @@ -5227,7 +5356,8 @@ options_init_from_torrc(int argc, char **argv) if (key_line) { if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--master-key without --keygen!"); - exit(1); + retval = -1; + goto err; } else { get_options_mutable()->master_key_fname = tor_strdup(key_line->value); } @@ -5275,13 +5405,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command = command; newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + smartlist_t *opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5310,6 +5443,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, } newoptions->IncludeUsed = cf_has_include; + newoptions->FilesOpenedByIncludes = opened_files; /* If this is a testing network configuration, change defaults * for a list of dependent config options, re-initialize newoptions @@ -5349,13 +5483,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; /* Assign all options a second time. */ + opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5380,6 +5517,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->IncludeUsed = cf_has_include; in_option_validation = 1; + newoptions->FilesOpenedByIncludes = opened_files; /* Validate newoptions */ if (options_validate(oldoptions, newoptions, newdefaultoptions, @@ -5406,6 +5544,12 @@ options_init_from_string(const char *cf_defaults, const char *cf, err: in_option_validation = 0; + if (opened_files) { + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + } + // may have been set to opened_files, avoid double free + newoptions->FilesOpenedByIncludes = NULL; or_options_free(newoptions); or_options_free(newdefaultoptions); if (*msg) { @@ -5601,16 +5745,29 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, } goto cleanup; } - if (smartlist_len(elts) == 1 && - !strcasecmp(smartlist_get(elts,0), "syslog")) { + if (smartlist_len(elts) == 1) { + if (!strcasecmp(smartlist_get(elts,0), "syslog")) { #ifdef HAVE_SYSLOG_H - if (!validate_only) { - add_syslog_log(severity, options->SyslogIdentityTag); - } + if (!validate_only) { + add_syslog_log(severity, options->SyslogIdentityTag); + } #else - log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); + log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); #endif /* defined(HAVE_SYSLOG_H) */ - goto cleanup; + goto cleanup; + } + + if (!strcasecmp(smartlist_get(elts, 0), "android")) { +#ifdef HAVE_ANDROID_LOG_H + if (!validate_only) { + add_android_log(severity, options->AndroidIdentityTag); + } +#else + log_warn(LD_CONFIG, "Android logging is not supported" + " on this system. Sorry."); +#endif // HAVE_ANDROID_LOG_H. + goto cleanup; + } } if (smartlist_len(elts) == 2 && @@ -5696,7 +5853,7 @@ validate_transport_socks_arguments(const smartlist_t *args) /** Deallocate a bridge_line_t structure. */ /* private */ void -bridge_line_free(bridge_line_t *bridge_line) +bridge_line_free_(bridge_line_t *bridge_line) { if (!bridge_line) return; @@ -6462,7 +6619,7 @@ port_cfg_new(size_t namelen) /** Free all storage held in <b>port</b> */ STATIC void -port_cfg_free(port_cfg_t *port) +port_cfg_free_(port_cfg_t *port) { tor_free(port); } @@ -7200,7 +7357,7 @@ parse_ports(or_options_t *options, int validate_only, *n_ports_out = 0; - const unsigned gw_flag = options->SocksSocketsGroupWritable ? + const unsigned gw_flag = options->UnixSocksGroupWritable ? CL_PORT_DFLT_GROUP_WRITABLE : 0; if (parse_port_config(ports, options->SocksPort_lines, @@ -7640,60 +7797,81 @@ port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, check_wildcard); } -/** Adjust the value of options->DataDirectory, or fill it in if it's - * absent. Return 0 on success, -1 on failure. */ -static int -normalize_data_directory(or_options_t *options) +/** Allocate and return a good value for the DataDirectory based on + * <b>val</b>, which may be NULL. Return NULL on failure. */ +static char * +get_data_directory(const char *val) { #ifdef _WIN32 - char *p; - if (options->DataDirectory) - return 0; /* all set */ - p = tor_malloc(MAX_PATH); - strlcpy(p,get_windows_conf_root(),MAX_PATH); - options->DataDirectory = p; - return 0; + if (val) { + return tor_strdup(val); + } else { + return tor_strdup(get_windows_conf_root()); + } #else /* !(defined(_WIN32)) */ - const char *d = options->DataDirectory; + const char *d = val; if (!d) d = "~/.tor"; - if (strncmp(d,"~/",2) == 0) { - char *fn = expand_filename(d); - if (!fn) { - log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); - return -1; - } - if (!options->DataDirectory && !strcmp(fn,"/.tor")) { - /* If our homedir is /, we probably don't want to use it. */ - /* Default to LOCALSTATEDIR/tor which is probably closer to what we - * want. */ - log_warn(LD_CONFIG, - "Default DataDirectory is \"~/.tor\". This expands to " - "\"%s\", which is probably not what you want. Using " - "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); - tor_free(fn); - fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); - } - tor_free(options->DataDirectory); - options->DataDirectory = fn; - } - return 0; + if (!strcmpstart(d, "~/")) { + char *fn = expand_filename(d); + if (!fn) { + log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); + return NULL; + } + if (!val && !strcmp(fn,"/.tor")) { + /* If our homedir is /, we probably don't want to use it. */ + /* Default to LOCALSTATEDIR/tor which is probably closer to what we + * want. */ + log_warn(LD_CONFIG, + "Default DataDirectory is \"~/.tor\". This expands to " + "\"%s\", which is probably not what you want. Using " + "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); + tor_free(fn); + fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); + } + return fn; + } + return tor_strdup(d); #endif /* defined(_WIN32) */ } -/** Check and normalize the value of options->DataDirectory; return 0 if it - * is sane, -1 otherwise. */ +/** Check and normalize the values of options->{Key,Data,Cache}Directory; + * return 0 if it is sane, -1 otherwise. */ static int -validate_data_directory(or_options_t *options) +validate_data_directories(or_options_t *options) { - if (normalize_data_directory(options) < 0) + tor_free(options->DataDirectory); + options->DataDirectory = get_data_directory(options->DataDirectory_option); + if (!options->DataDirectory) return -1; - tor_assert(options->DataDirectory); if (strlen(options->DataDirectory) > (512-128)) { log_warn(LD_CONFIG, "DataDirectory is too long."); return -1; } + + tor_free(options->KeyDirectory); + if (options->KeyDirectory_option) { + options->KeyDirectory = get_data_directory(options->KeyDirectory_option); + if (!options->KeyDirectory) + return -1; + } else { + /* Default to the data directory's keys subdir */ + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + } + + tor_free(options->CacheDirectory); + if (options->CacheDirectory_option) { + options->CacheDirectory = get_data_directory( + options->CacheDirectory_option); + if (!options->CacheDirectory) + return -1; + } else { + /* Default to the data directory. */ + options->CacheDirectory = tor_strdup(options->DataDirectory); + } + return 0; } @@ -7834,53 +8012,56 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); } -/** Return a newly allocated string holding a filename relative to the data - * directory. If <b>sub1</b> is present, it is the first path component after +/** Return a newly allocated string holding a filename relative to the + * directory in <b>options</b> specified by <b>roottype</b>. + * If <b>sub1</b> is present, it is the first path component after * the data directory. If <b>sub2</b> is also present, it is the second path * component after the data directory. If <b>suffix</b> is present, it * is appended to the filename. * - * Examples: - * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a - * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp - * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp - * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b - * - * Note: Consider using the get_datadir_fname* macros in or.h. + * Note: Consider using macros in config.h that wrap this function; + * you should probably never need to call it as-is. */ MOCK_IMPL(char *, -options_get_datadir_fname2_suffix,(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix)) +options_get_dir_fname2_suffix,(const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)) { - char *fname = NULL; - size_t len; tor_assert(options); - tor_assert(options->DataDirectory); - tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */ - len = strlen(options->DataDirectory); - if (sub1) { - len += strlen(sub1)+1; - if (sub2) - len += strlen(sub2)+1; - } - if (suffix) - len += strlen(suffix); - len++; - fname = tor_malloc(len); - if (sub1) { - if (sub2) { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1, sub2); - } else { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1); - } + + const char *rootdir = NULL; + switch (roottype) { + case DIRROOT_DATADIR: + rootdir = options->DataDirectory; + break; + case DIRROOT_CACHEDIR: + rootdir = options->CacheDirectory; + break; + case DIRROOT_KEYDIR: + rootdir = options->KeyDirectory; + break; + default: + tor_assert_unreached(); + break; + } + tor_assert(rootdir); + + if (!suffix) + suffix = ""; + + char *fname = NULL; + + if (sub1 == NULL) { + tor_asprintf(&fname, "%s%s", rootdir, suffix); + tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */ + } else if (sub2 == NULL) { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix); } else { - strlcpy(fname, options->DataDirectory, len); + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s", + rootdir, sub1, sub2, suffix); } - if (suffix) - strlcat(fname, suffix, len); + return fname; } @@ -7921,28 +8102,6 @@ write_to_data_subdir(const char* subdir, const char* fname, return return_val; } -/** Given a file name check to see whether the file exists but has not been - * modified for a very long time. If so, remove it. */ -void -remove_file_if_very_old(const char *fname, time_t now) -{ -#define VERY_OLD_FILE_AGE (28*24*60*60) - struct stat st; - - log_debug(LD_FS, "stat()ing %s", fname); - if (stat(sandbox_intern_string(fname), &st)==0 && - st.st_mtime < now-VERY_OLD_FILE_AGE) { - char buf[ISO_TIME_LEN+1]; - format_local_iso_time(buf, st.st_mtime); - log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. " - "Removing it.", fname, buf); - if (unlink(fname) != 0) { - log_warn(LD_FS, "Failed to unlink %s: %s", - fname, strerror(errno)); - } - } -} - /** Return a smartlist of ports that must be forwarded by * tor-fw-helper. The smartlist contains the ports in a string format * that is understandable by tor-fw-helper. */ diff --git a/src/or/config.h b/src/or/config.h index efdd8c59b0..2f23809b2e 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -31,6 +31,7 @@ const char *safe_str_client(const char *address); const char *safe_str(const char *address); const char *escaped_safe_str_client(const char *address); const char *escaped_safe_str(const char *address); +void init_protocol_warning_severity_level(void); int get_protocol_warning_severity_level(void); const char *get_version(void); const char *get_short_version(void); @@ -58,31 +59,77 @@ config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); const char *get_torrc_fname(int defaults_fname); +typedef enum { + DIRROOT_DATADIR, + DIRROOT_CACHEDIR, + DIRROOT_KEYDIR +} directory_root_t; + MOCK_DECL(char *, - options_get_datadir_fname2_suffix, + options_get_dir_fname2_suffix, (const or_options_t *options, + directory_root_t roottype, const char *sub1, const char *sub2, const char *suffix)); + +/* These macros wrap options_get_dir_fname2_suffix to provide a more + * convenient API for finding filenames that Tor uses inside its storage + * They are named according to a pattern: + * (options_)?get_(cache|key|data)dir_fname(2)?(_suffix)? + * + * Macros that begin with options_ take an options argument; the others + * work with respect to the global options. + * + * Each macro works relative to the data directory, the key directory, + * or the cache directory, as determined by which one is mentioned. + * + * Macro variants with "2" in their name take two path components; others + * take one. + * + * Macro variants with "_suffix" at the end take an additional suffix + * that gets appended to the end of the file + */ +#define options_get_datadir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_DATADIR, \ + (sub1), (sub2), (suffix)) +#define options_get_cachedir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_CACHEDIR, \ + (sub1), (sub2), (suffix)) +#define options_get_keydir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_KEYDIR, \ + (sub1), (sub2), (suffix)) + +#define options_get_datadir_fname(opts,sub1) \ + options_get_datadir_fname2_suffix((opts),(sub1), NULL, NULL) +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) + #define get_datadir_fname2_suffix(sub1, sub2, suffix) \ options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) -/** Return a newly allocated string containing datadir/sub1. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL) -/** Return a newly allocated string containing datadir/sub1/sub2. See - * get_datadir_fname2_suffix. */ +#define get_datadir_fname(sub1) \ + get_datadir_fname2_suffix((sub1), NULL, NULL) #define get_datadir_fname2(sub1,sub2) \ get_datadir_fname2_suffix((sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1/sub2 relative to - * opts. See get_datadir_fname2_suffix. */ -#define options_get_datadir_fname2(opts,sub1,sub2) \ - options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1suffix. See - * get_datadir_fname2_suffix. */ #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +/** DOCDOC */ +#define options_get_keydir_fname(options, sub1) \ + options_get_keydir_fname2_suffix((options), (sub1), NULL, NULL) +#define get_keydir_fname_suffix(sub1, suffix) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, suffix) +#define get_keydir_fname(sub1) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, NULL) + +#define get_cachedir_fname(sub1) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, NULL) +#define get_cachedir_fname_suffix(sub1, suffix) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, (suffix)) + int using_default_dir_authorities(const or_options_t *options); +int create_keys_directory(const or_options_t *options); + int check_or_create_data_subdir(const char *subdir); int write_to_data_subdir(const char* subdir, const char* fname, const char* str, const char* descr); @@ -152,7 +199,9 @@ typedef struct bridge_line_t { transport proxy. */ } bridge_line_t; -void bridge_line_free(bridge_line_t *bridge_line); +void bridge_line_free_(bridge_line_t *bridge_line); +#define bridge_line_free(line) \ + FREE_AND_NULL(bridge_line_t, bridge_line_free_, (line)) bridge_line_t *parse_bridge_line(const char *line); smartlist_t *get_options_from_transport_options_line(const char *line, const char *transport); @@ -175,8 +224,12 @@ extern struct config_format_t options_format; #endif STATIC port_cfg_t *port_cfg_new(size_t namelen); -STATIC void port_cfg_free(port_cfg_t *port); -STATIC void or_options_free(or_options_t *options); +#define port_cfg_free(port) \ + FREE_AND_NULL(port_cfg_t, port_cfg_free_, (port)) +STATIC void port_cfg_free_(port_cfg_t *port); +#define or_options_free(opt) \ + FREE_AND_NULL(or_options_t, or_options_free_, (opt)) +STATIC void or_options_free_(or_options_t *options); STATIC int options_validate_single_onion(or_options_t *options, char **msg); STATIC int options_validate(or_options_t *old_options, diff --git a/src/or/confparse.c b/src/or/confparse.c index abae7e33dc..64ed9ee6bb 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -863,7 +863,7 @@ config_reset(const config_format_t *fmt, void *options, /** Release storage held by <b>options</b>. */ void -config_free(const config_format_t *fmt, void *options) +config_free_(const config_format_t *fmt, void *options) { int i; diff --git a/src/or/confparse.h b/src/or/confparse.h index 6f0b3b325c..f1f2030343 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -68,7 +68,7 @@ typedef union { config_line_t **LINELIST_V; routerset_t **ROUTERSET; } confparse_dummy_values_t; -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** An abbreviation for a configuration option allowed on the command line. */ typedef struct config_abbrev_t { @@ -132,13 +132,13 @@ typedef struct config_var_t { { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } } #define DUMMY_TYPECHECK_INSTANCE(tp) \ static tp tp ## _dummy -#else +#else /* !(defined(TOR_UNIT_TESTS)) */ #define CONF_TEST_MEMBERS(tp, conftype, member) #define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } /* Repeatedly declarable incomplete struct to absorb redundant semicolons */ #define DUMMY_TYPECHECK_INSTANCE(tp) \ struct tor_semicolon_eater -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** Type of a callback to validate whether a given configuration is * well-formed and consistent. See options_trial_assign() for documentation @@ -177,7 +177,12 @@ typedef struct config_format_t { #define CAL_WARN_DEPRECATIONS (1u<<2) void *config_new(const config_format_t *fmt); -void config_free(const config_format_t *fmt, void *options); +void config_free_(const config_format_t *fmt, void *options); +#define config_free(fmt, options) do { \ + config_free_((fmt), (options)); \ + (options) = NULL; \ + } while (0) + config_line_t *config_get_assigned_option(const config_format_t *fmt, const void *options, const char *key, int escape_val); @@ -203,5 +208,13 @@ const char *config_expand_abbrev(const config_format_t *fmt, int command_line, int warn_obsolete); void warn_deprecated_option(const char *what, const char *why); +/* Helper macros to compare an option across two configuration objects */ +#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt) +#define CFG_EQ_STRING(a,b,opt) (!strcmp_opt((a)->opt, (b)->opt)) +#define CFG_EQ_SMARTLIST(a,b,opt) smartlist_strings_eq((a)->opt, (b)->opt) +#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) + #endif /* !defined(TOR_CONFPARSE_H) */ diff --git a/src/or/connection.c b/src/or/connection.c index ed8de05d78..2a6b10763e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001 Matej Pfajfar. + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2017, The Tor Project, Inc. */ @@ -119,8 +119,6 @@ static connection_t *connection_listener_new( const port_cfg_t *portcfg); static void connection_init(time_t now, connection_t *conn, int type, int socket_family); -static int connection_init_accepted_conn(connection_t *conn, - const listener_connection_t *listener); static int connection_handle_listener_read(connection_t *conn, int new_type); static int connection_bucket_should_increase(int bucket, or_connection_t *conn); @@ -338,8 +336,6 @@ entry_connection_new(int type, int socket_family) entry_conn->entry_cfg.ipv4_traffic = 1; else if (socket_family == AF_INET6) entry_conn->entry_cfg.ipv6_traffic = 1; - else if (socket_family == AF_UNIX) - entry_conn->is_socks_socket = 1; return entry_conn; } @@ -502,7 +498,7 @@ conn_listener_type_supports_af_unix(int type) * if <b>conn</b> is an OR or OP connection. */ STATIC void -connection_free_(connection_t *conn) +connection_free_minimal(connection_t *conn) { void *mem; size_t memlen; @@ -678,7 +674,7 @@ connection_free_(connection_t *conn) /** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. */ MOCK_IMPL(void, -connection_free,(connection_t *conn)) +connection_free_,(connection_t *conn)) { if (!conn) return; @@ -714,7 +710,7 @@ connection_free,(connection_t *conn)) } connection_unregister_events(conn); - connection_free_(conn); + connection_free_minimal(conn); } /** @@ -1682,11 +1678,15 @@ connection_handle_listener_read(connection_t *conn, int new_type) } /** Initialize states for newly accepted connection <b>conn</b>. + * * If conn is an OR, start the TLS handshake. + * * If conn is a transparent AP, get its original destination * and place it in circuit_wait. + * + * The <b>listener</b> parameter is only used for AP connections. */ -static int +int connection_init_accepted_conn(connection_t *conn, const listener_connection_t *listener) { @@ -1766,7 +1766,11 @@ connection_connect_sockaddr,(connection_t *conn, if (get_options()->DisableNetwork) { /* We should never even try to connect anyplace if DisableNetwork is set. - * Warn if we do, and refuse to make the connection. */ + * Warn if we do, and refuse to make the connection. + * + * We only check DisableNetwork here, not we_are_hibernating(), since + * we'll still try to fulfill client requests sometimes in the latter case + * (if it is soft hibernation) */ static ratelim_t disablenet_violated = RATELIM_INIT(30*60); *socket_error = SOCK_ERRNO(ENETUNREACH); log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, @@ -3771,6 +3775,44 @@ connection_outbuf_too_full(connection_t *conn) return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); } +/** + * On Windows Vista and Windows 7, tune the send buffer size according to a + * hint from the OS. + * + * This should help fix slow upload rates. + */ +static void + +update_send_buffer_size(tor_socket_t sock) +{ +#ifdef _WIN32 + /* We only do this on Vista and 7, because earlier versions of Windows + * don't have the SIO_IDEAL_SEND_BACKLOG_QUERY functionality, and on + * later versions it isn't necessary. */ + static int isVistaOr7 = -1; + if (isVistaOr7 == -1) { + isVistaOr7 = 0; + OSVERSIONINFO osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion < 2) + isVistaOr7 = 1; + } + if (!isVistaOr7) + return; + if (get_options()->ConstrainedSockets) + return; + ULONG isb = 0; + DWORD bytesReturned = 0; + if (!WSAIoctl(sock, SIO_IDEAL_SEND_BACKLOG_QUERY, NULL, 0, + &isb, sizeof(isb), &bytesReturned, NULL, NULL)) { + setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb)); + } +#else + (void) sock; +#endif +} + /** Try to flush more bytes onto <b>conn</b>-\>s. * * This function gets called either from conn_write_callback() in main.c @@ -3889,6 +3931,9 @@ connection_handle_write_impl(connection_t *conn, int force) result = buf_flush_to_tls(conn->outbuf, or_conn->tls, max_to_write, &conn->outbuf_flushlen); + if (result >= 0) + update_send_buffer_size(conn->s); + /* If we just flushed the last bytes, tell the channel on the * or_conn to check if it needs to geoip_change_dirreq_state() */ /* XXXX move this to flushed_some or finished_flushing -NM */ @@ -3963,6 +4008,7 @@ connection_handle_write_impl(connection_t *conn, int force) connection_mark_for_close(conn); return -1; } + update_send_buffer_size(conn->s); n_written = (size_t) result; } @@ -4065,6 +4111,68 @@ connection_flush(connection_t *conn) return connection_handle_write(conn, 1); } +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for + * writing. + */ +static int +connection_may_write_to_buf(connection_t *conn) +{ + /* if it's marked for close, only allow write if we mean to flush it */ + if (conn->marked_for_close && !conn->hold_open_until_flushed) + return 0; + + return 1; +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed; + * mark the connection and warn as appropriate. + */ +static void +connection_write_to_buf_failed(connection_t *conn) +{ + if (CONN_IS_EDGE(conn)) { + /* if it failed, it means we have our package/delivery windows set + wrong compared to our max outbuf size. close the whole circuit. */ + log_warn(LD_NET, + "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); + circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), + END_CIRC_REASON_INTERNAL); + } else if (conn->type == CONN_TYPE_OR) { + or_connection_t *orconn = TO_OR_CONN(conn); + log_warn(LD_NET, + "write_to_buf failed on an orconn; notifying of error " + "(fd %d)", (int)(conn->s)); + connection_or_close_for_error(orconn, 0); + } else { + log_warn(LD_NET, + "write_to_buf failed. Closing connection (fd %d).", + (int)conn->s); + connection_mark_for_close(conn); + } +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded: + * record the number of bytes added. + */ +static void +connection_write_to_buf_commit(connection_t *conn, size_t len) +{ + /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING + * state, we don't want to try to write it right away, since + * conn->write_event won't be set yet. Otherwise, write data from + * this conn as the socket is available. */ + if (conn->write_event) { + connection_start_writing(conn); + } + conn->outbuf_flushlen += len; +} + /** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s * outbuf, and ask it to start writing. * @@ -4079,58 +4187,52 @@ connection_write_to_buf_impl_,(const char *string, size_t len, { /* XXXX This function really needs to return -1 on failure. */ int r; - size_t old_datalen; if (!len && !(zlib<0)) return; - /* if it's marked for close, only allow write if we mean to flush it */ - if (conn->marked_for_close && !conn->hold_open_until_flushed) + + if (!connection_may_write_to_buf(conn)) return; - old_datalen = buf_datalen(conn->outbuf); + size_t written; + if (zlib) { + size_t old_datalen = buf_datalen(conn->outbuf); dir_connection_t *dir_conn = TO_DIR_CONN(conn); int done = zlib < 0; CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf, - dir_conn->compress_state, - string, len, done)); + dir_conn->compress_state, + string, len, done)); + written = buf_datalen(conn->outbuf) - old_datalen; } else { CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len)); + written = len; } if (r < 0) { - if (CONN_IS_EDGE(conn)) { - /* if it failed, it means we have our package/delivery windows set - wrong compared to our max outbuf size. close the whole circuit. */ - log_warn(LD_NET, - "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); - circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), - END_CIRC_REASON_INTERNAL); - } else if (conn->type == CONN_TYPE_OR) { - or_connection_t *orconn = TO_OR_CONN(conn); - log_warn(LD_NET, - "write_to_buf failed on an orconn; notifying of error " - "(fd %d)", (int)(conn->s)); - connection_or_close_for_error(orconn, 0); - } else { - log_warn(LD_NET, - "write_to_buf failed. Closing connection (fd %d).", - (int)conn->s); - connection_mark_for_close(conn); - } + connection_write_to_buf_failed(conn); return; } + connection_write_to_buf_commit(conn, written); +} - /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING - * state, we don't want to try to write it right away, since - * conn->write_event won't be set yet. Otherwise, write data from - * this conn as the socket is available. */ - if (conn->write_event) { - connection_start_writing(conn); - } - if (zlib) { - conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; - } else { - conn->outbuf_flushlen += len; - } +/** + * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them + * from <b>buf</b>. (If the connection is marked and will soon be closed, + * nothing is drained.) + */ +void +connection_buf_add_buf(connection_t *conn, buf_t *buf) +{ + tor_assert(conn); + tor_assert(buf); + size_t len = buf_datalen(buf); + if (len == 0) + return; + + if (!connection_may_write_to_buf(conn)) + return; + + buf_move_all(conn->outbuf, buf); + connection_write_to_buf_commit(conn, len); } #define CONN_GET_ALL_TEMPLATE(var, test) \ @@ -4146,7 +4248,7 @@ connection_write_to_buf_impl_,(const char *string, size_t len, /* Return a list of connections that aren't close and matches the given type * and state. The returned list can be empty and must be freed using - * smartlist_free(). The caller does NOT have owernship of the objects in the + * smartlist_free(). The caller does NOT have ownership of the objects in the * list so it must not free them nor reference them as they can disappear. */ smartlist_t * connection_list_by_type_state(int type, int state) @@ -4156,7 +4258,7 @@ connection_list_by_type_state(int type, int state) /* Return a list of connections that aren't close and matches the given type * and purpose. The returned list can be empty and must be freed using - * smartlist_free(). The caller does NOT have owernship of the objects in the + * smartlist_free(). The caller does NOT have ownership of the objects in the * list so it must not free them nor reference them as they can disappear. */ smartlist_t * connection_list_by_type_purpose(int type, int purpose) @@ -5193,8 +5295,8 @@ proxy_type_to_string(int proxy_type) return NULL; /*Unreached*/ } -/** Call connection_free_() on every connection in our array, and release all - * storage held by connection.c. +/** Call connection_free_minimal() on every connection in our array, and + * release all storage held by connection.c. * * Don't do the checks in connection_free(), because they will * fail. @@ -5218,7 +5320,8 @@ connection_free_all(void) /* Clear out our list of broken connections */ clear_broken_connection_map(0); - SMARTLIST_FOREACH(conns, connection_t *, conn, connection_free_(conn)); + SMARTLIST_FOREACH(conns, connection_t *, conn, + connection_free_minimal(conn)); if (outgoing_addrs) { SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr)); diff --git a/src/or/connection.h b/src/or/connection.h index 4a5bd6971b..6bc5a7cfd0 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -26,9 +26,12 @@ entry_connection_t *entry_connection_new(int type, int socket_family); control_connection_t *control_connection_new(int socket_family); listener_connection_t *listener_connection_new(int type, int socket_family); connection_t *connection_new(int type, int socket_family); - +int connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener); void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -MOCK_DECL(void,connection_free,(connection_t *conn)); +MOCK_DECL(void,connection_free_,(connection_t *conn)); +#define connection_free(conn) \ + FREE_AND_NULL(connection_t, connection_free_, (conn)) void connection_free_all(void); void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); @@ -155,6 +158,7 @@ connection_buf_add_compress(const char *string, size_t len, { connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1); } +void connection_buf_add_buf(connection_t *conn, buf_t *buf); /* DOCDOC connection_get_inbuf_len */ static size_t connection_get_inbuf_len(connection_t *conn); @@ -243,7 +247,6 @@ char *alloc_http_authenticator(const char *authenticator); void assert_connection_ok(connection_t *conn, time_t now); int connection_or_nonopen_was_started_here(or_connection_t *conn); void connection_dump_buffer_mem_stats(int severity); -void remove_file_if_very_old(const char *fname, time_t now); void clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, log_domain_mask_t domain, @@ -266,7 +269,7 @@ connection_is_moribund(connection_t *conn) void connection_check_oos(int n_socks, int failed); #ifdef CONNECTION_PRIVATE -STATIC void connection_free_(connection_t *conn); +STATIC void connection_free_minimal(connection_t *conn); /* Used only by connection.c and test*.c */ uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index f178917f0b..a47f044e08 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -339,6 +339,10 @@ relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ, payload[0] = (char) reason; + /* Note: we have to use relay_send_command_from_edge here, not + * connection_edge_end or connection_edge_send_command, since those require + * that we have a stream connected to a circuit, and we don't connect to a + * circuit until we have a pending/successful resolve. */ return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END, payload, 1, cpath_layer); } @@ -788,7 +792,10 @@ connection_ap_expire_beginning(void) } continue; } + if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL && + circ->purpose != CIRCUIT_PURPOSE_C_HSDIR_GET && + circ->purpose != CIRCUIT_PURPOSE_S_HSDIR_POST && circ->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT && circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) { log_warn(LD_BUG, "circuit->purpose == CIRCUIT_PURPOSE_C_GENERAL failed. " @@ -999,7 +1006,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, * So the fix is to tell it right now that it ought to finish its loop at * its next available opportunity. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } /** Mark <b>entry_conn</b> as no longer waiting for a circuit. */ @@ -2588,6 +2595,8 @@ connection_ap_supports_optimistic_data(const entry_connection_t *conn) if (edge_conn->on_circuit == NULL || edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN || (edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL && + edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_HSDIR_GET && + edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_S_HSDIR_POST && edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) return 0; @@ -3327,7 +3336,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) relay_send_end_cell_from_edge(conn->stream_id, circ, END_STREAM_REASON_DONE, origin_circ->cpath->prev); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); /* Drop the circuit here since it might be someone deliberately * scanning the hidden service ports. Note that this mitigates port @@ -3405,11 +3414,6 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (rh.length > RELAY_PAYLOAD_SIZE) return -END_CIRC_REASON_TORPROTOCOL; - /* Note: we have to use relay_send_command_from_edge here, not - * connection_edge_end or connection_edge_send_command, since those require - * that we have a stream connected to a circuit, and we don't connect to a - * circuit until we have a pending/successful resolve. */ - if (!server_mode(options) && circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -3524,7 +3528,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (we_are_hibernating()) { relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_HIBERNATING, NULL); - connection_free(TO_CONN(n_stream)); + connection_free_(TO_CONN(n_stream)); return 0; } @@ -3602,7 +3606,7 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) return 0; case 1: /* The result was cached; a resolved cell was sent. */ if (!dummy_conn->base_.marked_for_close) - connection_free(TO_CONN(dummy_conn)); + connection_free_(TO_CONN(dummy_conn)); return 0; case 0: /* resolve added to pending list */ assert_circuit_ok(TO_CIRCUIT(circ)); @@ -3775,8 +3779,8 @@ connection_exit_connect_dir(edge_connection_t *exitconn) if (connection_add(TO_CONN(exitconn))<0) { connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); - connection_free(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(exitconn)); + connection_free_(TO_CONN(dirconn)); return 0; } @@ -3788,7 +3792,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); connection_close_immediate(TO_CONN(exitconn)); connection_mark_for_close(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(dirconn)); return 0; } diff --git a/src/or/connection_or.c b/src/or/connection_or.c index fd8c5fc7f2..272a086a32 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -505,7 +505,7 @@ var_cell_copy(const var_cell_t *src) /** Release all space held by <b>cell</b>. */ void -var_cell_free(var_cell_t *cell) +var_cell_free_(var_cell_t *cell) { tor_free(cell); } @@ -592,8 +592,9 @@ connection_or_flushed_some(or_connection_t *conn) { size_t datalen; - /* The channel will want to update its estimated queue size */ - channel_update_xmit_queue_size(TLS_CHAN_TO_BASE(conn->chan)); + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); /* If we're under the low water mark, add cells until we're just over the * high water mark. */ @@ -655,6 +656,11 @@ connection_or_finished_flushing(or_connection_t *conn) tor_fragile_assert(); return -1; } + + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + return 0; } @@ -699,7 +705,6 @@ connection_or_finished_connecting(or_connection_t *or_conn) void connection_or_about_to_close(or_connection_t *or_conn) { - time_t now = time(NULL); connection_t *conn = TO_CONN(or_conn); /* Tell the controlling channel we're closed */ @@ -719,7 +724,6 @@ connection_or_about_to_close(or_connection_t *or_conn) if (connection_or_nonopen_was_started_here(or_conn)) { const or_options_t *options = get_options(); connection_or_note_state_when_broken(or_conn); - rep_hist_note_connect_failed(or_conn->identity_digest, now); /* Tell the new guard API about the channel failure */ entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan)); if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) { @@ -735,11 +739,9 @@ connection_or_about_to_close(or_connection_t *or_conn) } else if (conn->hold_open_until_flushed) { /* We only set hold_open_until_flushed when we're intentionally * closing a connection. */ - rep_hist_note_disconnect(or_conn->identity_digest, now); control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, tls_error_to_orconn_end_reason(or_conn->tls_error)); } else if (!tor_digest_is_zero(or_conn->identity_digest)) { - rep_hist_note_connection_died(or_conn->identity_digest, now); control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, tls_error_to_orconn_end_reason(or_conn->tls_error)); } @@ -886,7 +888,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here) const node_t *r = node_get_by_id(id_digest); if (r && - node_supports_ed25519_link_authentication(r) && + node_supports_ed25519_link_authentication(r, 1) && ! node_ed25519_id_matches(r, ed_id)) { /* If this node is capable of proving an ed25519 ID, * we can't call this a canonical connection unless both IDs match. */ @@ -965,6 +967,36 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) * too old for new circuits? */ #define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) +/** Expire an or_connection if it is too old. Helper for + * connection_or_group_set_badness_ and fast path for + * channel_rsa_id_group_set_badness. + * + * Returns 1 if the connection was already expired, else 0. + */ +int +connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force) +{ + /* XXXX this function should also be about channels? */ + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + return 1; + + if (force || + or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + + return 0; +} + /** Given a list of all the or_connections with a given * identity, set elements of that list as is_bad_for_new_circs as * appropriate. Helper for connection_or_set_bad_connections(). @@ -995,19 +1027,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force) /* Pass 1: expire everything that's old, and see what the status of * everything else is. */ SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) + if (connection_or_single_set_badness_(now, or_conn, force)) continue; - if (force || - or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD - < now) { - log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } if (connection_or_is_bad_for_new_circs(or_conn)) { ++n_old; @@ -1247,7 +1268,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); } - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; } @@ -1260,7 +1281,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, connection_or_connect_failed(conn, errno_to_orconn_end_reason(socket_error), tor_socket_strerror(socket_error)); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; case 0: connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); @@ -1854,7 +1875,7 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) /** Free all storage held by <b>state</b>. */ void -or_handshake_state_free(or_handshake_state_t *state) +or_handshake_state_free_(or_handshake_state_t *state) { if (!state) return; diff --git a/src/or/connection_or.h b/src/or/connection_or.h index ee66b7c528..7c1dced631 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -68,7 +68,9 @@ int connection_or_client_learned_peer_id(or_connection_t *conn, const ed25519_public_key_t *ed_peer_id); time_t connection_or_client_used(or_connection_t *conn); MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); -void or_handshake_state_free(or_handshake_state_t *state); +void or_handshake_state_free_(or_handshake_state_t *state); +#define or_handshake_state_free(state) \ + FREE_AND_NULL(or_handshake_state_t, or_handshake_state_free_, (state)) void or_handshake_state_record_cell(or_connection_t *conn, or_handshake_state_t *state, const cell_t *cell, @@ -105,13 +107,17 @@ int var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids); var_cell_t *var_cell_new(uint16_t payload_len); var_cell_t *var_cell_copy(const var_cell_t *src); -void var_cell_free(var_cell_t *cell); +void var_cell_free_(var_cell_t *cell); +#define var_cell_free(cell) FREE_AND_NULL(var_cell_t, var_cell_free_, (cell)) /* DOCDOC */ #define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 #define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5 #define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING +int connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force); void connection_or_group_set_badness_(smartlist_t *group, int force); #ifdef TOR_UNIT_TESTS diff --git a/src/or/conscache.c b/src/or/conscache.c index 1a15126f97..e25ac5f40b 100644 --- a/src/or/conscache.c +++ b/src/or/conscache.c @@ -79,7 +79,7 @@ consensus_cache_open(const char *subdir, int max_entries) { int storagedir_max_entries; consensus_cache_t *cache = tor_malloc_zero(sizeof(consensus_cache_t)); - char *directory = get_datadir_fname(subdir); + char *directory = get_cachedir_fname(subdir); cache->max_entries = max_entries; #ifdef MUST_UNMAP_TO_UNLINK @@ -170,7 +170,7 @@ consensus_cache_clear(consensus_cache_t *cache) * Drop all storage held by <b>cache</b>. */ void -consensus_cache_free(consensus_cache_t *cache) +consensus_cache_free_(consensus_cache_t *cache) { if (! cache) return; diff --git a/src/or/conscache.h b/src/or/conscache.h index 3c89dedf43..08a5c5a37b 100644 --- a/src/or/conscache.h +++ b/src/or/conscache.h @@ -10,9 +10,14 @@ typedef struct consensus_cache_entry_t consensus_cache_entry_t; typedef struct consensus_cache_t consensus_cache_t; HANDLE_DECL(consensus_cache_entry, consensus_cache_entry_t, ) +#define consensus_cache_entry_handle_free(h) \ + FREE_AND_NULL(consensus_cache_entry_handle_t, \ + consensus_cache_entry_handle_free_, (h)) consensus_cache_t *consensus_cache_open(const char *subdir, int max_entries); -void consensus_cache_free(consensus_cache_t *cache); +void consensus_cache_free_(consensus_cache_t *cache); +#define consensus_cache_free(cache) \ + FREE_AND_NULL(consensus_cache_t, consensus_cache_free_, (cache)) struct sandbox_cfg_elem; int consensus_cache_may_overallocate(consensus_cache_t *cache); int consensus_cache_register_with_sandbox(consensus_cache_t *cache, diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c index 8349f257de..02b905a520 100644 --- a/src/or/consdiffmgr.c +++ b/src/or/consdiffmgr.c @@ -207,9 +207,12 @@ HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq) HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq, 0.6, tor_reallocarray, tor_free_) +#define cdm_diff_free(diff) \ + FREE_AND_NULL(cdm_diff_t, cdm_diff_free_, (diff)) + /** Release all storage held in <b>diff</b>. */ static void -cdm_diff_free(cdm_diff_t *diff) +cdm_diff_free_(cdm_diff_t *diff) { if (!diff) return; @@ -1511,11 +1514,15 @@ consensus_diff_worker_threadfn(void *state_, void *work_) return WQ_RPL_REPLY; } +#define consensus_diff_worker_job_free(job) \ + FREE_AND_NULL(consensus_diff_worker_job_t, \ + consensus_diff_worker_job_free_, (job)) + /** * Helper: release all storage held in <b>job</b>. */ static void -consensus_diff_worker_job_free(consensus_diff_worker_job_t *job) +consensus_diff_worker_job_free_(consensus_diff_worker_job_t *job) { if (!job) return; @@ -1663,11 +1670,15 @@ typedef struct consensus_compress_worker_job_t { compressed_result_t out[ARRAY_LENGTH(compress_consensus_with)]; } consensus_compress_worker_job_t; +#define consensus_compress_worker_job_free(job) \ + FREE_AND_NULL(consensus_compress_worker_job_t, \ + consensus_compress_worker_job_free_, (job)) + /** * Free all resources held in <b>job</b> */ static void -consensus_compress_worker_job_free(consensus_compress_worker_job_t *job) +consensus_compress_worker_job_free_(consensus_compress_worker_job_t *job) { if (!job) return; diff --git a/src/or/control.c b/src/or/control.c index 202366aaec..948ac92bb1 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -58,7 +58,9 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -549,6 +551,49 @@ decode_escaped_string(const char *start, size_t in_len_max, return end+1; } +/** Create and add a new controller connection on <b>sock</b>. If + * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should + * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b> + * is set, then the connection does not need to authenticate. + */ +int +control_connection_add_local_fd(tor_socket_t sock, unsigned flags) +{ + if (BUG(! SOCKET_OK(sock))) + return -1; + const int is_owner = !!(flags & CC_LOCAL_FD_IS_OWNER); + const int is_authenticated = !!(flags & CC_LOCAL_FD_IS_AUTHENTICATED); + control_connection_t *control_conn = control_connection_new(AF_UNSPEC); + connection_t *conn = TO_CONN(control_conn); + conn->s = sock; + tor_addr_make_unspec(&conn->addr); + conn->port = 1; + conn->address = tor_strdup("<local socket>"); + + /* We take ownership of this socket so that later, when we close it, + * we don't freak out. */ + tor_take_socket_ownership(sock); + + if (set_socket_nonblocking(sock) < 0 || + connection_add(conn) < 0) { + connection_free(conn); + return -1; + } + + control_conn->is_owning_control_connection = is_owner; + + if (connection_init_accepted_conn(conn, NULL) < 0) { + connection_mark_for_close(conn); + return -1; + } + + if (is_authenticated) { + conn->state = CONTROL_CONN_STATE_OPEN; + } + + return 0; +} + /** Acts like sprintf, but writes its formatted string to the end of * <b>conn</b>-\>outbuf. */ static void @@ -740,9 +785,12 @@ queue_control_event_string,(uint16_t event, char *msg)) } } +#define queued_event_free(ev) \ + FREE_AND_NULL(queued_event_t, queued_event_free_, (ev)) + /** Release all storage held by <b>ev</b>. */ static void -queued_event_free(queued_event_t *ev) +queued_event_free_(queued_event_t *ev) { if (ev == NULL) return; @@ -1971,36 +2019,89 @@ getinfo_helper_dir(control_connection_t *control_conn, SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); } else if (!strcmpstart(question, "hs/client/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/client/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } - if (!rend_cache_lookup_entry(question, -1, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + rend_cache_entry_t *e = NULL; + if (!rend_cache_lookup_entry(question, -1, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + const char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_cache_lookup_encoded_as_client(&service_pk); + if (desc) { + *answer = tor_strdup(desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "hs/service/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/service/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } + rend_cache_entry_t *e = NULL; - if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_service_lookup_current_desc(&service_pk); + if (desc) { + /* Newly allocated string, we have ownership. */ + *answer = desc; + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "md/id/")) { const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0); @@ -2072,7 +2173,7 @@ getinfo_helper_dir(control_connection_t *control_conn, *answer = tor_strdup(consensus->dir); } if (!*answer) { /* try loading it from disk */ - char *filename = get_datadir_fname("cached-consensus"); + char *filename = get_cachedir_fname("cached-consensus"); *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!*answer) { /* generate an error */ @@ -2581,9 +2682,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ) } } - if (circ->rend_data != NULL) { - smartlist_add_asprintf(descparts, "REND_QUERY=%s", - rend_data_get_address(circ->rend_data)); + if (circ->rend_data != NULL || circ->hs_ident != NULL) { + char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const char *onion_address; + if (circ->rend_data) { + onion_address = rend_data_get_address(circ->rend_data); + } else { + hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr); + onion_address = addr; + } + smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address); } { @@ -4234,9 +4342,11 @@ handle_control_hspost(control_connection_t *conn, const char *body) { static const char *opt_server = "SERVER="; + static const char *opt_hsaddress = "HSADDRESS="; smartlist_t *hs_dirs = NULL; const char *encoded_desc = body; size_t encoded_desc_len = len; + const char *onion_address = NULL; char *cp = memchr(body, '\n', len); if (cp == NULL) { @@ -4266,15 +4376,16 @@ handle_control_hspost(control_connection_t *conn, server); goto done; } - if (!node->rs->is_hs_dir) { - connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir" - "\r\n", server); - goto done; - } /* Valid server, add it to our local list. */ if (!hs_dirs) hs_dirs = smartlist_new(); smartlist_add(hs_dirs, node->rs); + } else if (!strcasecmpstart(arg, opt_hsaddress)) { + if (!hs_address_is_valid(arg)) { + connection_printf_to_buf(conn, "512 Malformed onion address\r\n"); + goto done; + } + onion_address = arg; } else { connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n", arg); @@ -4283,6 +4394,19 @@ handle_control_hspost(control_connection_t *conn, } SMARTLIST_FOREACH_END(arg); } + /* Handle the v3 case. */ + if (onion_address) { + char *desc_str = NULL; + read_escaped_data(encoded_desc, encoded_desc_len, &desc_str); + if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) { + connection_printf_to_buf(conn, "554 Invalid descriptor\r\n"); + } + tor_free(desc_str); + goto done; + } + + /* From this point on, it is only v2. */ + /* Read the dot encoded descriptor, and parse it. */ rend_encoded_v2_service_descriptor_t *desc = tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t)); @@ -4327,6 +4451,52 @@ handle_control_hspost(control_connection_t *conn, return 0; } +/* Helper function for ADD_ONION that adds an ephemeral service depending on + * the given hs_version. + * + * The secret key in pk depends on the hs_version. The ownership of the key + * used in pk is given to the HS subsystem so the caller must stop accessing + * it after. + * + * The port_cfgs is a list of service port. Ownership transferred to service. + * The max_streams refers to the MaxStreams= key. + * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key. + * The auth_type is the authentication type of the clients in auth_clients. + * The ownership of that list is transferred to the service. + * + * On success (RSAE_OKAY), the address_out points to a newly allocated string + * containing the onion address without the .onion part. On error, address_out + * is untouched. */ +static hs_service_add_ephemeral_status_t +add_onion_helper_add_service(int hs_version, + add_onion_secret_key_t *pk, + smartlist_t *port_cfgs, int max_streams, + int max_streams_close_circuit, int auth_type, + smartlist_t *auth_clients, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + + tor_assert(pk); + tor_assert(port_cfgs); + tor_assert(address_out); + + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams, + max_streams_close_circuit, auth_type, + auth_clients, address_out); + break; + case HS_VERSION_THREE: + ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams, + max_streams_close_circuit, address_out); + break; + default: + tor_assert_unreached(); + } + + return ret; +} + /** Called when we get a ADD_ONION command; parse the body, and set up * the new ephemeral Onion Service. */ static int @@ -4508,15 +4678,15 @@ handle_control_add_onion(control_connection_t *conn, } /* Parse the "keytype:keyblob" argument. */ - crypto_pk_t *pk = NULL; + int hs_version = 0; + add_onion_secret_key_t pk = { NULL }; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, - &key_new_alg, &key_new_blob, - &err_msg); - if (!pk) { + if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, &pk, &hs_version, + &err_msg) < 0) { if (err_msg) { connection_write_str_to_buf(err_msg, conn); tor_free(err_msg); @@ -4525,16 +4695,23 @@ handle_control_add_onion(control_connection_t *conn, } tor_assert(!err_msg); + /* Hidden service version 3 don't have client authentication support so if + * ClientAuth was given, send back an error. */ + if (hs_version == HS_VERSION_THREE && auth_clients) { + connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n"); + goto out; + } + /* Create the HS, using private key pk, client authentication auth_type, * the list of auth_clients, and port config port_cfg. * rend_service_add_ephemeral() will take ownership of pk and port_cfg, * regardless of success/failure. */ char *service_id = NULL; - int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, - max_streams_close_circuit, - auth_type, auth_clients, - &service_id); + int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs, + max_streams, + max_streams_close_circuit, auth_type, + auth_clients, &service_id); port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ auth_clients = NULL; /* so is auth_clients */ switch (ret) { @@ -4627,9 +4804,10 @@ handle_control_add_onion(control_connection_t *conn, * Note: The error messages returned are deliberately vague to avoid echoing * key material. */ -STATIC crypto_pk_t * +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4637,7 +4815,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - int ok = 0; + int ret = -1; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { @@ -4649,6 +4827,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, static const char *key_type_new = "NEW"; static const char *key_type_best = "BEST"; static const char *key_type_rsa1024 = "RSA1024"; + static const char *key_type_ed25519_v3 = "ED25519-V3"; const char *key_type = smartlist_get(key_args, 0); const char *key_blob = smartlist_get(key_args, 1); @@ -4661,9 +4840,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + crypto_pk_free(pk); err_msg = tor_strdup("512 Invalid RSA key size\r\n"); goto err; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, + strlen(key_blob)) != sizeof(sk->seckey)) { + tor_free(sk); + err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); + goto err; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ if (!strcasecmp(key_type_rsa1024, key_blob) || @@ -4677,12 +4870,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } if (!discard_pk) { if (crypto_pk_base64_encode(pk, &key_new_blob)) { + crypto_pk_free(pk); tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) { + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (ed25519_secret_key_generate(sk, 1) < 0) { + tor_free(sk); + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_ed25519_v3); + goto err; + } + if (!discard_pk) { + ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1; + key_new_blob = tor_malloc_zero(len); + if (base64_encode(key_new_blob, len, (const char *) sk->seckey, + sizeof(sk->seckey), 0) != (len - 1)) { + tor_free(sk); + tor_free(key_new_blob); + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_ed25519_v3); + goto err; + } + key_new_alg = key_type_ed25519_v3; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); goto err; @@ -4692,9 +4911,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } - /* Succeded in loading or generating a private key. */ - tor_assert(pk); - ok = 1; + /* Succeeded in loading or generating a private key. */ + ret = 0; err: SMARTLIST_FOREACH(key_args, char *, cp, { @@ -4703,10 +4921,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args); - if (!ok) { - crypto_pk_free(pk); - pk = NULL; - } if (err_msg_out) { *err_msg_out = err_msg; } else { @@ -4715,7 +4929,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob; - return pk; + return ret; } /** Helper function to handle parsing a ClientAuth argument to the @@ -4784,6 +4998,7 @@ handle_control_del_onion(control_connection_t *conn, uint32_t len, const char *body) { + int hs_version = 0; smartlist_t *args; (void) len; /* body is nul-terminated; it's safe to ignore the length */ args = getargs_helper("DEL_ONION", conn, body, 1, 1); @@ -4791,7 +5006,11 @@ handle_control_del_onion(control_connection_t *conn, return 0; const char *service_id = smartlist_get(args, 0); - if (!rend_valid_v2_service_id(service_id)) { + if (rend_valid_v2_service_id(service_id)) { + hs_version = HS_VERSION_TWO; + } else if (hs_address_is_valid(service_id)) { + hs_version = HS_VERSION_THREE; + } else { connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); goto out; } @@ -4818,8 +5037,20 @@ handle_control_del_onion(control_connection_t *conn, if (onion_services == NULL) { connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); } else { - int ret = rend_service_del_ephemeral(service_id); - if (ret) { + int ret = -1; + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_del_ephemeral(service_id); + break; + case HS_VERSION_THREE: + ret = hs_service_del_ephemeral(service_id); + break; + default: + /* The ret value will be -1 thus hitting the warning below. This should + * never happen because of the check at the start of the function. */ + break; + } + if (ret < 0) { /* This should *NEVER* fail, since the service is on either the * per-control connection list, or the global one. */ @@ -4889,9 +5120,16 @@ connection_control_closed(control_connection_t *conn) * The list and it's contents are scrubbed/freed in connection_free_. */ if (conn->ephemeral_onion_services) { - SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { - rend_service_del_ephemeral(cp); - }); + SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) { + if (rend_valid_v2_service_id(cp)) { + rend_service_del_ephemeral(cp); + } else if (hs_address_is_valid(cp)) { + hs_service_del_ephemeral(cp); + } else { + /* An invalid .onion in our list should NEVER happen */ + tor_fragile_assert(); + } + } SMARTLIST_FOREACH_END(cp); } if (conn->is_owning_control_connection) { @@ -6571,8 +6809,7 @@ monitor_owning_controller_process(const char *process_spec) "owning controller: %s. Exiting.", msg); owning_controller_process_spec = NULL; - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } @@ -6970,27 +7207,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address) * <b>rend_query</b> is used to fetch requested onion address and auth type. * <b>hs_dir</b> is the description of contacting hs directory. * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string. */ void -control_event_hs_descriptor_requested(const rend_data_t *rend_query, +control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!id_digest || !rend_query || !desc_id_base32) { - log_warn(LD_BUG, "Called with rend_query==%p, " - "id_digest==%p, desc_id_base32==%p", - rend_query, id_digest, desc_id_base32); + char *hsdir_index_field = NULL; + + if (BUG(!id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_hsaddress_str_or_unknown( - rend_data_get_address(rend_query)), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_query)->auth_type), + "650 HS_DESC REQUESTED %s %s %s %s%s\r\n", + rend_hsaddress_str_or_unknown(onion_address), + rend_auth_type_to_string(auth_type), node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** For an HS descriptor query <b>rend_data</b>, using the @@ -7039,89 +7282,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) /** send HS_DESC CREATED event when a local service generates a descriptor. * - * <b>service_id</b> is the descriptor onion address. - * <b>desc_id_base32</b> is the descriptor ID. - * <b>replica</b> is the the descriptor replica number. + * <b>onion_address</b> is service address. + * <b>desc_id</b> is the descriptor ID. + * <b>replica</b> is the the descriptor replica number. If it is negative, it + * is ignored. */ void -control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica) { - if (!service_id || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p", service_id, desc_id_base32); + char *replica_field = NULL; + + if (BUG(!onion_address || !desc_id)) { return; } + if (replica >= 0) { + tor_asprintf(&replica_field, " REPLICA=%d", replica); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s " - "REPLICA=%d\r\n", - service_id, - desc_id_base32, - replica); + "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n", + onion_address, desc_id, + replica_field ? replica_field : ""); + tor_free(replica_field); } /** send HS_DESC upload event. * - * <b>service_id</b> is the descriptor onion address. + * <b>onion_address</b> is service address. * <b>hs_dir</b> is the description of contacting hs directory. - * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>desc_id</b> is the ID of requested hs descriptor. */ void -control_event_hs_descriptor_upload(const char *service_id, +control_event_hs_descriptor_upload(const char *onion_address, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!service_id || !id_digest || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p, id_digest==%p", service_id, - desc_id_base32, id_digest); + char *hsdir_index_field = NULL; + + if (BUG(!onion_address || !id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", - service_id, + "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n", + onion_address, node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** send HS_DESC event after got response from hs directory. * * NOTE: this is an internal function used by following functions: - * control_event_hs_descriptor_received - * control_event_hs_descriptor_failed + * control_event_hsv2_descriptor_received + * control_event_hsv2_descriptor_failed + * control_event_hsv3_descriptor_failed * * So do not call this function directly. */ -void -control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +static void +event_hs_descriptor_receive_end(const char *action, + const char *onion_address, + const char *desc_id, + rend_auth_type_t auth_type, + const char *hsdir_id_digest, + const char *reason) { - char *desc_id_field = NULL; char *reason_field = NULL; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - const char *desc_id = NULL; - if (!action || !rend_data || !onion_address) { - log_warn(LD_BUG, "Called with action==%p, rend_data==%p, " - "onion_address==%p", action, rend_data, onion_address); + if (BUG(!action || !onion_address)) { return; } - desc_id = get_desc_id_from_query(rend_data, id_digest); - if (desc_id != NULL) { - /* Set the descriptor ID digest to base32 so we can send it. */ - base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, - DIGEST_LEN); - /* Extra whitespace is needed before the value. */ - tor_asprintf(&desc_id_field, " %s", desc_id_base32); - } - if (reason) { tor_asprintf(&reason_field, " REASON=%s", reason); } @@ -7130,14 +7371,13 @@ control_event_hs_descriptor_receive_end(const char *action, "650 HS_DESC %s %s %s %s%s%s\r\n", action, rend_hsaddress_str_or_unknown(onion_address), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_data)->auth_type), - id_digest ? - node_describe_longname_by_id(id_digest) : "UNKNOWN", - desc_id_field ? desc_id_field : "", + rend_auth_type_to_string(auth_type), + hsdir_id_digest ? + node_describe_longname_by_id(hsdir_id_digest) : + "UNKNOWN", + desc_id ? desc_id : "", reason_field ? reason_field : ""); - tor_free(desc_id_field); tor_free(reason_field); } @@ -7157,9 +7397,7 @@ control_event_hs_descriptor_upload_end(const char *action, { char *reason_field = NULL; - if (!action || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action, - id_digest); + if (BUG(!action || !id_digest)) { return; } @@ -7182,17 +7420,54 @@ control_event_hs_descriptor_upload_end(const char *action, * called when we successfully received a hidden service descriptor. */ void -control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest) +control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *hsdir_id_digest) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, NULL); + tor_free(desc_id_field); +} + +/* Send HS_DESC RECEIVED event + * + * Called when we successfully received a hidden service descriptor. */ +void +control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest) { - if (!rend_data || !id_digest || !onion_address) { - log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, " - "onion_address==%p", rend_data, id_digest, onion_address); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) { return; } - control_event_hs_descriptor_receive_end("RECEIVED", onion_address, - rend_data, id_digest, NULL); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, NULL); + tor_free(desc_id_field); } /** send HS_DESC UPLOADED event @@ -7203,9 +7478,7 @@ void control_event_hs_descriptor_uploaded(const char *id_digest, const char *onion_address) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } @@ -7219,17 +7492,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest, * add it to REASON= field. */ void -control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *hsdir_id_digest, + const char *reason) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data), + desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, reason); + tor_free(desc_id_field); +} + +/** Send HS_DESC event to inform controller that the query to + * <b>onion_address</b> failed to retrieve hidden service descriptor + * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If + * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON= + * field. */ +void +control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason) { - if (!rend_data) { - log_warn(LD_BUG, "Called with rend_data==%p", rend_data); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !reason)) { return; } - control_event_hs_descriptor_receive_end("FAILED", - rend_data_get_address(rend_data), - rend_data, id_digest, reason); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, reason); + tor_free(desc_id_field); } /** Send HS_DESC_CONTENT event after completion of a successful fetch from hs @@ -7279,9 +7593,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest, const char *onion_address, const char *reason) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } control_event_hs_descriptor_upload_end("FAILED", onion_address, diff --git a/src/or/control.h b/src/or/control.h index e957b593a6..28ffeaed86 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -27,6 +27,10 @@ void control_ports_write_to_file(void); #define LOG_FN_CONN(conn, args) \ CONN_LOG_PROTECT(conn, log_fn args) +#define CC_LOCAL_FD_IS_OWNER (1u<<0) +#define CC_LOCAL_FD_IS_AUTHENTICATED (1u<<1) +int control_connection_add_local_fd(tor_socket_t sock, unsigned flags); + int connection_control_finished_flushing(control_connection_t *conn); int connection_control_reached_eof(control_connection_t *conn); void connection_control_closed(control_connection_t *conn); @@ -111,32 +115,39 @@ void control_event_transport_launched(const char *mode, tor_addr_t *addr, uint16_t port); const char *rend_auth_type_to_string(rend_auth_type_t auth_type); MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); -void control_event_hs_descriptor_requested(const rend_data_t *rend_query, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +void control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, + const char *id_digest, + const char *desc_id, + const char *hsdir_index); +void control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica); -void control_event_hs_descriptor_upload(const char *service_id, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +void control_event_hs_descriptor_upload(const char *onion_address, + const char *desc_id, + const char *hs_dir, + const char *hsdir_index); void control_event_hs_descriptor_upload_end(const char *action, const char *onion_address, const char *hs_dir, const char *reason); -void control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest); void control_event_hs_descriptor_uploaded(const char *hs_dir, const char *onion_address); -void control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +/* Hidden service v2 HS_DESC specific. */ +void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *id_digest, + const char *reason); +void control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest); +/* Hidden service v3 HS_DESC specific. */ +void control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason); +void control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest); void control_event_hs_descriptor_upload_failed(const char *hs_dir, const char *onion_address, const char *reason); @@ -252,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); -STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, - const char **key_new_alg_out, - char **key_new_blob_out, - char **err_msg_out); +/* ADD_ONION secret key to create an ephemeral service. The command supports + * multiple versions so this union stores the key and passes it to the HS + * subsystem depending on the requested version. */ +typedef union add_onion_secret_key_t { + /* Hidden service v2 secret key. */ + crypto_pk_t *v2; + /* Hidden service v3 secret key. */ + ed25519_secret_key_t *v3; +} add_onion_secret_key_t; + +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, + int *hs_version, char **err_msg_out); + STATIC rend_authorized_client_t * add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index d9371b3446..50761dd4d3 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -48,14 +48,25 @@ worker_state_new(void *arg) ws->onion_keys = server_onion_keys_new(); return ws; } + +#define worker_state_free(ws) \ + FREE_AND_NULL(worker_state_t, worker_state_free_, (ws)) + static void -worker_state_free(void *arg) +worker_state_free_(worker_state_t *ws) { - worker_state_t *ws = arg; + if (!ws) + return; server_onion_keys_free(ws->onion_keys); tor_free(ws); } +static void +worker_state_free_void(void *arg) +{ + worker_state_free_(arg); +} + static replyqueue_t *replyqueue = NULL; static threadpool_t *threadpool = NULL; static struct event *reply_event = NULL; @@ -102,7 +113,7 @@ cpu_init(void) threadpool = threadpool_new(n_threads, replyqueue, worker_state_new, - worker_state_free, + worker_state_free_void, NULL); } /* Total voodoo. Can we make this more sensible? */ @@ -198,7 +209,7 @@ cpuworkers_rotate_keyinfo(void) if (threadpool_queue_update(threadpool, worker_state_new, update_state_threadfn, - worker_state_free, + worker_state_free_void, NULL)) { log_warn(LD_OR, "Failed to queue key update for worker threads."); } @@ -239,7 +250,7 @@ should_time_request(uint16_t onionskin_type) } /** Return an estimate of how many microseconds we will need for a single - * cpuworker to to process <b>n_requests</b> onionskins of type + * cpuworker to process <b>n_requests</b> onionskins of type * <b>onionskin_type</b>. */ uint64_t estimated_usec_for_onionskins(uint32_t n_requests, uint16_t onionskin_type) diff --git a/src/or/dircollate.c b/src/or/dircollate.c index d34ebe8af5..ce4534ff6c 100644 --- a/src/or/dircollate.c +++ b/src/or/dircollate.c @@ -41,9 +41,12 @@ typedef struct ddmap_entry_s { vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER]; } ddmap_entry_t; +#define ddmap_entry_free(e) \ + FREE_AND_NULL(ddmap_entry_t, ddmap_entry_free_, (e)) + /** Release all storage held by e. */ static void -ddmap_entry_free(ddmap_entry_t *e) +ddmap_entry_free_(ddmap_entry_t *e) { tor_free(e); } @@ -158,7 +161,7 @@ dircollator_new(int n_votes, int n_authorities) /** Release all storage held by <b>dc</b>. */ void -dircollator_free(dircollator_t *dc) +dircollator_free_(dircollator_t *dc) { if (!dc) return; diff --git a/src/or/dircollate.h b/src/or/dircollate.h index 7932e18998..0584b2fe06 100644 --- a/src/or/dircollate.h +++ b/src/or/dircollate.h @@ -18,7 +18,9 @@ typedef struct dircollator_s dircollator_t; dircollator_t *dircollator_new(int n_votes, int n_authorities); -void dircollator_free(dircollator_t *obj); +void dircollator_free_(dircollator_t *obj); +#define dircollator_free(c) \ + FREE_AND_NULL(dircollator_t, dircollator_free_, (c)) void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v); void dircollator_collate(dircollator_t *dc, int consensus_method); diff --git a/src/or/directory.c b/src/or/directory.c index f14bdde90b..983a3e2f34 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -25,6 +25,7 @@ #include "geoip.h" #include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "hs_client.h" #include "main.h" #include "microdesc.h" @@ -1101,7 +1102,7 @@ directory_request_new(uint8_t dir_purpose) * Release all resources held by <b>req</b>. */ void -directory_request_free(directory_request_t *req) +directory_request_free_(directory_request_t *req) { if (req == NULL) return; @@ -1111,7 +1112,7 @@ directory_request_free(directory_request_t *req) /** * Set the address and OR port to use for this directory request. If there is * no OR port, we'll have to connect over the dirport. (If there are both, - * the indirection setting determins which to use.) + * the indirection setting determines which to use.) */ void directory_request_set_or_addr_port(directory_request_t *req, @@ -1122,7 +1123,7 @@ directory_request_set_or_addr_port(directory_request_t *req, /** * Set the address and dirport to use for this directory request. If there * is no dirport, we'll have to connect over the OR port. (If there are both, - * the indirection setting determins which to use.) + * the indirection setting determines which to use.) */ void directory_request_set_dir_addr_port(directory_request_t *req, @@ -2196,8 +2197,6 @@ load_downloaded_routers(const char *body, smartlist_t *which, return added; } -static int handle_response_fetch_consensus(dir_connection_t *, - const response_handler_args_t *); static int handle_response_fetch_certificate(dir_connection_t *, const response_handler_args_t *); static int handle_response_fetch_status_vote(dir_connection_t *, @@ -2542,7 +2541,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) * consensus document by checking the consensus, storing it, and marking * router requests as reachable. **/ -static int +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, const response_handler_args_t *args) { @@ -3092,10 +3091,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* We got something: Try storing it in the cache. */ if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) { log_warn(LD_REND, "Failed to store hidden service descriptor"); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "BAD_DESC"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); } else { log_info(LD_REND, "Stored hidden service descriptor successfully."); TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; hs_client_desc_has_arrived(conn->hs_ident); + /* Fire control port RECEIVED event. */ + hs_control_desc_event_received(conn->hs_ident, conn->identity_digest); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + body); } break; case 404: @@ -3103,13 +3111,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, * tries to clean this conn up. */ log_info(LD_REND, "Fetching hidden service v3 descriptor not found: " "Retrying at another directory."); - /* TODO: Inform the control port */ + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "NOT_FOUND"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; case 400: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " "http status 400 (%s). Dirserver didn't like our " "query? Retrying at another directory.", escaped(reason)); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "QUERY_REJECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; default: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " @@ -3117,6 +3134,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, "'%s:%d'. Retrying at another directory.", status_code, escaped(reason), TO_CONN(conn)->address, TO_CONN(conn)->port); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; } @@ -3138,9 +3160,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, const size_t body_len = args->body_len; #define SEND_HS_DESC_FAILED_EVENT(reason) \ - (control_event_hs_descriptor_failed(conn->rend_data, \ - conn->identity_digest, \ - reason)) + (control_event_hsv2_descriptor_failed(conn->rend_data, \ + conn->identity_digest, \ + reason)) #define SEND_HS_DESC_FAILED_CONTENT() \ (control_event_hs_descriptor_content( \ rend_data_get_address(conn->rend_data), \ @@ -3175,9 +3197,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, /* success. notify pending connections about this. */ log_info(LD_REND, "Successfully fetched v2 rendezvous " "descriptor."); - control_event_hs_descriptor_received(service_id, - conn->rend_data, - conn->identity_digest); + control_event_hsv2_descriptor_received(service_id, + conn->rend_data, + conn->identity_digest); control_event_hs_descriptor_content(service_id, conn->requested_resource, conn->identity_digest, @@ -3294,7 +3316,7 @@ handle_response_upload_hsdesc(dir_connection_t *conn, case 200: log_info(LD_REND, "Uploading hidden service descriptor: " "finished with status 200 (%s)", escaped(reason)); - /* XXX: Trigger control event. */ + hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest); break; case 400: log_fn(LOG_PROTOCOL_WARN, LD_REND, @@ -3302,7 +3324,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "status 400 (%s) response from dirserver " "'%s:%d'. Malformed hidden service descriptor?", escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UPLOAD_REJECTED"); break; default: log_warn(LD_REND, "Uploading hidden service descriptor: http " @@ -3310,7 +3333,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "'%s:%d').", status_code, escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); break; } @@ -3389,7 +3413,7 @@ connection_dir_process_inbuf(dir_connection_t *conn) } /** We are closing a dir connection: If <b>dir_conn</b> is a dir connection - * that tried to fetch an HS descriptor, check if it successfuly fetched it, + * that tried to fetch an HS descriptor, check if it successfully fetched it, * or if we need to try again. */ static void refetch_hsdesc_if_needed(dir_connection_t *dir_conn) @@ -3476,63 +3500,47 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, long cache_lifetime) { char date[RFC1123_TIME_LEN+1]; - char tmp[1024]; - char *cp; time_t now = time(NULL); + buf_t *buf = buf_new_with_capacity(1024); tor_assert(conn); format_rfc1123_time(date, now); - cp = tmp; - tor_snprintf(cp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\n", - date); - cp += strlen(tmp); + + buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date); if (type) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type); - cp += strlen(cp); + buf_add_printf(buf, "Content-Type: %s\r\n", type); } if (!is_local_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - X_ADDRESS_HEADER "%s\r\n", conn->base_.address); - cp += strlen(cp); + buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address); } if (encoding) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Encoding: %s\r\n", encoding); - cp += strlen(cp); + buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding); } if (length >= 0) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Length: %ld\r\n", (long)length); - cp += strlen(cp); + buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length); } if (cache_lifetime > 0) { char expbuf[RFC1123_TIME_LEN+1]; format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime)); /* We could say 'Cache-control: max-age=%d' here if we start doing * http/1.1 */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Expires: %s\r\n", expbuf); - cp += strlen(cp); + buf_add_printf(buf, "Expires: %s\r\n", expbuf); } else if (cache_lifetime == 0) { /* We could say 'Cache-control: no-cache' here if we start doing * http/1.1 */ - strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, "Pragma: no-cache\r\n"); } if (extra_headers) { - strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, extra_headers); } - if (sizeof(tmp)-(cp-tmp) > 3) - memcpy(cp, "\r\n", 3); - else - tor_assert(0); - connection_buf_add(tmp, strlen(tmp), TO_CONN(conn)); + buf_add_string(buf, "\r\n"); + + connection_buf_add_buf(TO_CONN(conn), buf); + buf_free(buf); } /** As write_http_response_header_impl, but sets encoding and content-typed @@ -4028,7 +4036,7 @@ find_best_diff(const smartlist_t *digests, int flav, } /** Lookup the cached consensus document by the flavor found in <b>flav</b>. - * The prefered set of compression methods should be listed in the + * The preferred set of compression methods should be listed in the * <b>compression_methods</b> bitfield. The compression method chosen (if any) * is stored in <b>compression_used_out</b>. */ static struct consensus_cache_entry_t * @@ -4940,7 +4948,7 @@ handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args) /* Given the <b>url</b> from a POST request, try to extract the version number * using the provided <b>prefix</b>. The version should be after the prefix and - * ending with the seperator "/". For instance: + * ending with the separator "/". For instance: * /tor/hs/3/publish * * On success, <b>end_pos</b> points to the position right after the version diff --git a/src/or/directory.h b/src/or/directory.h index 5e6a91d3e7..edf75ffe13 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -51,7 +51,9 @@ int directory_must_use_begindir(const or_options_t *options); */ typedef struct directory_request_t directory_request_t; directory_request_t *directory_request_new(uint8_t dir_purpose); -void directory_request_free(directory_request_t *req); +void directory_request_free_(directory_request_t *req); +#define directory_request_free(req) \ + FREE_AND_NULL(directory_request_t, directory_request_free_, (req)) void directory_request_set_or_addr_port(directory_request_t *req, const tor_addr_port_t *p); void directory_request_set_dir_addr_port(directory_request_t *req, @@ -238,6 +240,9 @@ STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn, STATIC int handle_response_fetch_microdesc(dir_connection_t *conn, const response_handler_args_t *args); +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, + const response_handler_args_t *args); + #endif /* defined(DIRECTORY_PRIVATE) */ #ifdef TOR_UNIT_TESTS diff --git a/src/or/dirserv.c b/src/or/dirserv.c index ff85dfa2ac..0f47a83986 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -43,7 +43,7 @@ * the directory authority functionality. The directory.c module delegates * here in order to handle incoming requests from clients, via * connection_dirserv_flushed_some() and its kin. In order to save RAM, this - * module is reponsible for spooling directory objects (in whole or in part) + * module is responsible for spooling directory objects (in whole or in part) * onto buf_t instances, and then closing the dir_connection_t once the * objects are totally flushed. * @@ -1092,7 +1092,7 @@ router_is_active(const routerinfo_t *ri, const node_t *node, time_t now) if (!node->is_running || !node->is_valid || ri->is_hibernating) { return 0; } - /* Only require bandwith capacity in non-test networks, or + /* Only require bandwidth capacity in non-test networks, or * if TestingTorNetwork, and TestingMinExitFlagThreshold is non-zero */ if (!ri->bandwidthcapacity) { if (get_options()->TestingTorNetwork) { @@ -1525,15 +1525,21 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) node->ri && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) continue; + + routerinfo_t *ri = node->ri; + if (ri) { + node->is_exit = (!router_exit_policy_rejects_all(ri) && + exit_policy_is_general_exit(ri->exit_policy)); + } + if (router_counts_toward_thresholds(node, now, omit_as_sybil, require_mbw)) { - routerinfo_t *ri = node->ri; const char *id = node->identity; uint32_t bw_kb; + /* resolve spurious clang shallow analysis null pointer errors */ tor_assert(ri); - node->is_exit = (!router_exit_policy_rejects_all(ri) && - exit_policy_is_general_exit(ri->exit_policy)); + uptimes[n_active] = (uint32_t)real_uptime(ri, now); mtbfs[n_active] = rep_hist_get_stability(id, now); tks [n_active] = rep_hist_get_weighted_time_known(id, now); @@ -1903,21 +1909,28 @@ version_from_platform(const char *platform) /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. + * + * consensus_method is the current consensus method when format is + * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other + * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD. + * * Return 0 on success, -1 on failure. * * The format argument has one of the following values: * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry + * for consensus_method. * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc - * consensus entry. + * consensus entry for consensus_method. * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present, * it contains additional information for the vote. - * NS_CONTROL_PORT - Output a NS document for the control port + * NS_CONTROL_PORT - Output a NS document for the control port. */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs) { char *summary; @@ -1948,8 +1961,10 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, * networkstatus_type_t values, with an additional control port value * added -MP */ - /* V3 microdesc consensuses don't have "a" lines. */ - if (format == NS_V3_CONSENSUS_MICRODESC) + /* V3 microdesc consensuses only have "a" lines in later consensus methods + */ + if (format == NS_V3_CONSENSUS_MICRODESC && + consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS) goto done; /* Possible "a" line. At most one for now. */ @@ -1958,7 +1973,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); } - if (format == NS_V3_CONSENSUS) + if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) goto done; smartlist_add_asprintf(chunks, @@ -2219,7 +2234,8 @@ routers_make_ed_keys_unique(smartlist_t *routers) } /** Extract status information from <b>ri</b> and from other authority - * functions and store it in <b>rs</b>>. + * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is + * set. * * We assume that ri-\>is_running has already been set, e.g. by * dirserv_set_router_is_running(ri, now); @@ -2285,6 +2301,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, OR port and it's reachable so copy it to the routerstatus. */ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); rs->ipv6_orport = ri->ipv6_orport; + } else { + tor_addr_make_null(&rs->ipv6_addr, AF_INET6); + rs->ipv6_orport = 0; } if (options->TestingTorNetwork) { @@ -3290,7 +3309,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, ri = node->ri; if (get_options()->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && ri->cache_info.signing_key_cert) { /* We allow the node to have an ed25519 key if we haven't been told one in * the routerinfo, but if we *HAVE* been told one in the routerinfo, it @@ -3373,7 +3392,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) tor_assert(node); if (options->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node)) { + node_supports_ed25519_link_authentication(node, 1)) { ed_id_key = &router->cache_info.signing_key_cert->signing_key; } else { ed_id_key = NULL; @@ -3502,7 +3521,7 @@ spooled_resource_new_from_cache_entry(consensus_cache_entry_t *entry) /** Release all storage held by <b>spooled</b>. */ void -spooled_resource_free(spooled_resource_t *spooled) +spooled_resource_free_(spooled_resource_t *spooled) { if (spooled == NULL) return; diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 46967a6cb2..b9af68ff6e 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -123,7 +123,7 @@ void dirserv_set_cached_consensus_networkstatus(const char *consensus, void dirserv_clear_old_networkstatuses(time_t cutoff); int dirserv_get_routerdesc_spool(smartlist_t *spools_out, const char *key, dir_spool_source_t source, - int conn_is_encrytped, + int conn_is_encrypted, const char **msg_out); int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, const char **msg); @@ -150,6 +150,7 @@ char *routerstatus_format_entry( const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs); void dirserv_free_all(void); void cached_dir_decref(cached_dir_t *d); @@ -195,7 +196,9 @@ spooled_resource_t *spooled_resource_new(dir_spool_source_t source, size_t digestlen); spooled_resource_t *spooled_resource_new_from_cache_entry( struct consensus_cache_entry_t *entry); -void spooled_resource_free(spooled_resource_t *spooled); +void spooled_resource_free_(spooled_resource_t *spooled); +#define spooled_resource_free(sp) \ + FREE_AND_NULL(spooled_resource_t, spooled_resource_free_, (sp)) void dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn, time_t cutoff, int compression, diff --git a/src/or/dirvote.c b/src/or/dirvote.c index ce82a5ef4a..c3cd0d3cd1 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -278,7 +278,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, vote_microdesc_hash_t *h; rsf = routerstatus_format_entry(&vrs->status, vrs->version, vrs->protocols, - NS_V3_VOTE, vrs); + NS_V3_VOTE, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + vrs); if (rsf) smartlist_add(chunks, rsf); @@ -519,7 +521,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, /* compare_vote_rs_() sorts the items by identity digest (all the same), * then by SD digest. That way, if we have a tie that the published_on - * date cannot tie, we use the descriptor with the smaller digest. + * date cannot break, we use the descriptor with the smaller digest. */ smartlist_sort(votes, compare_vote_rs_); SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { @@ -663,7 +665,7 @@ static int consensus_method_is_supported(int method) { if (method == MIN_METHOD_FOR_ED25519_ID_IN_MD) { - /* This method was broken due to buggy code accidently left in + /* This method was broken due to buggy code accidentally left in * dircollate.c; do not actually use it. */ return 0; @@ -795,6 +797,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) output = smartlist_new(); SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) { + /* resolve spurious clang shallow analysis null pointer errors */ + tor_assert(param); + const char *next_param; int ok=0; eq = strchr(param, '='); @@ -807,8 +812,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) next_param = NULL; else next_param = smartlist_get(param_list, param_sl_idx+1); - /* resolve spurious clang shallow analysis null pointer errors */ - tor_assert(param); + if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ /* Make sure enough authorities voted on this param, unless the @@ -1315,8 +1319,9 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) /** Given a list of vote networkstatus_t in <b>votes</b>, our public * authority <b>identity_key</b>, our private authority <b>signing_key</b>, * and the number of <b>total_authorities</b> that we believe exist in our - * voting quorum, generate the text of a new v3 consensus vote, and return the - * value in a newly allocated string. + * voting quorum, generate the text of a new v3 consensus or microdescriptor + * consensus (depending on <b>flavor</b>), and return the value in a newly + * allocated string. * * Note: this function DOES NOT check whether the votes are from * recognized authorities. (dirvote_add_vote does that.) @@ -2099,7 +2104,8 @@ networkstatus_compute_consensus(smartlist_t *votes, char *buf; /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL); + buf = routerstatus_format_entry(&rs_out, NULL, NULL, + rs_format, consensus_method, NULL); if (buf) smartlist_add(chunks, buf); } @@ -2685,7 +2691,7 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, /** Release all storage held in <b>s</b>. */ void -ns_detached_signatures_free(ns_detached_signatures_t *s) +ns_detached_signatures_free_(ns_detached_signatures_t *s) { if (!s) return; @@ -2843,10 +2849,13 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity) return new_voting_schedule; } +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + /** Frees a voting_schedule_t. This should be used instead of the generic * tor_free. */ static void -voting_schedule_free(voting_schedule_t *voting_schedule_to_free) +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) { if (!voting_schedule_to_free) return; @@ -3551,7 +3560,13 @@ dirvote_add_signatures_to_pending_consensus( } r = networkstatus_add_detached_signatures(pc->consensus, sigs, source, severity, msg_out); - log_info(LD_DIR,"Added %d signatures to consensus.", r); + if (r >= 0) { + log_info(LD_DIR,"Added %d signatures to consensus.", r); + } else { + log_fn(LOG_PROTOCOL_WARN, LD_DIR, + "Unable to add signatures to consensus: %s", + *msg_out ? *msg_out : "(unknown)"); + } if (r >= 1) { char *new_signatures = @@ -3829,7 +3844,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } + /* We originally put a lines in the micrdescriptors, but then we worked out + * that we needed them in the microdesc consensus. See #20916. */ if (consensus_method >= MIN_METHOD_FOR_A_LINES && + consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC && !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) smartlist_add_asprintf(chunks, "a %s\n", fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); @@ -3938,7 +3956,9 @@ static const struct consensus_method_range_t { {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1}, {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1}, - {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_METHOD_FOR_ED25519_ID_IN_MD, + MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1}, + {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/or/dirvote.h b/src/or/dirvote.h index 72a35fea6d..deeb27bfe1 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -51,11 +51,15 @@ #define MIN_VOTE_INTERVAL_TESTING_INITIAL \ ((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1) +/* A placeholder for routerstatus_format_entry() when the consensus method + * argument is not applicable. */ +#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0 + /** The lowest consensus method that we currently support. */ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 26 +#define MAX_SUPPORTED_CONSENSUS_METHOD 28 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -115,6 +119,14 @@ * instead of 0. See #14881 */ #define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26 +/** Lowest consensus method where the microdesc consensus contains relay IPv6 + * addresses. See #23826 and #20916. */ +#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27 + +/** Lowest consensus method where microdescriptors do not contain relay IPv6 + * addresses. See #23828 and #20916. */ +#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ @@ -136,7 +148,9 @@ int networkstatus_add_detached_signatures(networkstatus_t *target, int severity, const char **msg_out); char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free(ns_detached_signatures_t *s); +void ns_detached_signatures_free_(ns_detached_signatures_t *s); +#define ns_detached_signatures_free(s) \ + FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) /* cert manipulation */ authority_cert_t *authority_cert_dup(authority_cert_t *cert); diff --git a/src/or/dns.c b/src/or/dns.c index 7dc3575f53..e4dc8048ed 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -19,7 +19,7 @@ * dns_seems_to_be_broken(). * <li>When a client has asked the relay, in a RELAY_BEGIN cell, to connect * to a given server by hostname. This happens via dns_resolve(). - * <li>When a client has asked the rela, in a RELAY_RESOLVE cell, to look + * <li>When a client has asked the relay, in a RELAY_RESOLVE cell, to look * up a given server's IP address(es) by hostname. This also happens via * dns_resolve(). * </ol> @@ -457,7 +457,7 @@ purge_expired_resolves(time_t now) if (!pendconn->base_.marked_for_close) { connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } tor_free(pend); } @@ -670,7 +670,7 @@ dns_resolve(edge_connection_t *exitconn) /* If we made the connection pending, then we freed it already in * dns_cancel_pending_resolve(). If we marked it for close, it'll * get freed from the main loop. Otherwise, can free it now. */ - connection_free(TO_CONN(exitconn)); + connection_free_(TO_CONN(exitconn)); } break; default: @@ -1101,7 +1101,7 @@ dns_cancel_pending_resolve,(const char *address)) if (circ) circuit_detach_stream(circ, pendconn); if (!pendconn->base_.marked_for_close) - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); resolve->pending_connections = pend->next; tor_free(pend); } @@ -1230,7 +1230,7 @@ inform_pending_connections(cached_resolve_t *resolve) /* This detach must happen after we send the resolved cell. */ circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); } - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } else { circuit_t *circ; if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) { @@ -1259,7 +1259,7 @@ inform_pending_connections(cached_resolve_t *resolve) circ = circuit_get_by_edge_conn(pendconn); tor_assert(circ); circuit_detach_stream(circ, pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } } resolve->pending_connections = pend->next; @@ -1666,7 +1666,7 @@ launch_resolve,(cached_resolve_t *resolve)) tor_addr_t a; int r; - if (get_options()->DisableNetwork) + if (net_is_disabled()) return -1; /* What? Nameservers not configured? Sounds like a bug. */ @@ -1901,7 +1901,7 @@ launch_test_addresses(evutil_socket_t fd, short event, void *args) (void)event; (void)args; - if (options->DisableNetwork) + if (net_is_disabled()) return; log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index d254717a54..9385561d99 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -172,7 +172,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) { log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - connection_free(ENTRY_TO_CONN(entry_conn)); + connection_free_(ENTRY_TO_CONN(entry_conn)); return; } @@ -249,7 +249,7 @@ dnsserv_launch_request(const char *name, int reverse, if (connection_add(TO_CONN(conn))<0) { log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return -1; } diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 67b0259243..2b6ff38c9c 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1094,7 +1094,11 @@ select_and_add_guard_item_for_sample(guard_selection_t *gs, return added_guard; } -/** Return true iff we need a consensus to maintain our */ +/** + * Return true iff we need a consensus to update our guards, but we don't + * have one. (We can return 0 here either if the consensus is _not_ missing, + * or if we don't need a consensus because we're using bridges.) + */ static int live_consensus_is_missing(const guard_selection_t *gs) { @@ -2196,7 +2200,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b) /** Release all storage held in <b>restriction</b> */ STATIC void -entry_guard_restriction_free(entry_guard_restriction_t *rst) +entry_guard_restriction_free_(entry_guard_restriction_t *rst) { tor_free(rst); } @@ -2205,7 +2209,7 @@ entry_guard_restriction_free(entry_guard_restriction_t *rst) * Release all storage held in <b>state</b>. */ void -circuit_guard_state_free(circuit_guard_state_t *state) +circuit_guard_state_free_(circuit_guard_state_t *state) { if (!state) return; @@ -2216,9 +2220,9 @@ circuit_guard_state_free(circuit_guard_state_t *state) /** Allocate and return a new circuit_guard_state_t to track the result * of using <b>guard</b> for a given operation. */ -static circuit_guard_state_t * -circuit_guard_state_new(entry_guard_t *guard, unsigned state, - entry_guard_restriction_t *rst) +MOCK_IMPL(STATIC circuit_guard_state_t *, +circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)) { circuit_guard_state_t *result; @@ -3108,7 +3112,7 @@ get_guard_state_for_bridge_desc_fetch(const char *digest) /** Release all storage held by <b>e</b>. */ STATIC void -entry_guard_free(entry_guard_t *e) +entry_guard_free_(entry_guard_t *e) { if (!e) return; @@ -3303,6 +3307,22 @@ entry_guards_update_state(or_state_t *state) entry_guards_dirty = 0; } +/** Return true iff the circuit's guard can succeed that is can be used. */ +int +entry_guard_could_succeed(const circuit_guard_state_t *guard_state) +{ + if (!guard_state) { + return 0; + } + + entry_guard_t *guard = entry_guard_handle_get(guard_state->guard); + if (!guard || BUG(guard->in_selection == NULL)) { + return 0; + } + + return 1; +} + /** * Format a single entry guard in the format expected by the controller. * Return a newly allocated string. @@ -3615,7 +3635,7 @@ entry_guards_get_err_str_if_dir_info_missing(int using_mds, /** Free one guard selection context */ STATIC void -guard_selection_free(guard_selection_t *gs) +guard_selection_free_(guard_selection_t *gs) { if (!gs) return; diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index d909115b1f..d562498313 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -220,9 +220,10 @@ struct guard_selection_s { guard_selection_type_t type; /** - * A value of 1 means that primary_entry_guards is up-to-date; 0 - * means we need to recalculate it before using primary_entry_guards - * or the is_primary flag on any guard. + * A value of 1 means that primary_entry_guards is up-to-date with respect to + * the consensus and status info that we currently have; 0 means we need to + * recalculate it before using primary_entry_guards or the is_primary flag on + * any guard. */ int primary_guards_up_to_date; @@ -356,7 +357,10 @@ typedef enum { GUARD_USAGE_DIRGUARD = 1 } guard_usage_t; -void circuit_guard_state_free(circuit_guard_state_t *state); +#define circuit_guard_state_free(val) \ + FREE_AND_NULL(circuit_guard_state_t, circuit_guard_state_free_, (val)) + +void circuit_guard_state_free_(circuit_guard_state_t *state); int entry_guard_pick_for_circuit(guard_selection_t *gs, guard_usage_t usage, entry_guard_restriction_t *rst, @@ -383,6 +387,8 @@ void entry_guards_note_internet_connectivity(guard_selection_t *gs); int update_guard_selection_choice(const or_options_t *options); +int entry_guard_could_succeed(const circuit_guard_state_t *guard_state); + MOCK_DECL(int,num_bridges_usable,(int use_maybe_reachable)); #ifdef ENTRYNODES_PRIVATE @@ -475,6 +481,9 @@ STATIC double get_meaningful_restriction_threshold(void); STATIC double get_extreme_restriction_threshold(void); HANDLE_DECL(entry_guard, entry_guard_t, STATIC) +#define entry_guard_handle_free(h) \ + FREE_AND_NULL(entry_guard_handle_t, entry_guard_handle_free_, (h)) + STATIC guard_selection_type_t guard_selection_infer_type( guard_selection_type_t type_in, const char *name); @@ -482,7 +491,9 @@ STATIC guard_selection_t *guard_selection_new(const char *name, guard_selection_type_t type); STATIC guard_selection_t *get_guard_selection_by_name( const char *name, guard_selection_type_t type, int create_if_absent); -STATIC void guard_selection_free(guard_selection_t *gs); +STATIC void guard_selection_free_(guard_selection_t *gs); +#define guard_selection_free(gs) \ + FREE_AND_NULL(guard_selection_t, guard_selection_free_, (gs)) MOCK_DECL(STATIC int, entry_guard_is_listed, (guard_selection_t *gs, const entry_guard_t *guard)); STATIC const char *choose_guard_selection(const or_options_t *options, @@ -494,12 +505,18 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs, MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate)); +MOCK_DECL(STATIC circuit_guard_state_t *, + circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)); + STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs, const node_t *node); STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs); STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); -STATIC void entry_guard_free(entry_guard_t *e); +#define entry_guard_free(e) \ + FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e)) +STATIC void entry_guard_free_(entry_guard_t *e); STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /** @@ -562,7 +579,10 @@ STATIC entry_guard_restriction_t *guard_create_exit_restriction( STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void); -STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst); +STATIC void entry_guard_restriction_free_(entry_guard_restriction_t *rst); +#define entry_guard_restriction_free(rst) \ + FREE_AND_NULL(entry_guard_restriction_t, \ + entry_guard_restriction_free_, (rst)) #endif /* defined(ENTRYNODES_PRIVATE) */ diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index 28377a3f36..16a250fa58 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -40,7 +40,7 @@ ext_or_cmd_new(uint16_t len) /** Deallocate the Extended ORPort message in <b>cmd</b>. */ void -ext_or_cmd_free(ext_or_cmd_t *cmd) +ext_or_cmd_free_(ext_or_cmd_t *cmd) { tor_free(cmd); } diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index af2b97e77c..09acbc407e 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -10,7 +10,11 @@ int connection_ext_or_start_auth(or_connection_t *or_conn); ext_or_cmd_t *ext_or_cmd_new(uint16_t len); -void ext_or_cmd_free(ext_or_cmd_t *cmd); + +#define ext_or_cmd_free(cmd) \ + FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd)) + +void ext_or_cmd_free_(ext_or_cmd_t *cmd); void connection_or_set_ext_or_identifier(or_connection_t *conn); void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); void connection_or_clear_ext_or_id_map(void); diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c index f730106d06..c938e76678 100644 --- a/src/or/fp_pair.c +++ b/src/or/fp_pair.c @@ -196,7 +196,7 @@ fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) */ void -fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) +fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)) { fp_pair_map_entry_t **ent, **next, *this; diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h index f7c060b459..4498a16101 100644 --- a/src/or/fp_pair.h +++ b/src/or/fp_pair.h @@ -26,7 +26,12 @@ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key); void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, const char *first, const char *second); void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key); -void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)); +void fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)); +#define fp_pair_map_free(map, free_val) do { \ + fp_pair_map_free_((map), (free_val)); \ + (map) = NULL; \ + } while (0) + int fp_pair_map_isempty(const fp_pair_map_t *map); int fp_pair_map_size(const fp_pair_map_t *map); fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map); diff --git a/src/or/geoip.c b/src/or/geoip.c index 14e0b1b6fa..0ff1c6ce0d 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -30,6 +30,7 @@ #define GEOIP_PRIVATE #include "or.h" #include "ht.h" +#include "buffers.h" #include "config.h" #include "control.h" #include "dnsserv.h" @@ -541,6 +542,9 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash, clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) +#define clientmap_entry_free(ent) \ + FREE_AND_NULL(clientmap_entry_t, clientmap_entry_free_, ent) + /** Return the size of a client map entry. */ static inline size_t clientmap_entry_size(const clientmap_entry_t *ent) @@ -552,7 +556,7 @@ clientmap_entry_size(const clientmap_entry_t *ent) /** Free all storage held by <b>ent</b>. */ static void -clientmap_entry_free(clientmap_entry_t *ent) +clientmap_entry_free_(clientmap_entry_t *ent) { if (!ent) return; @@ -1067,9 +1071,9 @@ static char * geoip_get_dirreq_history(dirreq_type_t type) { char *result = NULL; + buf_t *buf = NULL; smartlist_t *dirreq_completed = NULL; uint32_t complete = 0, timeouts = 0, running = 0; - int bufsize = 1024, written; dirreq_map_entry_t **ptr, **next; struct timeval now; @@ -1102,13 +1106,9 @@ geoip_get_dirreq_history(dirreq_type_t type) DIR_REQ_GRANULARITY); running = round_uint32_to_next_multiple_of(running, DIR_REQ_GRANULARITY); - result = tor_malloc_zero(bufsize); - written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u," - "running=%u", complete, timeouts, running); - if (written < 0) { - tor_free(result); - goto done; - } + buf = buf_new_with_capacity(1024); + buf_add_printf(buf, "complete=%u,timeout=%u," + "running=%u", complete, timeouts, running); #define MIN_DIR_REQ_RESPONSES 16 if (complete >= MIN_DIR_REQ_RESPONSES) { @@ -1129,7 +1129,7 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[ent_sl_idx] = bytes_per_second; } SMARTLIST_FOREACH_END(ent); median_uint32(dltimes, complete); /* sorts as a side effect. */ - written = tor_snprintf(result + written, bufsize - written, + buf_add_printf(buf, ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u," "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u", dltimes[0], @@ -1145,14 +1145,15 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[8*complete/10-1], dltimes[9*complete/10-1], dltimes[complete-1]); - if (written<0) - tor_free(result); tor_free(dltimes); } - done: + + result = buf_extract(buf, NULL); + SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent, tor_free(ent)); smartlist_free(dirreq_completed); + buf_free(buf); return result; } diff --git a/src/or/git_revision.c b/src/or/git_revision.c new file mode 100644 index 0000000000..8f326b8751 --- /dev/null +++ b/src/or/git_revision.c @@ -0,0 +1,17 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "git_revision.h" + +/** String describing which Tor Git repository version the source was + * built from. This string is generated by a bit of shell kludging in + * src/or/include.am, and is usually right. + */ +const char tor_git_revision[] = +#ifndef _MSC_VER +#include "micro-revision.i" +#endif + ""; + diff --git a/src/or/git_revision.h b/src/or/git_revision.h new file mode 100644 index 0000000000..5613cb4335 --- /dev/null +++ b/src/or/git_revision.h @@ -0,0 +1,12 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_GIT_REVISION_H +#define TOR_GIT_REVISION_H + +extern const char tor_git_revision[]; + +#endif /* !defined(TOR_GIT_REVISION_H) */ + diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 74ab766468..4dc35f68d0 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -34,6 +34,7 @@ hibernating, phase 2: #include "config.h" #include "connection.h" #include "connection_edge.h" +#include "connection_or.h" #include "control.h" #include "hibernate.h" #include "main.h" @@ -818,8 +819,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now) log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", hibernate_state == HIBERNATE_STATE_EXITING ? "a second time" : "while hibernating"); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } if (new_state == HIBERNATE_STATE_LOWBANDWIDTH && @@ -906,20 +907,23 @@ hibernate_go_dormant(time_t now) while ((conn = connection_get_by_type(CONN_TYPE_OR)) || (conn = connection_get_by_type(CONN_TYPE_AP)) || (conn = connection_get_by_type(CONN_TYPE_EXIT))) { - if (CONN_IS_EDGE(conn)) + if (CONN_IS_EDGE(conn)) { connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); + } log_info(LD_NET,"Closing conn type %d", conn->type); - if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ + if (conn->type == CONN_TYPE_AP) { + /* send socks failure if needed */ connection_mark_unattached_ap(TO_ENTRY_CONN(conn), END_STREAM_REASON_HIBERNATING); - else if (conn->type == CONN_TYPE_OR) { + } else if (conn->type == CONN_TYPE_OR) { if (TO_OR_CONN(conn)->chan) { - channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); + connection_or_close_normally(TO_OR_CONN(conn), 0); } else { connection_mark_for_close(conn); } - } else + } else { connection_mark_for_close(conn); + } } if (now < interval_wakeup_time) @@ -980,8 +984,7 @@ consider_hibernation(time_t now) tor_assert(shutdown_time); if (shutdown_time <= now) { log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); } return; /* if exiting soon, don't worry about bandwidth limits */ } diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c index 6a5a3895b0..df53efd32d 100644 --- a/src/or/hs_cache.c +++ b/src/or/hs_cache.c @@ -52,9 +52,12 @@ lookup_v3_desc_as_dir(const uint8_t *key) return digest256map_get(hs_cache_v3_dir, key); } +#define cache_dir_desc_free(val) \ + FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val)) + /* Free a directory descriptor object. */ static void -cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) +cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc) { if (desc == NULL) { return; @@ -67,10 +70,9 @@ cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) /* Helper function: Use by the free all function using the digest256map * interface to cache entries. */ static void -cache_dir_desc_free_(void *ptr) +cache_dir_desc_free_void(void *ptr) { - hs_cache_dir_descriptor_t *desc = ptr; - cache_dir_desc_free(desc); + cache_dir_desc_free_(ptr); } /* Create a new directory cache descriptor object from a encoded descriptor. @@ -417,9 +419,12 @@ cache_client_desc_new(const char *desc_str, return client_desc; } +#define cache_client_desc_free(val) \ + FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val)) + /** Free memory allocated by <b>desc</b>. */ static void -cache_client_desc_free(hs_cache_client_descriptor_t *desc) +cache_client_desc_free_(hs_cache_client_descriptor_t *desc) { if (desc == NULL) { return; @@ -433,7 +438,7 @@ cache_client_desc_free(hs_cache_client_descriptor_t *desc) /** Helper function: Use by the free all function to clear the client cache */ static void -cache_client_desc_free_(void *ptr) +cache_client_desc_free_void(void *ptr) { hs_cache_client_descriptor_t *desc = ptr; cache_client_desc_free(desc); @@ -448,18 +453,21 @@ cache_intro_state_new(void) return state; } +#define cache_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val)) + /* Free an hs_cache_intro_state_t object. */ static void -cache_intro_state_free(hs_cache_intro_state_t *state) +cache_intro_state_free_(hs_cache_intro_state_t *state) { tor_free(state); } /* Helper function: use by the free all function. */ static void -cache_intro_state_free_(void *state) +cache_intro_state_free_void(void *state) { - cache_intro_state_free(state); + cache_intro_state_free_(state); } /* Return a newly allocated and initialized hs_cache_client_intro_state_t @@ -472,22 +480,26 @@ cache_client_intro_state_new(void) return cache; } +#define cache_client_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_client_intro_state_t, \ + cache_client_intro_state_free_, (val)) + /* Free a cache client intro state object. */ static void -cache_client_intro_state_free(hs_cache_client_intro_state_t *cache) +cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache) { if (cache == NULL) { return; } - digest256map_free(cache->intro_points, cache_intro_state_free_); + digest256map_free(cache->intro_points, cache_intro_state_free_void); tor_free(cache); } /* Helper function: use by the free all function. */ static void -cache_client_intro_state_free_(void *entry) +cache_client_intro_state_free_void(void *entry) { - cache_client_intro_state_free(entry); + cache_client_intro_state_free_(entry); } /* For the given service identity key service_pk and an introduction @@ -690,7 +702,7 @@ cache_clean_v3_as_client(time_t now) /* 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 - * a loop so we have to explicitely decrement. */ + * a loop so we have to explicitly decrement. */ rend_cache_decrement_allocation(entry_size); /* Logging. */ { @@ -706,6 +718,24 @@ cache_clean_v3_as_client(time_t now) } /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return + * its HS encoded descriptor if it's stored in our cache, or NULL if not. */ +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key) +{ + hs_cache_client_descriptor_t *cached_desc = NULL; + + tor_assert(key); + + cached_desc = lookup_v3_desc_as_client(key->pubkey); + if (cached_desc) { + tor_assert(cached_desc->encoded_desc); + return cached_desc->encoded_desc; + } + + return NULL; +} + +/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return * its HS descriptor if it's stored in our cache, or NULL if not. */ const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key) @@ -775,7 +805,7 @@ hs_cache_purge_as_client(void) MAP_DEL_CURRENT(key); cache_client_desc_free(entry); /* Update our OOM. We didn't use the remove() function because we are in - * a loop so we have to explicitely decrement. */ + * a loop so we have to explicitly decrement. */ rend_cache_decrement_allocation(entry_size); } DIGEST256MAP_FOREACH_END; @@ -933,14 +963,14 @@ hs_cache_init(void) void hs_cache_free_all(void) { - digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_); + digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void); hs_cache_v3_dir = NULL; - digest256map_free(hs_cache_v3_client, cache_client_desc_free_); + digest256map_free(hs_cache_v3_client, cache_client_desc_free_void); hs_cache_v3_client = NULL; digest256map_free(hs_cache_client_intro_state, - cache_client_intro_state_free_); + cache_client_intro_state_free_void); hs_cache_client_intro_state = NULL; } diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h index 2dcc518a71..a141634cc4 100644 --- a/src/or/hs_cache.h +++ b/src/or/hs_cache.h @@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query, const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key); +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key); int hs_cache_store_as_client(const char *desc_str, const ed25519_public_key_t *identity_pk); void hs_cache_clean_as_client(time_t now); diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index 66c59e0dc7..3a674f6223 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -13,6 +13,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "nodelist.h" #include "policies.h" #include "relay.h" #include "rendservice.h" @@ -558,76 +559,99 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) return; } -/* Using an extend info object ei, set all possible link specifiers in lspecs. - * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present, - * logs a BUG() warning, and returns an empty smartlist. Clients never make - * direct connections to rendezvous points, so they should always have an - * IPv4 address in ei. */ +/* Add all possible link specifiers in node to lspecs. + * legacy ID is mandatory thus MUST be present in node. If the primary address + * is not IPv4, log a BUG() warning, and return an empty smartlist. + * Includes ed25519 id and IPv6 link specifiers if present in the node. */ static void -get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) +get_lspecs_from_node(const node_t *node, smartlist_t *lspecs) { link_specifier_t *ls; + tor_addr_port_t ap; - tor_assert(ei); + tor_assert(node); tor_assert(lspecs); - /* We require IPv4, we will add IPv6 support in a later tor version */ - if (BUG(!tor_addr_is_v4(&ei->addr))) { + /* Get the relay's IPv4 address. */ + node_get_prim_orport(node, &ap); + + /* We expect the node's primary address to be a valid IPv4 address. + * This conforms to the protocol, which requires either an IPv4 or IPv6 + * address (or both). */ + if (BUG(!tor_addr_is_v4(&ap.addr)) || + BUG(!tor_addr_port_is_valid_ap(&ap, 0))) { return; } ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_IPV4); - link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr)); - link_specifier_set_un_ipv4_port(ls, ei->port); + link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr)); + link_specifier_set_un_ipv4_port(ls, ap.port); /* Four bytes IPv4 and two bytes port. */ - link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) + - sizeof(ei->port)); + link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) + + sizeof(ap.port)); smartlist_add(lspecs, ls); - /* Legacy ID is mandatory. */ + /* Legacy ID is mandatory and will always be present in node. */ ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_LEGACY_ID); - memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest, + memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity, link_specifier_getlen_un_legacy_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is only included if the extend_info has it. */ - if (!ed25519_public_key_is_zero(&ei->ed_identity)) { + /* ed25519 ID is only included if the node has it. */ + if (!ed25519_public_key_is_zero(&node->ed25519_id)) { ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_ED25519_ID); - memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity, + memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id, link_specifier_getlen_un_ed25519_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); smartlist_add(lspecs, ls); } + + /* Check for IPv6. If so, include it as well. */ + if (node_has_ipv6_orport(node)) { + ls = link_specifier_new(); + node_get_pref_ipv6_orport(node, &ap); + link_specifier_set_ls_type(ls, LS_IPV6); + size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls); + const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr); + uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls); + memcpy(ipv6_array, in6_addr, addr_len); + link_specifier_set_un_ipv6_port(ls, ap.port); + /* Sixteen bytes IPv6 and two bytes port. */ + link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port)); + smartlist_add(lspecs, ls); + } } -/* Using the given descriptor intro point ip, the extend information of the - * rendezvous point rp_ei and the service's subcredential, populate the +/* Using the given descriptor intro point ip, the node of the + * rendezvous point rp_node and the service's subcredential, populate the * already allocated intro1_data object with the needed key material and link * specifiers. * - * This can't fail but the ip MUST be a valid object containing the needed - * keys and authentication method. */ + * If rp_node has an invalid primary address, intro1_data->link_specifiers + * will be an empty list. Otherwise, this function can't fail. The ip + * MUST be a valid object containing the needed keys and authentication + * method. */ static void setup_introduce1_data(const hs_desc_intro_point_t *ip, - const extend_info_t *rp_ei, + const node_t *rp_node, const uint8_t *subcredential, hs_cell_introduce1_data_t *intro1_data) { smartlist_t *rp_lspecs; tor_assert(ip); - tor_assert(rp_ei); + tor_assert(rp_node); tor_assert(subcredential); tor_assert(intro1_data); /* Build the link specifiers from the extend information of the rendezvous * circuit that we've picked previously. */ rp_lspecs = smartlist_new(); - get_lspecs_from_extend_info(rp_ei, rp_lspecs); + get_lspecs_from_node(rp_node, rp_lspecs); /* Populate the introduce1 data object. */ memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); @@ -638,7 +662,7 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip, intro1_data->auth_pk = &ip->auth_key_cert->signed_key; intro1_data->enc_pk = &ip->enc_key; intro1_data->subcredential = subcredential; - intro1_data->onion_pk = &rp_ei->curve25519_onion_key; + intro1_data->onion_pk = node_get_curve25519_onion_key(rp_node); intro1_data->link_specifiers = rp_lspecs; } @@ -675,12 +699,12 @@ hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip) * * We currently relaunch connections to rendezvous points if: * - A rendezvous circuit timed out before connecting to RP. - * - The redenzvous circuit failed to connect to the RP. + * - The rendezvous circuit failed to connect to the RP. * * We avoid relaunching a connection to this rendezvous point if: - * - We have already tried MAX_REND_FAILURES times to connect to this RP. + * - We have already tried MAX_REND_FAILURES times to connect to this RP, * - We've been trying to connect to this RP for more than MAX_REND_TIMEOUT - * seconds + * seconds, or * - We've already retried this specific rendezvous circuit. */ void @@ -694,11 +718,11 @@ hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ) goto done; } - /* Flag the circuit that we are relaunching so to avoid to relaunch twice a + /* Flag the circuit that we are relaunching, to avoid to relaunch twice a * circuit to the same rendezvous point at the same time. */ circ->hs_service_side_rend_circ_has_been_relaunched = 1; - /* Legacy service don't have an hidden service ident. */ + /* Legacy services don't have a hidden service ident. */ if (circ->hs_ident) { retry_service_rendezvous_point(circ); } else { @@ -800,7 +824,11 @@ hs_circ_service_intro_has_opened(hs_service_t *service, /* Cleaning up the hidden service identifier and repurpose. */ hs_ident_circuit_free(circ->hs_ident); circ->hs_ident = NULL; - circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL); + if (circuit_should_use_vanguards(TO_CIRCUIT(circ)->purpose)) + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_HS_VANGUARDS); + else + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL); + /* Inform that this circuit just opened for this new purpose. */ circuit_has_opened(circ); /* This return value indicate to the caller that the IP object should be @@ -1082,11 +1110,21 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ, tor_assert(ip); tor_assert(subcredential); + /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data + * has been declared on the stack but not initialized. Here, we set it to 0. + */ + memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); + /* This takes various objects in order to populate the introduce1 data * object which is used to build the content of the cell. */ - setup_introduce1_data(ip, rend_circ->build_state->chosen_exit, - subcredential, &intro1_data); - /* If we didn't get any link specifiers, it's because our extend info was + const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state); + if (exit_node == NULL) { + log_info(LD_REND, "Unable to get rendezvous point for circuit %u. " + "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id); + goto done; + } + setup_introduce1_data(ip, exit_node, subcredential, &intro1_data); + /* If we didn't get any link specifiers, it's because our node was * bad. */ if (BUG(!intro1_data.link_specifiers) || !smartlist_len(intro1_data.link_specifiers)) { diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h index 63ff5e463c..2f5beaa168 100644 --- a/src/or/hs_circuit.h +++ b/src/or/hs_circuit.h @@ -70,7 +70,7 @@ create_rp_circuit_identifier(const hs_service_t *service, const curve25519_public_key_t *server_pk, const hs_ntor_rend_cell_keys_t *keys); -#endif +#endif /* defined(HS_CIRCUIT_PRIVATE) */ #endif /* !defined(TOR_HS_CIRCUIT_H) */ diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c index 97d6053e9b..112c8bdced 100644 --- a/src/or/hs_circuitmap.c +++ b/src/or/hs_circuitmap.c @@ -106,9 +106,12 @@ hs_token_new(hs_token_type_t type, size_t token_len, return hs_token; } +#define hs_token_free(val) \ + FREE_AND_NULL(hs_token_t, hs_token_free_, (val)) + /** Free memory allocated by this <b>hs_token</b>. */ static void -hs_token_free(hs_token_t *hs_token) +hs_token_free_(hs_token_t *hs_token) { if (!hs_token) { return; @@ -428,30 +431,54 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie) { origin_circuit_t *circ = NULL; - circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, - REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY); + circ = hs_circuitmap_get_established_rend_circ_client_side(cookie); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); + CIRCUIT_PURPOSE_C_ESTABLISH_REND); + return circ; +} + +/* Public function: Return client-side established rendezvous circuit with + * rendezvous <b>cookie</b>. It will look for circuits with the following + * purposes: + * + * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received + * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for + * INTRODUCE_ACK from intro point. + * + * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and + * introduce circuit acked. Waiting for RENDEZVOUS2 from service. + * + * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received + * RENDEZVOUS2 from service. + * + * Return NULL if no such circuit is found in the circuitmap. */ +origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie) +{ + origin_circuit_t *circ = NULL; + + circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, + REND_TOKEN_LEN, cookie, + CIRCUIT_PURPOSE_C_REND_READY); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_JOINED); + CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_ESTABLISH_REND); + CIRCUIT_PURPOSE_C_REND_JOINED); return circ; } diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h index 43b2947c17..9e653480b5 100644 --- a/src/or/hs_circuitmap.h +++ b/src/or/hs_circuitmap.h @@ -45,6 +45,8 @@ struct origin_circuit_t * hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie); struct origin_circuit_t * hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie); +struct origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie); void hs_circuitmap_register_intro_circ_v2_service_side( struct origin_circuit_t *circ, diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 551cf50554..d3978f22f0 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -21,6 +21,7 @@ #include "config.h" #include "directory.h" #include "hs_client.h" +#include "hs_control.h" #include "router.h" #include "routerset.h" #include "circuitlist.h" @@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk, safe_str_client(base64_blinded_pubkey), safe_str_client(routerstatus_describe(hsdir))); + /* Fire a REQUESTED event on the control port. */ + hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey, + hsdir); + /* Cleanup memory. */ memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey)); memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey)); @@ -711,7 +716,7 @@ desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip) smartlist_add(lspecs, lspec); } SMARTLIST_FOREACH_END(desc_lspec); - /* Explicitely put the direct connection option to 0 because this is client + /* Explicitly put the direct connection option to 0 because this is client * side and there is no such thing as a non anonymous client. */ ei = hs_get_extend_info_from_lspecs(lspecs, &ip->onion_key, 0); @@ -940,7 +945,8 @@ handle_introduce_ack_success(origin_circuit_t *intro_circ) /* Get the rendezvous circuit for this rendezvous cookie. */ uint8_t *rendezvous_cookie = intro_circ->hs_ident->rendezvous_cookie; - rend_circ = hs_circuitmap_get_rend_circ_client_side(rendezvous_cookie); + rend_circ = + hs_circuitmap_get_established_rend_circ_client_side(rendezvous_cookie); if (rend_circ == NULL) { log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping"); goto end; diff --git a/src/or/hs_common.c b/src/or/hs_common.c index e9d7323316..6d97c8775c 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -210,7 +210,7 @@ hs_check_service_private_dir(const char *username, const char *path, return 0; } -/* Default, minimum and maximum values for the maximum rendezvous failures +/* Default, minimum, and maximum values for the maximum rendezvous failures * consensus parameter. */ #define MAX_REND_FAILURES_DEFAULT 2 #define MAX_REND_FAILURES_MIN 1 @@ -347,7 +347,7 @@ rend_data_alloc(uint32_t version) /** Free all storage associated with <b>data</b> */ void -rend_data_free(rend_data_t *data) +rend_data_free_(rend_data_t *data) { if (!data) { return; diff --git a/src/or/hs_common.h b/src/or/hs_common.h index 7c5ea4792c..83ba1b8599 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -130,6 +130,17 @@ typedef enum { HS_AUTH_KEY_TYPE_ED25519 = 2, } hs_auth_key_type_t; +/* Return value when adding an ephemeral service through the ADD_ONION + * control port command. Both v2 and v3 share these. */ +typedef enum { + RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ + RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ + RSAE_ADDREXISTS = -3, /**< Onion address collision */ + RSAE_BADPRIVKEY = -2, /**< Invalid public key */ + RSAE_INTERNAL = -1, /**< Internal error */ + RSAE_OKAY = 0 /**< Service added as expected */ +} hs_service_add_ephemeral_status_t; + /* Represents the mapping from a virtual port of a rendezvous service to a * real port on some IP. */ typedef struct rend_service_port_config_t { @@ -185,7 +196,9 @@ void hs_build_blinded_keypair(const ed25519_keypair_t *kp, ed25519_keypair_t *kp_out); int hs_service_requires_uptime_circ(const smartlist_t *ports); -void rend_data_free(rend_data_t *data); +void rend_data_free_(rend_data_t *data); +#define rend_data_free(data) \ + FREE_AND_NULL(rend_data_t, rend_data_free_, (data)) rend_data_t *rend_data_dup(const rend_data_t *data); rend_data_t *rend_data_client_create(const char *onion_address, const char *desc_id, diff --git a/src/or/hs_config.c b/src/or/hs_config.c index fa5c1ab176..be223503a0 100644 --- a/src/or/hs_config.c +++ b/src/or/hs_config.c @@ -558,7 +558,7 @@ hs_config_service_all(const or_options_t *options, int validate_only) } /* In non validation mode, we'll stage those services we just successfully - * configured. Service ownership is transfered from the list to the global + * configured. Service ownership is transferred from the list to the global * state. If any service is invalid, it will be removed from the list and * freed. All versions are handled in that function. */ if (!validate_only) { diff --git a/src/or/hs_control.c b/src/or/hs_control.c new file mode 100644 index 0000000000..87b4e3fca8 --- /dev/null +++ b/src/or/hs_control.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.c + * \brief Contains control port event related code. + **/ + +#include "or.h" +#include "control.h" +#include "hs_common.h" +#include "hs_control.h" +#include "hs_descriptor.h" +#include "hs_service.h" +#include "nodelist.h" + +/* Send on the control port the "HS_DESC REQUESTED [...]" event. + * + * The onion_pk is the onion service public key, base64_blinded_pk is the + * base64 encoded blinded key for the service and hsdir_rs is the routerstatus + * object of the HSDir that this request is for. */ +void +hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const uint8_t *hsdir_index; + const node_t *hsdir_node; + + tor_assert(onion_pk); + tor_assert(base64_blinded_pk); + tor_assert(hsdir_rs); + + hs_build_address(onion_pk, HS_VERSION_THREE, onion_address); + + /* Get the node from the routerstatus object to get the HSDir index used for + * this request. We can't have a routerstatus entry without a node and we + * can't pick a node without an hsdir_index. */ + hsdir_node = node_get_by_id(hsdir_rs->identity_digest); + tor_assert(hsdir_node); + tor_assert(hsdir_node->hsdir_index); + /* This is a fetch event. */ + hsdir_index = hsdir_node->hsdir_index->fetch; + + /* Trigger the event. */ + control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH, + hsdir_rs->identity_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); + memwipe(onion_address, 0, sizeof(onion_address)); +} + +/* Send on the control port the "HS_DESC FAILED [...]" event. + * + * Using a directory connection identifier, the HSDir identity digest and a + * reason for the failure. None can be NULL. */ +void +hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + tor_assert(reason); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk, + hsdir_id_digest, reason); +} + +/* Send on the control port the "HS_DESC RECEIVED [...]" event. + * + * Using a directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk, + hsdir_id_digest); +} + +/* Send on the control port the "HS_DESC CREATED [...]" event. + * + * Using the onion address of the descriptor's service and the blinded public + * key of the descriptor as a descriptor ID. None can be NULL. */ +void +hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(blinded_pk); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + /* Version 3 doesn't use the replica number in its descriptor ID computation + * so we pass negative value so the control port subsystem can ignore it. */ + control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1); +} + +/* Send on the control port the "HS_DESC UPLOAD [...]" event. + * + * Using the onion address of the descriptor's service, the HSDir identity + * digest, the blinded public key of the descriptor as a descriptor ID and the + * HSDir index for this particular request. None can be NULL. */ +void +hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(hsdir_id_digest); + tor_assert(blinded_pk); + tor_assert(hsdir_index); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + control_event_hs_descriptor_upload(onion_address, hsdir_id_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); +} + +/* Send on the control port the "HS_DESC UPLOADED [...]" event. + * + * Using the directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address); +} + +/* Send on the control port the "HS_DESC_CONTENT [...]" event. + * + * Using the directory connection identifier, the HSDir identity digest and + * the body of the descriptor (as it was received from the directory). None + * can be NULL. */ +void +hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_content(onion_address, base64_blinded_pk, + hsdir_id_digest, body); +} + +/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for + * the given onion_address. The descriptor will be uploaded to each directory + * in hsdirs_rs. If NULL, the responsible directories for the current time + * period will be selected. + * + * Return -1 on if the descriptor plaintext section is not decodable. Else, 0 + * on success. */ +int +hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs) +{ + int ret = -1; + ed25519_public_key_t identity_pk; + hs_desc_plaintext_data_t plaintext; + smartlist_t *hsdirs = NULL; + + tor_assert(body); + tor_assert(onion_address); + + /* This can't fail because we require the caller to pass us a valid onion + * address that has passed hs_address_is_valid(). */ + if (BUG(hs_parse_address(onion_address, &identity_pk, NULL, NULL) < 0)) { + goto done; // LCOV_EXCL_LINE + } + + /* Only decode the plaintext part which is what the directory will do to + * validate before caching. */ + if (hs_desc_decode_plaintext(body, &plaintext) < 0) { + goto done; + } + + /* No HSDir(s) given, we'll compute what the current ones should be. */ + if (hsdirs_rs == NULL) { + hsdirs = smartlist_new(); + hs_get_responsible_hsdirs(&plaintext.blinded_pubkey, + hs_get_time_period_num(0), + 0, /* Always the current descriptor which uses + * the first hsdir index. */ + 0, /* It is for storing on a directory. */ + hsdirs); + hsdirs_rs = hsdirs; + } + + SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) { + hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk, + &plaintext.blinded_pubkey, rs); + } SMARTLIST_FOREACH_END(rs); + ret = 0; + + done: + /* We don't have ownership of the objects in this list. */ + smartlist_free(hsdirs); + return ret; +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h new file mode 100644 index 0000000000..95c46e655e --- /dev/null +++ b/src/or/hs_control.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.h + * \brief Header file containing control port event related code. + **/ + +#ifndef TOR_HS_CONTROL_H +#define TOR_HS_CONTROL_H + +#include "hs_ident.h" + +/* Event "HS_DESC REQUESTED [...]" */ +void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs); + +/* Event "HS_DESC FAILED [...]" */ +void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason); + +/* Event "HS_DESC RECEIVED [...]" */ +void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC CREATED [...]" */ +void hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk); + +/* Event "HS_DESC UPLOAD [...]" */ +void hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index); + +/* Event "HS_DESC UPLOADED [...]" */ +void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC_CONTENT [...]" */ +void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body); + +/* Command "HSPOST [...]" */ +int hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs); + +#endif /* !defined(TOR_HS_CONTROL_H) */ + diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 9683fca50f..7388807bc5 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -745,8 +745,8 @@ get_fake_auth_client_lines(void) /* Create the inner layer of the descriptor (which includes the intro points, * etc.). Return a newly-allocated string with the layer plaintext, or NULL if - * an error occured. It's the responsibility of the caller to free the returned - * string. */ + * an error occurred. It's the responsibility of the caller to free the + * returned string. */ static char * get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc) { @@ -802,8 +802,8 @@ get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc) /* Create the middle layer of the descriptor, which includes the client auth * data and the encrypted inner layer (provided as a base64 string at * <b>layer2_b64_ciphertext</b>). Return a newly-allocated string with the - * layer plaintext, or NULL if an error occured. It's the responsibility of the - * caller to free the returned string. */ + * layer plaintext, or NULL if an error occurred. It's the responsibility of + * the caller to free the returned string. */ static char * get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc, const char *layer2_b64_ciphertext) @@ -1609,7 +1609,7 @@ decode_intro_legacy_key(const directory_token_t *tok, /* The check on the expiration date is for the entire lifetime of a * certificate which is 24 hours. However, a descriptor has a maximum * lifetime of 12 hours meaning we have a 12h difference between the two - * which ultimately accomodate the clock skewed client. */ + * which ultimately accommodate the clock skewed client. */ if (rsa_ed25519_crosscert_check(ip->legacy.cert.encoded, ip->legacy.cert.len, ip->legacy.key, &desc->plaintext_data.signing_pubkey, @@ -2370,7 +2370,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc, /* Free the descriptor plaintext data object. */ void -hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) +hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc) { desc_plaintext_data_free_contents(desc); tor_free(desc); @@ -2378,7 +2378,7 @@ hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) /* Free the descriptor encrypted data object. */ void -hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) +hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc) { desc_encrypted_data_free_contents(desc); tor_free(desc); @@ -2386,7 +2386,7 @@ hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) /* Free the given descriptor object. */ void -hs_descriptor_free(hs_descriptor_t *desc) +hs_descriptor_free_(hs_descriptor_t *desc) { if (!desc) { return; @@ -2451,7 +2451,7 @@ hs_desc_intro_point_new(void) /* Free a descriptor intro point object. */ void -hs_desc_intro_point_free(hs_desc_intro_point_t *ip) +hs_desc_intro_point_free_(hs_desc_intro_point_t *ip) { if (ip == NULL) { return; @@ -2470,7 +2470,7 @@ hs_desc_intro_point_free(hs_desc_intro_point_t *ip) /* Free the given descriptor link specifier. */ void -hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls) +hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls) { if (ls == NULL) { return; diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 52bec8e244..09979410e1 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -208,11 +208,20 @@ hs_desc_is_supported_version(uint32_t version) /* Public API. */ -void hs_descriptor_free(hs_descriptor_t *desc); -void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc); -void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc); +void hs_descriptor_free_(hs_descriptor_t *desc); +#define hs_descriptor_free(desc) \ + FREE_AND_NULL(hs_descriptor_t, hs_descriptor_free_, (desc)) +void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc); +#define hs_desc_plaintext_data_free(desc) \ + FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc)) +void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc); +#define hs_desc_encrypted_data_free(desc) \ + FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc)) + +void hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls); +#define hs_desc_link_specifier_free(ls) \ + FREE_AND_NULL(hs_desc_link_specifier_t, hs_desc_link_specifier_free_, (ls)) -void hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls); hs_desc_link_specifier_t *hs_desc_link_specifier_new( const extend_info_t *info, uint8_t type); void hs_descriptor_clear_intro_points(hs_descriptor_t *desc); @@ -234,7 +243,9 @@ size_t hs_desc_obj_size(const hs_descriptor_t *data); size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data); hs_desc_intro_point_t *hs_desc_intro_point_new(void); -void hs_desc_intro_point_free(hs_desc_intro_point_t *ip); +void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip); +#define hs_desc_intro_point_free(ip) \ + FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip)) link_specifier_t *hs_desc_lspec_to_trunnel( const hs_desc_link_specifier_t *spec); diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c index b0e4e36a9b..0bce2f625b 100644 --- a/src/or/hs_ident.c +++ b/src/or/hs_ident.c @@ -25,7 +25,7 @@ hs_ident_circuit_new(const ed25519_public_key_t *identity_pk, /* Free the given circuit identifier. */ void -hs_ident_circuit_free(hs_ident_circuit_t *ident) +hs_ident_circuit_free_(hs_ident_circuit_t *ident) { if (ident == NULL) { return; @@ -56,7 +56,7 @@ hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) /* Free the given directory connection identifier. */ void -hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident) +hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident) { if (ident == NULL) { return; @@ -93,7 +93,7 @@ hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) /* Free the given edge connection identifier. */ void -hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident) +hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident) { if (ident == NULL) { return; diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h index 03150d25ea..91ec389aa4 100644 --- a/src/or/hs_ident.h +++ b/src/or/hs_ident.h @@ -119,12 +119,16 @@ typedef struct hs_ident_edge_conn_t { hs_ident_circuit_t *hs_ident_circuit_new( const ed25519_public_key_t *identity_pk, hs_ident_circuit_type_t circuit_type); -void hs_ident_circuit_free(hs_ident_circuit_t *ident); +void hs_ident_circuit_free_(hs_ident_circuit_t *ident); +#define hs_ident_circuit_free(id) \ + FREE_AND_NULL(hs_ident_circuit_t, hs_ident_circuit_free_, (id)) hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src); /* Directory connection identifier API. */ hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src); -void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident); +void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident); +#define hs_ident_dir_conn_free(id) \ + FREE_AND_NULL(hs_ident_dir_conn_t, hs_ident_dir_conn_free_, (id)) void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, const ed25519_public_key_t *blinded_pk, hs_ident_dir_conn_t *ident); @@ -132,7 +136,9 @@ void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, /* Edge connection identifier API. */ hs_ident_edge_conn_t *hs_ident_edge_conn_new( const ed25519_public_key_t *identity_pk); -void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident); +void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident); +#define hs_ident_edge_conn_free(id) \ + FREE_AND_NULL(hs_ident_edge_conn_t, hs_ident_edge_conn_free_, (id)) /* Validators */ int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident); diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c index 4a5202f614..8c6453e6fd 100644 --- a/src/or/hs_intropoint.c +++ b/src/or/hs_intropoint.c @@ -424,7 +424,7 @@ validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell) /* We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with * the payload in <b>request</b> of size <b>request_len</b>. Return 0 if - * everything went well, or -1 if an error occured. This function is in charge + * everything went well, or -1 if an error occurred. This function is in charge * of sending back an INTRODUCE_ACK cell and will close client_circ on error. */ STATIC int diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 45810c5c5f..8ae00df48e 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -30,11 +30,13 @@ #include "hs_circuit.h" #include "hs_common.h" #include "hs_config.h" +#include "hs_control.h" #include "hs_circuit.h" #include "hs_descriptor.h" #include "hs_ident.h" #include "hs_intropoint.h" #include "hs_service.h" +#include "hs_stats.h" /* Trunnel */ #include "ed25519_cert.h" @@ -70,7 +72,7 @@ static const char address_tld[] = "onion"; /* Staging list of service object. When configuring service, we add them to * this list considered a staging area and they will get added to our global - * map once the keys have been loaded. These two steps are seperated because + * map once the keys have been loaded. These two steps are separated because * loading keys requires that we are an actual running tor process. */ static smartlist_t *hs_service_staging_list; @@ -137,7 +139,7 @@ find_service(hs_service_ht *map, const ed25519_public_key_t *pk) /* Register the given service in the given map. If the service already exists * in the map, -1 is returned. On success, 0 is returned and the service - * ownership has been transfered to the global map. */ + * ownership has been transferred to the global map. */ STATIC int register_service(hs_service_ht *map, hs_service_t *service) { @@ -251,7 +253,7 @@ describe_intro_point(const hs_service_intro_point_t *ip) static int32_t get_intro_point_min_introduce2(void) { - /* The [0, 2147483647] range is quite large to accomodate anything we decide + /* The [0, 2147483647] range is quite large to accommodate anything we decide * in the future. */ return networkstatus_get_param(NULL, "hs_intro_min_introduce2", INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS, @@ -264,7 +266,7 @@ get_intro_point_min_introduce2(void) static int32_t get_intro_point_max_introduce2(void) { - /* The [0, 2147483647] range is quite large to accomodate anything we decide + /* The [0, 2147483647] range is quite large to accommodate anything we decide * in the future. */ return networkstatus_get_param(NULL, "hs_intro_max_introduce2", INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS, @@ -281,7 +283,7 @@ get_intro_point_min_lifetime(void) return MIN_INTRO_POINT_LIFETIME_TESTING; } - /* The [0, 2147483647] range is quite large to accomodate anything we decide + /* The [0, 2147483647] range is quite large to accommodate anything we decide * in the future. */ return networkstatus_get_param(NULL, "hs_intro_min_lifetime", INTRO_POINT_LIFETIME_MIN_SECONDS, @@ -298,7 +300,7 @@ get_intro_point_max_lifetime(void) return MAX_INTRO_POINT_LIFETIME_TESTING; } - /* The [0, 2147483647] range is quite large to accomodate anything we decide + /* The [0, 2147483647] range is quite large to accommodate anything we decide * in the future. */ return networkstatus_get_param(NULL, "hs_intro_max_lifetime", INTRO_POINT_LIFETIME_MAX_SECONDS, @@ -352,7 +354,7 @@ service_free_all(void) /* Free a given service intro point object. */ STATIC void -service_intro_point_free(hs_service_intro_point_t *ip) +service_intro_point_free_(hs_service_intro_point_t *ip) { if (!ip) { return; @@ -368,9 +370,9 @@ service_intro_point_free(hs_service_intro_point_t *ip) /* Helper: free an hs_service_intro_point_t object. This function is used by * digest256map_free() which requires a void * pointer. */ static void -service_intro_point_free_(void *obj) +service_intro_point_free_void(void *obj) { - service_intro_point_free(obj); + service_intro_point_free_(obj); } /* Return a newly allocated service intro point and fully initialized from the @@ -1027,7 +1029,7 @@ load_service_keys(hs_service_t *service) /* Free a given service descriptor object and all key material is wiped. */ STATIC void -service_descriptor_free(hs_service_descriptor_t *desc) +service_descriptor_free_(hs_service_descriptor_t *desc) { if (!desc) { return; @@ -1036,7 +1038,7 @@ service_descriptor_free(hs_service_descriptor_t *desc) memwipe(&desc->signing_kp, 0, sizeof(desc->signing_kp)); memwipe(&desc->blinded_kp, 0, sizeof(desc->blinded_kp)); /* Cleanup all intro points. */ - digest256map_free(desc->intro_points.map, service_intro_point_free_); + digest256map_free(desc->intro_points.map, service_intro_point_free_void); digestmap_free(desc->intro_points.failed_id, tor_free_); if (desc->previous_hsdirs) { SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s)); @@ -1270,7 +1272,7 @@ build_desc_intro_points(const hs_service_t *service, } DIGEST256MAP_FOREACH_END; } -/* Populate the descriptor encrypted section fomr the given service object. +/* Populate the descriptor encrypted section from the given service object. * This will generate a valid list of introduction points that can be used * after for circuit creation. Return 0 on success else -1 on error. */ static int @@ -1300,7 +1302,7 @@ build_service_desc_encrypted(const hs_service_t *service, return 0; } -/* Populare the descriptor plaintext section from the given service object. +/* Populate the descriptor plaintext section from the given service object. * The caller must make sure that the keys in the descriptors are valid that * is are non-zero. Return 0 on success else -1 on error. */ static int @@ -1431,6 +1433,9 @@ build_service_descriptor(hs_service_t *service, time_t now, /* Assign newly built descriptor to the next slot. */ *desc_out = desc; + /* Fire a CREATED control port event. */ + hs_control_desc_event_created(service->onion_address, + &desc->blinded_kp.pubkey); return; err: @@ -1572,7 +1577,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) /* Let's do a basic sanity check here so that we don't end up advertising the * ed25519 identity key of relays that don't actually support the link * protocol */ - if (!node_supports_ed25519_link_authentication(node)) { + if (!node_supports_ed25519_link_authentication(node, 0)) { tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity)); } else { /* Make sure we *do* have an ed key if we support the link authentication. @@ -2221,16 +2226,12 @@ static void upload_descriptor_to_hsdir(const hs_service_t *service, hs_service_descriptor_t *desc, const node_t *hsdir) { - char version_str[4] = {0}, *encoded_desc = NULL; - directory_request_t *dir_req; - hs_ident_dir_conn_t ident; + char *encoded_desc = NULL; tor_assert(service); tor_assert(desc); tor_assert(hsdir); - memset(&ident, 0, sizeof(ident)); - /* Let's avoid doing that if tor is configured to not publish. */ if (!get_options()->PublishHidServDescriptors) { log_info(LD_REND, "Service %s not publishing descriptor. " @@ -2246,29 +2247,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service, goto end; } - /* Setup the connection identifier. */ - hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey, - &ident); - - /* This is our resource when uploading which is used to construct the URL - * with the version number: "/tor/hs/<version>/publish". */ - tor_snprintf(version_str, sizeof(version_str), "%u", - service->config.version); - - /* Build the directory request for this HSDir. */ - dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); - directory_request_set_routerstatus(dir_req, hsdir->rs); - directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); - directory_request_set_resource(dir_req, version_str); - directory_request_set_payload(dir_req, encoded_desc, - strlen(encoded_desc)); - /* The ident object is copied over the directory connection object once - * the directory request is initiated. */ - directory_request_upload_set_hs_ident(dir_req, &ident); - - /* Initiate the directory request to the hsdir.*/ - directory_initiate_request(dir_req); - directory_request_free(dir_req); + /* Time to upload the descriptor to the directory. */ + hs_service_upload_desc_to_dir(encoded_desc, service->config.version, + &service->keys.identity_pk, + &desc->blinded_kp.pubkey, hsdir->rs); /* Add this node to previous_hsdirs list */ service_desc_note_upload(desc, hsdir); @@ -2285,9 +2267,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service, desc->desc->plaintext_data.revision_counter, safe_str_client(node_describe(hsdir)), safe_str_client(hex_str((const char *) idx, 32))); + + /* Fire a UPLOAD control port event. */ + hs_control_desc_event_upload(service->onion_address, hsdir->identity, + &desc->blinded_kp.pubkey, idx); } - /* XXX: Inform control port of the upload event (#20699). */ end: tor_free(encoded_desc); return; @@ -2922,6 +2907,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* Upload an encoded descriptor in encoded_desc of the given version. This + * descriptor is for the service identity_pk and blinded_pk used to setup the + * directory connection identifier. It is uploaded to the directory hsdir_rs + * routerstatus_t object. + * + * NOTE: This function does NOT check for PublishHidServDescriptors because it + * is only used by the control port command HSPOST outside of this subsystem. + * Inside this code, upload_descriptor_to_hsdir() should be used. */ +void +hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char version_str[4] = {0}; + directory_request_t *dir_req; + hs_ident_dir_conn_t ident; + + tor_assert(encoded_desc); + tor_assert(identity_pk); + tor_assert(blinded_pk); + tor_assert(hsdir_rs); + + /* Setup the connection identifier. */ + memset(&ident, 0, sizeof(ident)); + hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident); + + /* This is our resource when uploading which is used to construct the URL + * with the version number: "/tor/hs/<version>/publish". */ + tor_snprintf(version_str, sizeof(version_str), "%u", version); + + /* Build the directory request for this HSDir. */ + dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); + directory_request_set_routerstatus(dir_req, hsdir_rs); + directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); + directory_request_set_resource(dir_req, version_str); + directory_request_set_payload(dir_req, encoded_desc, + strlen(encoded_desc)); + /* The ident object is copied over the directory connection object once + * the directory request is initiated. */ + directory_request_upload_set_hs_ident(dir_req, &ident); + + /* Initiate the directory request to the hsdir.*/ + directory_initiate_request(dir_req); + directory_request_free(dir_req); +} + +/* Add the ephemeral service using the secret key sk and ports. Both max + * streams parameter will be set in the newly created service. + * + * Ownership of sk and ports is passed to this routine. Regardless of + * success/failure, callers should not touch these values after calling this + * routine, and may assume that correct cleanup has been done on failure. + * + * Return an appropriate hs_service_add_ephemeral_status_t. */ +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + hs_service_t *service = NULL; + + tor_assert(sk); + tor_assert(ports); + tor_assert(address_out); + + service = hs_service_new(get_options()); + + /* Setup the service configuration with specifics. A default service is + * HS_VERSION_TWO so explicitly set it. */ + service->config.version = HS_VERSION_THREE; + service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit; + service->config.max_streams_close_circuit = !!max_streams_close_circuit; + service->config.is_ephemeral = 1; + smartlist_free(service->config.ports); + service->config.ports = ports; + + /* Handle the keys. */ + memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk)); + if (ed25519_public_key_generate(&service->keys.identity_pk, + &service->keys.identity_sk) < 0) { + log_warn(LD_CONFIG, "Unable to generate ed25519 public key" + "for v3 service."); + ret = RSAE_BADPRIVKEY; + goto err; + } + + /* Make sure we have at least one port. */ + if (smartlist_len(service->config.ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified " + "for v3 service."); + ret = RSAE_BADVIRTPORT; + goto err; + } + + /* The only way the registration can fail is if the service public key + * already exists. */ + if (BUG(register_service(hs_service_map, service) < 0)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing v3 service."); + ret = RSAE_ADDREXISTS; + goto err; + } + + /* Last step is to build the onion address. */ + hs_build_address(&service->keys.identity_pk, + (uint8_t) service->config.version, + service->onion_address); + *address_out = tor_strdup(service->onion_address); + + log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s", + safe_str_client(service->onion_address)); + ret = RSAE_OKAY; + goto end; + + err: + hs_service_free(service); + + end: + memwipe(sk, 0, sizeof(ed25519_secret_key_t)); + tor_free(sk); + return ret; +} + +/* For the given onion address, delete the ephemeral service. Return 0 on + * success else -1 on error. */ +int +hs_service_del_ephemeral(const char *address) +{ + uint8_t version; + ed25519_public_key_t pk; + hs_service_t *service = NULL; + + tor_assert(address); + + if (hs_parse_address(address, &pk, NULL, &version) < 0) { + log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal."); + goto err; + } + + if (version != HS_VERSION_THREE) { + log_warn(LD_CONFIG, "Requested version of onion address for removal " + "is not supported."); + goto err; + } + + service = find_service(hs_service_map, &pk); + if (service == NULL) { + log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for " + "removal."); + goto err; + } + + if (!service->config.is_ephemeral) { + log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for " + "removal."); + goto err; + } + + /* Close circuits, remove from map and finally free. */ + close_service_circuits(service); + remove_service(hs_service_map, service); + hs_service_free(service); + + log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s", + safe_str_client(address)); + return 0; + + err: + return -1; +} + +/* Using the ed25519 public key pk, find a service for that key and return the + * current encoded descriptor as a newly allocated string or NULL if not + * found. This is used by the control port subsystem. */ +char * +hs_service_lookup_current_desc(const ed25519_public_key_t *pk) +{ + const hs_service_t *service; + + tor_assert(pk); + + service = find_service(hs_service_map, pk); + if (service && service->desc_current) { + char *encoded_desc = NULL; + /* No matter what is the result (which should never be a failure), return + * the encoded variable, if success it will contain the right thing else + * it will be NULL. */ + hs_desc_encode_descriptor(service->desc_current->desc, + &service->desc_current->signing_kp, + &encoded_desc); + return encoded_desc; + } + + return NULL; +} + /* Return the number of service we have configured and usable. */ unsigned int hs_service_get_num_services(void) @@ -2950,7 +3134,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ) get_objects_from_ident(circ->hs_ident, &service, &ip, &desc); if (service == NULL) { - log_warn(LD_REND, "Unable to find any hidden service associated " + /* This is possible if the circuits are closed and the service is + * immediately deleted. */ + log_info(LD_REND, "Unable to find any hidden service associated " "identity key %s on intro circuit %u.", ed25519_fmt(&circ->hs_ident->identity_pk), TO_CIRCUIT(circ)->n_circ_id); @@ -3032,7 +3218,7 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ, } /* Find a virtual port of that service mathcing the one in the connection if - * succesful, set the address in the connection. */ + * successful, set the address in the connection. */ if (hs_set_conn_addr_port(service->config.ports, conn) < 0) { log_info(LD_REND, "No virtual port mapping exists for port %d for " "hidden service %s.", @@ -3119,8 +3305,10 @@ hs_service_receive_introduce2(origin_circuit_t *circ, const uint8_t *payload, if (circ->hs_ident) { ret = service_handle_introduce2(circ, payload, payload_len); + hs_stats_note_introduce2_cell(1); } else { ret = rend_service_receive_introduction(circ, payload, payload_len); + hs_stats_note_introduce2_cell(0); } done: @@ -3269,7 +3457,7 @@ hs_service_new(const or_options_t *options) * also takes care of wiping service keys from memory. It is safe to pass a * NULL pointer. */ void -hs_service_free(hs_service_t *service) +hs_service_free_(hs_service_t *service) { if (service == NULL) { return; diff --git a/src/or/hs_service.h b/src/or/hs_service.h index ed1053d850..d163eeef28 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -249,7 +249,8 @@ void hs_service_free_all(void); /* Service new/free functions. */ hs_service_t *hs_service_new(const or_options_t *options); -void hs_service_free(hs_service_t *service); +void hs_service_free_(hs_service_t *service); +#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s)) unsigned int hs_service_get_num_services(void); void hs_service_stage_services(const smartlist_t *service_list); @@ -271,15 +272,33 @@ int hs_service_receive_introduce2(origin_circuit_t *circ, void hs_service_intro_circ_has_closed(origin_circuit_t *circ); +char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk); + +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out); +int hs_service_del_ephemeral(const char *address); + +/* Used outside of the HS subsystem by the control port command HSPOST. */ +void hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS - /* Useful getters for unit tests. */ STATIC unsigned int get_hs_service_map_size(void); STATIC int get_hs_service_staging_list_size(void); STATIC hs_service_ht *get_hs_service_map(void); STATIC hs_service_t *get_first_service(void); +STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( + const hs_service_t *service, + const hs_ident_circuit_t *ident); +#endif /* Service accessors. */ STATIC hs_service_t *find_service(hs_service_ht *map, @@ -290,7 +309,10 @@ STATIC int register_service(hs_service_ht *map, hs_service_t *service); STATIC hs_service_intro_point_t *service_intro_point_new( const extend_info_t *ei, unsigned int is_legacy); -STATIC void service_intro_point_free(hs_service_intro_point_t *ip); +STATIC void service_intro_point_free_(hs_service_intro_point_t *ip); +#define service_intro_point_free(ip) \ + FREE_AND_NULL(hs_service_intro_point_t, \ + service_intro_point_free_, (ip)) STATIC void service_intro_point_add(digest256map_t *map, hs_service_intro_point_t *ip); STATIC void service_intro_point_remove(const hs_service_t *service, @@ -298,9 +320,6 @@ STATIC void service_intro_point_remove(const hs_service_t *service, STATIC hs_service_intro_point_t *service_intro_point_find( const hs_service_t *service, const ed25519_public_key_t *auth_key); -STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( - const hs_service_t *service, - const hs_ident_circuit_t *ident); /* Service descriptor functions. */ STATIC hs_service_descriptor_t *service_descriptor_new(void); STATIC hs_service_descriptor_t *service_desc_find_by_intro( @@ -326,7 +345,10 @@ STATIC void run_upload_descriptor_event(time_t now); STATIC char * encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc); -STATIC void service_descriptor_free(hs_service_descriptor_t *desc); +STATIC void service_descriptor_free_(hs_service_descriptor_t *desc); +#define service_descriptor_free(d) \ + FREE_AND_NULL(hs_service_descriptor_t, \ + service_descriptor_free_, (d)) STATIC uint64_t check_state_line_for_service_rev_counter(const char *state_line, @@ -346,8 +368,6 @@ STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc, STATIC int service_desc_hsdirs_changed(const hs_service_t *service, const hs_service_descriptor_t *desc); -#endif /* defined(TOR_UNIT_TESTS) */ - #endif /* defined(HS_SERVICE_PRIVATE) */ #endif /* !defined(TOR_HS_SERVICE_H) */ diff --git a/src/or/hs_stats.c b/src/or/hs_stats.c new file mode 100644 index 0000000000..3e183a5bfc --- /dev/null +++ b/src/or/hs_stats.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2016-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_stats.c + * \brief Keeps stats about the activity of our hidden service. + **/ + +#include "or.h" +#include "hs_stats.h" +#include "hs_service.h" + +/** Number of v3 INTRODUCE2 cells received */ +static uint32_t n_introduce2_v3 = 0; +/** Number of v2 INTRODUCE2 cells received */ +static uint32_t n_introduce2_v2 = 0; +/** Number of attempts to make a circuit to a rendezvous point */ +static uint32_t n_rendezvous_launches = 0; + +/** Note that we received another INTRODUCE2 cell. */ +void +hs_stats_note_introduce2_cell(int is_hsv3) +{ + if (is_hsv3) { + n_introduce2_v3++; + } else { + n_introduce2_v2++; + } +} + +/** Return the number of v3 INTRODUCE2 cells we have received. */ +uint32_t +hs_stats_get_n_introduce2_v3_cells(void) +{ + return n_introduce2_v3; +} + +/** Return the number of v2 INTRODUCE2 cells we have received. */ +uint32_t +hs_stats_get_n_introduce2_v2_cells(void) +{ + return n_introduce2_v2; +} + +/** Note that we attempted to launch another circuit to a rendezvous point */ +void +hs_stats_note_service_rendezvous_launch(void) +{ + n_rendezvous_launches++; +} + +/** Return the number of rendezvous circuits we have attempted to launch */ +uint32_t +hs_stats_get_n_rendezvous_launches(void) +{ + return n_rendezvous_launches; +} + diff --git a/src/or/hs_stats.h b/src/or/hs_stats.h new file mode 100644 index 0000000000..a946ad75e5 --- /dev/null +++ b/src/or/hs_stats.h @@ -0,0 +1,14 @@ +/* Copyright (c) 2016-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_stats.h + * \brief Header file for hs_stats.c + **/ + +void hs_stats_note_introduce2_cell(int is_hsv3); +uint32_t hs_stats_get_n_introduce2_v3_cells(void); +uint32_t hs_stats_get_n_introduce2_v2_cells(void); +void hs_stats_note_service_rendezvous_launch(void); +uint32_t hs_stats_get_n_rendezvous_launches(void); + diff --git a/src/or/include.am b/src/or/include.am index abf62c2e8f..e0366a0cac 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -52,6 +52,7 @@ LIBTOR_A_SOURCES = \ src/or/geoip.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ + src/or/git_revision.c \ src/or/hibernate.c \ src/or/hs_cache.c \ src/or/hs_cell.c \ @@ -60,11 +61,13 @@ LIBTOR_A_SOURCES = \ src/or/hs_client.c \ src/or/hs_common.c \ src/or/hs_config.c \ + src/or/hs_control.c \ src/or/hs_descriptor.c \ src/or/hs_ident.c \ src/or/hs_intropoint.c \ src/or/hs_ntor.c \ src/or/hs_service.c \ + src/or/hs_stats.c \ src/or/keypin.c \ src/or/main.c \ src/or/microdesc.c \ @@ -79,6 +82,7 @@ LIBTOR_A_SOURCES = \ src/or/parsecommon.c \ src/or/periodic.c \ src/or/protover.c \ + src/or/protover_rust.c \ src/or/proto_cell.c \ src/or/proto_control0.c \ src/or/proto_ext_or.c \ @@ -105,6 +109,7 @@ LIBTOR_A_SOURCES = \ src/or/statefile.c \ src/or/status.c \ src/or/torcert.c \ + src/or/tor_api.c \ src/or/onion_ntor.c \ $(tor_platform_source) @@ -191,18 +196,21 @@ ORHEADERS = \ src/or/fp_pair.h \ src/or/geoip.h \ src/or/entrynodes.h \ + src/or/git_revision.h \ src/or/hibernate.h \ src/or/hs_cache.h \ src/or/hs_cell.h \ - src/or/hs_config.h \ src/or/hs_circuit.h \ src/or/hs_circuitmap.h \ src/or/hs_client.h \ src/or/hs_common.h \ + src/or/hs_config.h \ + src/or/hs_control.h \ src/or/hs_descriptor.h \ src/or/hs_ident.h \ src/or/hs_intropoint.h \ src/or/hs_ntor.h \ + src/or/hs_stats.h \ src/or/hs_service.h \ src/or/keypin.h \ src/or/main.h \ @@ -245,9 +253,13 @@ ORHEADERS = \ src/or/scheduler.h \ src/or/statefile.h \ src/or/status.h \ - src/or/torcert.h + src/or/torcert.h \ + src/or/tor_api_internal.h + +# This may someday want to be an installed file? +noinst_HEADERS += src/or/tor_api.h -noinst_HEADERS+= $(ORHEADERS) micro-revision.i +noinst_HEADERS += $(ORHEADERS) micro-revision.i micro-revision.i: FORCE $(AM_V_at)rm -f micro-revision.tmp; \ diff --git a/src/or/main.c b/src/or/main.c index 036ef1efd4..d1f0044095 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -60,7 +60,6 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" -#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -107,6 +106,8 @@ #include "shared_random.h" #include "statefile.h" #include "status.h" +#include "tor_api.h" +#include "tor_api_internal.h" #include "util_process.h" #include "ext_orport.h" #ifdef USE_DMALLOC @@ -129,6 +130,12 @@ void evdns_shutdown(int); +#ifdef HAVE_RUST +// helper function defined in Rust to output a log message indicating if tor is +// running with Rust enabled. See src/rust/tor_util +char *rust_welcome_string(void); +#endif + /********* PROTOTYPES **********/ static void dumpmemusage(int severity); @@ -141,6 +148,8 @@ static void connection_start_reading_from_linked_conn(connection_t *conn); static int connection_should_read_from_linked_conn(connection_t *conn); static int run_main_loop_until_done(void); static void process_signal(int sig); +static void shutdown_did_not_work_callback(evutil_socket_t fd, short event, + void *arg) ATTR_NORETURN; /********* START VARIABLES **********/ int global_read_bucket; /**< Max number of bytes I can read this second. */ @@ -171,6 +180,12 @@ static uint64_t stats_n_bytes_written = 0; time_t time_of_process_start = 0; /** How many seconds have we been running? */ long stats_n_seconds_working = 0; +/** How many times have we returned from the main loop successfully? */ +static uint64_t stats_n_main_loop_successes = 0; +/** How many times have we received an error from the main loop? */ +static uint64_t stats_n_main_loop_errors = 0; +/** How many times have we returned from the main loop with no events. */ +static uint64_t stats_n_main_loop_idle = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -193,6 +208,14 @@ static smartlist_t *active_linked_connection_lst = NULL; * <b>loop_once</b>. If so, there's no need to trigger a loopexit in order * to handle linked connections. */ static int called_loop_once = 0; +/** Flag: if true, it's time to shut down, so the main loop should exit as + * soon as possible. + */ +static int main_loop_should_exit = 0; +/** The return value that the main loop should yield when it exits, if + * main_loop_should_exit is true. + */ +static int main_loop_exit_value = 0; /** We set this to 1 when we've opened a circuit, so we can print a log * entry to inform the user that Tor is working. We set it to 0 when @@ -319,7 +342,7 @@ connection_remove(connection_t *conn) smartlist_len(connection_array)); if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { - log_info(LD_NET, "Closing SOCKS SocksSocket connection"); + log_info(LD_NET, "Closing SOCKS Unix socket connection"); } control_event_conn_bandwidth(conn); @@ -477,6 +500,57 @@ connection_is_reading(connection_t *conn) (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); } +/** Reset our main loop counters. */ +void +reset_main_loop_counters(void) +{ + stats_n_main_loop_successes = 0; + stats_n_main_loop_errors = 0; + stats_n_main_loop_idle = 0; +} + +/** Increment the main loop success counter. */ +static void +increment_main_loop_success_count(void) +{ + ++stats_n_main_loop_successes; +} + +/** Get the main loop success counter. */ +uint64_t +get_main_loop_success_count(void) +{ + return stats_n_main_loop_successes; +} + +/** Increment the main loop error counter. */ +static void +increment_main_loop_error_count(void) +{ + ++stats_n_main_loop_errors; +} + +/** Get the main loop error counter. */ +uint64_t +get_main_loop_error_count(void) +{ + return stats_n_main_loop_errors; +} + +/** Increment the main loop idle counter. */ +static void +increment_main_loop_idle_count(void) +{ + ++stats_n_main_loop_idle; +} + +/** Get the main loop idle counter. */ +uint64_t +get_main_loop_idle_count(void) +{ + return stats_n_main_loop_idle; +} + /** Check whether <b>conn</b> is correct in having (or not having) a * read/write event (passed in <b>ev</b>). On success, return 0. On failure, * log a warning and return -1. */ @@ -638,9 +712,10 @@ connection_should_read_from_linked_conn(connection_t *conn) /** If we called event_base_loop() and told it to never stop until it * runs out of events, now we've changed our mind: tell it we want it to - * finish. */ + * exit once the current round of callbacks is done, so that we can + * run external code, and then return to the main loop. */ void -tell_event_loop_to_finish(void) +tell_event_loop_to_run_external_code(void) { if (!called_loop_once) { struct timeval tv = { 0, 0 }; @@ -649,6 +724,72 @@ tell_event_loop_to_finish(void) } } +/** Event to run 'shutdown did not work callback'. */ +static struct event *shutdown_did_not_work_event = NULL; + +/** Failsafe measure that should never actually be necessary: If + * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the + * event loop, then this callback will kill Tor with an assertion failure + * seconds later + */ +static void +shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg) +{ + // LCOV_EXCL_START + (void) fd; + (void) event; + (void) arg; + tor_assert_unreached(); + // LCOV_EXCL_STOP +} + +#ifdef ENABLE_RESTART_DEBUGGING +static struct event *tor_shutdown_event_loop_for_restart_event = NULL; +static void +tor_shutdown_event_loop_for_restart_cb( + evutil_socket_t fd, short event, void *arg) +{ + (void)fd; + (void)event; + (void)arg; + tor_event_free(tor_shutdown_event_loop_for_restart_event); + tor_shutdown_event_loop_and_exit(0); +} +#endif + +/** + * After finishing the current callback (if any), shut down the main loop, + * clean up the process, and exit with <b>exitcode</b>. + */ +void +tor_shutdown_event_loop_and_exit(int exitcode) +{ + if (main_loop_should_exit) + return; /* Ignore multiple calls to this function. */ + + main_loop_should_exit = 1; + main_loop_exit_value = exitcode; + + /* Die with an assertion failure in ten seconds, if for some reason we don't + * exit normally. */ + /* XXXX We should consider this code if it's never used. */ + struct timeval ten_seconds = { 10, 0 }; + shutdown_did_not_work_event = tor_evtimer_new( + tor_libevent_get_base(), + shutdown_did_not_work_callback, NULL); + event_add(shutdown_did_not_work_event, &ten_seconds); + + /* Unlike loopexit, loopbreak prevents other callbacks from running. */ + tor_event_base_loopbreak(tor_libevent_get_base()); +} + +/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */ +int +tor_event_loop_shutdown_is_pending(void) +{ + return main_loop_should_exit; +} + /** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from * its linked connection, if it is not doing so already. Called by * connection_start_reading and connection_start_writing as appropriate. */ @@ -664,7 +805,7 @@ connection_start_reading_from_linked_conn(connection_t *conn) /* make sure that the event_base_loop() function exits at * the end of its run through the current connections, so we can * activate read events for linked connections. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } else { tor_assert(smartlist_contains(active_linked_connection_lst, conn)); } @@ -1056,7 +1197,7 @@ run_connection_housekeeping(int i, time_t now) if (!connection_speaks_cells(conn)) return; /* we're all done here, the rest is just for OR conns */ - /* If we haven't written to an OR connection for a while, then either nuke + /* If we haven't flushed to an OR connection for a while, then either nuke the connection or send a keepalive, depending. */ or_conn = TO_OR_CONN(conn); @@ -1276,6 +1417,9 @@ find_periodic_event(const char *name) return NULL; } +/** Event to run initialize_periodic_events_cb */ +static struct event *initialize_periodic_events_event = NULL; + /** Helper, run one second after setup: * Initializes all members of periodic_events and starts them running. * @@ -1287,6 +1431,7 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) (void) fd; (void) events; (void) data; + tor_event_free(initialize_periodic_events_event); int i; for (i = 0; periodic_events[i].name; ++i) { periodic_event_launch(&periodic_events[i]); @@ -1315,9 +1460,10 @@ initialize_periodic_events(void) NAMED_CALLBACK(check_dns_honesty); struct timeval one_second = { 1, 0 }; - event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT, - initialize_periodic_events_cb, NULL, - &one_second); + initialize_periodic_events_event = tor_evtimer_new( + tor_libevent_get_base(), + initialize_periodic_events_cb, NULL); + event_add(initialize_periodic_events_event, &one_second); } STATIC void @@ -1406,7 +1552,9 @@ run_scheduled_events(time_t now) /* Maybe enough time elapsed for us to reconsider a circuit. */ circuit_upgrade_circuits_from_guard_wait(); - if (options->UseBridges && !options->DisableNetwork) { + if (options->UseBridges && !net_is_disabled()) { + /* Note: this check uses net_is_disabled(), not should_delay_dir_fetches() + * -- the latter is only for fetching consensus-derived directory info. */ fetch_bridge_descriptors(options, now); } @@ -1512,7 +1660,7 @@ rotate_onion_key_callback(time_t now, const or_options_t *options) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode() && !options->DisableNetwork) + if (advertised_server_mode() && !net_is_disabled()) router_upload_dir_desc_to_dirservers(0); return ONION_KEY_CONSENSUS_CHECK_INTERVAL; } @@ -1555,8 +1703,7 @@ check_ed_keys_callback(time_t now, const or_options_t *options) if (new_signing_key < 0 || generate_ed_link_cert(options, now, new_signing_key > 0)) { log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } return 30; @@ -1878,9 +2025,11 @@ check_descriptor_callback(time_t now, const or_options_t *options) * address has changed. */ #define CHECK_DESCRIPTOR_INTERVAL (60) + (void)options; + /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (!options->DisableNetwork) { + if (!net_is_disabled()) { check_descriptor_bandwidth_changed(now); check_descriptor_ipaddress_changed(now); mark_my_descriptor_dirty_if_too_old(now); @@ -1912,7 +2061,7 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options) * 20 minutes of our uptime. */ if (server_mode(options) && (have_completed_a_circuit() || !any_predicted_circuits(now)) && - !we_are_hibernating()) { + !net_is_disabled()) { if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { consider_testing_reachability(1, dirport_reachability_count==0); if (++dirport_reachability_count > 5) @@ -2367,10 +2516,18 @@ do_hup(void) /* first, reload config variables, in case they've changed */ if (options->ReloadTorrcOnSIGHUP) { /* no need to provide argc/v, they've been cached in init_from_config */ - if (options_init_from_torrc(0, NULL) < 0) { + int init_rv = options_init_from_torrc(0, NULL); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above. " "For usage, try -h."); return -1; + } else if (BUG(init_rv > 0)) { + // LCOV_EXCL_START + /* This should be impossible: the only "return 1" cases in + * options_init_from_torrc are ones caused by command-line arguments; + * but they can't change while Tor is running. */ + return -1; + // LCOV_EXCL_STOP } options = get_options(); /* they have changed now */ /* Logs are only truncated the first time they are opened, but were @@ -2406,7 +2563,7 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - if (!options->DisableNetwork) + if (!net_is_disabled()) update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to @@ -2454,7 +2611,7 @@ do_main_loop(void) } } - handle_signals(1); + handle_signals(); monotime_init(); timers_initialize(); @@ -2596,6 +2753,34 @@ do_main_loop(void) } #endif /* defined(HAVE_SYSTEMD) */ + main_loop_should_exit = 0; + main_loop_exit_value = 0; + +#ifdef ENABLE_RESTART_DEBUGGING + { + static int first_time = 1; + + if (first_time && getenv("TOR_DEBUG_RESTART")) { + first_time = 0; + const char *sec_str = getenv("TOR_DEBUG_RESTART_AFTER_SECONDS"); + long sec; + int sec_ok=0; + if (sec_str && + (sec = tor_parse_long(sec_str, 10, 0, INT_MAX, &sec_ok, NULL)) && + sec_ok) { + /* Okay, we parsed the seconds. */ + } else { + sec = 5; + } + struct timeval restart_after = { (time_t) sec, 0 }; + tor_shutdown_event_loop_for_restart_event = + tor_evtimer_new(tor_libevent_get_base(), + tor_shutdown_event_loop_for_restart_cb, NULL); + event_add(tor_shutdown_event_loop_for_restart_event, &restart_after); + } + } +#endif + return run_main_loop_until_done(); } @@ -2611,6 +2796,9 @@ run_main_loop_once(void) if (nt_service_is_stopping()) return 0; + if (main_loop_should_exit) + return 0; + #ifndef _WIN32 /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; @@ -2620,7 +2808,14 @@ run_main_loop_once(void) * so that libevent knows to run their callbacks. */ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, event_active(conn->read_event, EV_READ, 1)); - called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + + if (get_options()->MainloopStats) { + /* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we + * are collecting main loop statistics. */ + called_loop_once = 1; + } else { + called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + } /* Make sure we know (about) what time it is. */ update_approx_time(time(NULL)); @@ -2632,6 +2827,21 @@ run_main_loop_once(void) loop_result = event_base_loop(tor_libevent_get_base(), called_loop_once ? EVLOOP_ONCE : 0); + if (get_options()->MainloopStats) { + /* Update our main loop counters. */ + if (loop_result == 0) { + // The call was successful. + increment_main_loop_success_count(); + } else if (loop_result == -1) { + // The call was erroneous. + increment_main_loop_error_count(); + } else if (loop_result == 1) { + // The call didn't have any active or pending events + // to handle. + increment_main_loop_idle_count(); + } + } + /* Oh, the loop failed. That might be an error that we need to * catch, but more likely, it's just an interrupted poll() call or something, * and we should try again. */ @@ -2657,6 +2867,9 @@ run_main_loop_once(void) } } + if (main_loop_should_exit) + return 0; + /* And here is where we put callbacks that happen "every time the event loop * runs." They must be very fast, or else the whole Tor process will get * slowed down. @@ -2685,7 +2898,11 @@ run_main_loop_until_done(void) do { loop_result = run_main_loop_once(); } while (loop_result == 1); - return loop_result; + + if (main_loop_should_exit) + return main_loop_exit_value; + else + return loop_result; } /** Libevent callback: invoked when we get a signal. @@ -2709,14 +2926,13 @@ process_signal(int sig) { case SIGTERM: log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); break; case SIGINT: if (!server_mode(get_options())) { /* do it now */ log_notice(LD_GENERAL,"Interrupt: exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "STOPPING=1"); @@ -2745,8 +2961,8 @@ process_signal(int sig) #endif if (do_hup() < 0) { log_warn(LD_CONFIG,"Restart failed (config error?). Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "READY=1"); @@ -2928,9 +3144,15 @@ exit_function(void) #else #define UNIX_ONLY 1 #endif + static struct { + /** A numeric code for this signal. Must match the signal value if + * try_to_register is true. */ int signal_value; + /** True if we should try to register this signal with libevent and catch + * corresponding posix signals. False otherwise. */ int try_to_register; + /** Pointer to hold the event object constructed for this signal. */ struct event *signal_event; } signal_handlers[] = { #ifdef SIGINT @@ -2964,50 +3186,40 @@ static struct { { -1, -1, NULL } }; -/** Set up the signal handlers for either parent or child process */ +/** Set up the signal handler events for this process, and register them + * with libevent if appropriate. */ void -handle_signals(int is_parent) +handle_signals(void) { int i; - if (is_parent) { - for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { - if (signal_handlers[i].try_to_register) { - signal_handlers[i].signal_event = - tor_evsignal_new(tor_libevent_get_base(), - signal_handlers[i].signal_value, - signal_callback, - &signal_handlers[i].signal_value); - if (event_add(signal_handlers[i].signal_event, NULL)) - log_warn(LD_BUG, "Error from libevent when adding " - "event for signal %d", - signal_handlers[i].signal_value); - } else { - signal_handlers[i].signal_event = - tor_event_new(tor_libevent_get_base(), -1, - EV_SIGNAL, signal_callback, - &signal_handlers[i].signal_value); - } + const int enabled = !get_options()->DisableSignalHandlers; + + for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { + /* Signal handlers are only registered with libevent if they need to catch + * real POSIX signals. We construct these signal handler events in either + * case, though, so that controllers can activate them with the SIGNAL + * command. + */ + if (enabled && signal_handlers[i].try_to_register) { + signal_handlers[i].signal_event = + tor_evsignal_new(tor_libevent_get_base(), + signal_handlers[i].signal_value, + signal_callback, + &signal_handlers[i].signal_value); + if (event_add(signal_handlers[i].signal_event, NULL)) + log_warn(LD_BUG, "Error from libevent when adding " + "event for signal %d", + signal_handlers[i].signal_value); + } else { + signal_handlers[i].signal_event = + tor_event_new(tor_libevent_get_base(), -1, + EV_SIGNAL, signal_callback, + &signal_handlers[i].signal_value); } - } else { -#ifndef _WIN32 - struct sigaction action; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); - action.sa_handler = SIG_IGN; - sigaction(SIGINT, &action, NULL); - sigaction(SIGTERM, &action, NULL); - sigaction(SIGPIPE, &action, NULL); - sigaction(SIGUSR1, &action, NULL); - sigaction(SIGUSR2, &action, NULL); - sigaction(SIGHUP, &action, NULL); -#ifdef SIGXFSZ - sigaction(SIGXFSZ, &action, NULL); -#endif -#endif /* !defined(_WIN32) */ } } -/* Make sure the signal handler for signal_num will be called. */ +/* Cause the signal handler for signal_num to be called in the event loop. */ void activate_signal(int signal_num) { @@ -3020,7 +3232,8 @@ activate_signal(int signal_num) } } -/** Main entry point for the Tor command-line client. +/** Main entry point for the Tor command-line client. Return 0 on "success", + * negative on "failure", and positive on "success and exit". */ int tor_init(int argc, char *argv[]) @@ -3112,14 +3325,13 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } - { - rust_str_t rust_str = rust_welcome_string(); - const char *s = rust_str_get(rust_str); - if (strlen(s) > 0) { - log_notice(LD_GENERAL, "%s", s); - } - rust_str_free(rust_str); +#ifdef HAVE_RUST + char *rust_str = rust_welcome_string(); + if (rust_str != NULL && strlen(rust_str) > 0) { + log_notice(LD_GENERAL, "%s", rust_str); } + tor_free(rust_str); +#endif /* defined(HAVE_RUST) */ if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); @@ -3127,9 +3339,14 @@ tor_init(int argc, char *argv[]) } atexit(exit_function); - if (options_init_from_torrc(argc,argv) < 0) { + int init_rv = options_init_from_torrc(argc,argv); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above."); return -1; + } else if (init_rv > 0) { + // We succeeded, and should exit anyway -- probably the user just said + // "--version" or something like that. + return 1; } /* The options are now initialised */ @@ -3159,7 +3376,7 @@ tor_init(int argc, char *argv[]) log_warn(LD_NET, "Problem initializing libevent RNG."); } - /* Scan/clean unparseable descroptors; after reading config */ + /* Scan/clean unparseable descriptors; after reading config */ routerparse_init(); return 0; @@ -3181,7 +3398,7 @@ try_locking(const or_options_t *options, int err_if_locked) if (lockfile) return 0; else { - char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL); + char *fname = options_get_datadir_fname(options, "lock"); int already_locked = 0; tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); tor_free(fname); @@ -3199,7 +3416,7 @@ try_locking(const or_options_t *options, int err_if_locked) r = try_locking(options, 0); if (r<0) { log_err(LD_GENERAL, "No, it's still there. Exiting."); - exit(1); + return -1; } return r; } @@ -3292,10 +3509,13 @@ tor_free_all(int postfork) periodic_timer_free(second_timer); teardown_periodic_events(); periodic_timer_free(refill_timer); + tor_event_free(shutdown_did_not_work_event); + tor_event_free(initialize_periodic_events_event); if (!postfork) { release_lockfile(); } + tor_libevent_free_all(); /* Stuff in util.c and address.c*/ if (!postfork) { escaped(NULL); @@ -3305,6 +3525,19 @@ tor_free_all(int postfork) } } +/** + * Remove the specified file, and log a warning if the operation fails for + * any reason other than the file not existing. Ignores NULL filenames. + */ +void +tor_remove_file(const char *filename) +{ + if (filename && tor_unlink(filename) != 0 && errno != ENOENT) { + log_warn(LD_FS, "Couldn't unlink %s: %s", + filename, strerror(errno)); + } +} + /** Do whatever cleanup is necessary before shutting Tor down. */ void tor_cleanup(void) @@ -3314,18 +3547,20 @@ tor_cleanup(void) time_t now = time(NULL); /* Remove our pid file. We don't care if there was an error when we * unlink, nothing we could do about it anyways. */ - if (options->PidFile) { - if (unlink(options->PidFile) != 0) { - log_warn(LD_FS, "Couldn't unlink pid file %s: %s", - options->PidFile, strerror(errno)); - } + tor_remove_file(options->PidFile); + /* Remove control port file */ + tor_remove_file(options->ControlPortWriteToFile); + /* Remove cookie authentication file */ + { + char *cookie_fname = get_controller_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } - if (options->ControlPortWriteToFile) { - if (unlink(options->ControlPortWriteToFile) != 0) { - log_warn(LD_FS, "Couldn't unlink control port file %s: %s", - options->ControlPortWriteToFile, - strerror(errno)); - } + /* Remove Extended ORPort cookie authentication file */ + { + char *cookie_fname = get_ext_or_auth_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } if (accounting_is_enabled(options)) accounting_record_bandwidth_usage(now, get_or_state()); @@ -3458,7 +3693,7 @@ sandbox_init_filter(void) int i; sandbox_cfg_allow_openat_filename(&cfg, - get_datadir_fname("cached-status")); + get_cachedir_fname("cached-status")); #define OPEN(name) \ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name)) @@ -3479,21 +3714,38 @@ sandbox_init_filter(void) OPEN_DATADIR2(name, name2 suffix); \ } while (0) +#define OPEN_KEY_DIRECTORY() \ + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory)) +#define OPEN_CACHEDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name)) +#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \ + OPEN_CACHEDIR(name); \ + OPEN_CACHEDIR(name suffix); \ + } while (0) +#define OPEN_KEYDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name)) +#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \ + OPEN_KEYDIR(name); \ + OPEN_KEYDIR(name suffix); \ + } while (0) + OPEN(options->DataDirectory); - OPEN_DATADIR("keys"); - OPEN_DATADIR_SUFFIX("cached-certs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs.new", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors.new", ".tmp"); - OPEN_DATADIR("cached-descriptors.tmp.tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp"); - OPEN_DATADIR("cached-extrainfo.tmp.tmp"); + OPEN_KEY_DIRECTORY(); + + OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + OPEN_CACHEDIR("cached-descriptors.tmp.tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + OPEN_CACHEDIR("cached-extrainfo.tmp.tmp"); + OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("sr-state", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); @@ -3523,6 +3775,10 @@ sandbox_init_filter(void) } } + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, { + OPEN(f); + }); + #define RENAME_SUFFIX(name, suffix) \ sandbox_cfg_allow_rename(&cfg, \ get_datadir_fname(name suffix), \ @@ -3533,20 +3789,31 @@ sandbox_init_filter(void) get_datadir_fname2(prefix, name suffix), \ get_datadir_fname2(prefix, name)) - RENAME_SUFFIX("cached-certs", ".tmp"); - RENAME_SUFFIX("cached-consensus", ".tmp"); - RENAME_SUFFIX("unverified-consensus", ".tmp"); - RENAME_SUFFIX("unverified-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".new"); - RENAME_SUFFIX("cached-microdescs.new", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".new"); - RENAME_SUFFIX("cached-descriptors.new", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".new"); - RENAME_SUFFIX("cached-extrainfo.new", ".tmp"); +#define RENAME_CACHEDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_cachedir_fname(name suffix), \ + get_cachedir_fname(name)) + +#define RENAME_KEYDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_keydir_fname(name suffix), \ + get_keydir_fname(name)) + + RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + RENAME_SUFFIX("state", ".tmp"); RENAME_SUFFIX("sr-state", ".tmp"); RENAME_SUFFIX("unparseable-desc", ".tmp"); @@ -3558,14 +3825,21 @@ sandbox_init_filter(void) #define STAT_DATADIR(name) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) +#define STAT_CACHEDIR(name) \ + sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name)) + #define STAT_DATADIR2(name, name2) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2))) +#define STAT_KEY_DIRECTORY() \ + sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory)) + STAT_DATADIR(NULL); STAT_DATADIR("lock"); STAT_DATADIR("state"); STAT_DATADIR("router-stability"); - STAT_DATADIR("cached-extrainfo.new"); + + STAT_CACHEDIR("cached-extrainfo.new"); { smartlist_t *files = smartlist_new(); @@ -3630,22 +3904,20 @@ sandbox_init_filter(void) // orport if (server_mode(get_options())) { - OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key_ntor", ".tmp"); - OPEN_DATADIR2("keys", "secret_id_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key_ntor.old"); - - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + OPEN_KEYDIR("secret_id_key.old"); + OPEN_KEYDIR("secret_onion_key.old"); + OPEN_KEYDIR("secret_onion_key_ntor.old"); + + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); @@ -3664,11 +3936,13 @@ sandbox_init_filter(void) OPEN("/etc/resolv.conf"); RENAME_SUFFIX("fingerprint", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key_ntor", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key.old", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + + RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp"); + RENAME_SUFFIX2("stats", "bridge-stats", ".tmp"); RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp"); RENAME_SUFFIX2("stats", "entry-stats", ".tmp"); @@ -3679,20 +3953,20 @@ sandbox_init_filter(void) RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key"), - get_datadir_fname2("keys", "secret_onion_key.old")); + get_keydir_fname("secret_onion_key"), + get_keydir_fname("secret_onion_key.old")); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key_ntor"), - get_datadir_fname2("keys", "secret_onion_key_ntor.old")); + get_keydir_fname("secret_onion_key_ntor"), + get_keydir_fname("secret_onion_key_ntor.old")); - STAT_DATADIR("keys"); + STAT_KEY_DIRECTORY(); OPEN_DATADIR("stats"); STAT_DATADIR("stats"); STAT_DATADIR2("stats", "dirreq-stats"); @@ -3705,14 +3979,16 @@ sandbox_init_filter(void) return cfg; } -/** Main entry point for the Tor process. Called from main(). */ -/* This function is distinct from main() only so we can link main.c into - * the unittest binary without conflicting with the unittests' main. */ +/* Main entry point for the Tor process. Called from tor_main(), and by + * anybody embedding Tor. */ int -tor_main(int argc, char *argv[]) +tor_run_main(const tor_main_configuration_t *tor_cfg) { int result = 0; + int argc = tor_cfg->argc; + char **argv = tor_cfg->argv; + #ifdef _WIN32 #ifndef HeapEnableTerminationOnCorruption #define HeapEnableTerminationOnCorruption 1 @@ -3735,6 +4011,7 @@ tor_main(int argc, char *argv[]) #endif /* defined(_WIN32) */ configure_backtrace_handler(get_version()); + init_protocol_warning_severity_level(); update_approx_time(time(NULL)); tor_threads_init(); @@ -3756,8 +4033,13 @@ tor_main(int argc, char *argv[]) if (done) return result; } #endif /* defined(NT_SERVICE) */ - if (tor_init(argc, argv)<0) - return -1; + { + int init_rv = tor_init(argc, argv); + if (init_rv < 0) + return -1; + else if (init_rv > 0) + return 0; + } if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) { sandbox_cfg_t* cfg = sandbox_init_filter(); diff --git a/src/or/main.h b/src/or/main.h index 132ab12bbb..c49d216f4e 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -45,7 +45,9 @@ int connection_is_writing(connection_t *conn); MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); MOCK_DECL(void,connection_start_writing,(connection_t *conn)); -void tell_event_loop_to_finish(void); +void tell_event_loop_to_run_external_code(void); +void tor_shutdown_event_loop_and_exit(int exitcode); +int tor_event_loop_shutdown_is_pending(void); void connection_stop_reading_from_linked_conn(connection_t *conn); @@ -64,21 +66,26 @@ MOCK_DECL(long,get_uptime,(void)); unsigned get_signewnym_epoch(void); -void handle_signals(int is_parent); +void handle_signals(void); void activate_signal(int signal_num); int try_locking(const or_options_t *options, int err_if_locked); int have_lockfile(void); void release_lockfile(void); +void tor_remove_file(const char *filename); + void tor_cleanup(void); void tor_free_all(int postfork); -int tor_main(int argc, char *argv[]); - int do_main_loop(void); int tor_init(int argc, char **argv); +void reset_main_loop_counters(void); +uint64_t get_main_loop_success_count(void); +uint64_t get_main_loop_error_count(void); +uint64_t get_main_loop_idle_count(void); + extern time_t time_of_process_start; extern long stats_n_seconds_working; extern int quiet_level; diff --git a/src/or/microdesc.c b/src/or/microdesc.c index fe327c6c82..d8a4660af1 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -238,8 +238,8 @@ get_microdesc_cache_noload(void) if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache)); HT_INIT(microdesc_map, &cache->map); - cache->cache_fname = get_datadir_fname("cached-microdescs"); - cache->journal_fname = get_datadir_fname("cached-microdescs.new"); + cache->cache_fname = get_cachedir_fname("cached-microdescs"); + cache->journal_fname = get_cachedir_fname("cached-microdescs.new"); the_microdesc_cache = cache; } return the_microdesc_cache; diff --git a/src/or/microdesc.h b/src/or/microdesc.h index 1be12156a4..83a90bd8ff 100644 --- a/src/or/microdesc.h +++ b/src/or/microdesc.h @@ -38,8 +38,10 @@ smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns, digest256map_t *skip); void microdesc_free_(microdesc_t *md, const char *fname, int line); -#define microdesc_free(md) \ - microdesc_free_((md), __FILE__, __LINE__) +#define microdesc_free(md) do { \ + microdesc_free_((md), __FILE__, __LINE__); \ + (md) = NULL; \ + } while (0) void microdesc_free_all(void); void update_microdesc_downloads(time_t now); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index dd82f66816..31ecb20985 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -53,6 +53,7 @@ #include "dirvote.h" #include "dos.h" #include "entrynodes.h" +#include "hibernate.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -197,7 +198,7 @@ networkstatus_read_cached_consensus_impl(int flav, tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname); } - char *filename = get_datadir_fname(buf); + char *filename = get_cachedir_fname(buf); char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); return result; @@ -255,7 +256,7 @@ router_reload_consensus_networkstatus(void) /** Free all storage held by the vote_routerstatus object <b>rs</b>. */ void -vote_routerstatus_free(vote_routerstatus_t *rs) +vote_routerstatus_free_(vote_routerstatus_t *rs) { vote_microdesc_hash_t *h, *next; if (!rs) @@ -273,7 +274,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs) /** Free all storage held by the routerstatus object <b>rs</b>. */ void -routerstatus_free(routerstatus_t *rs) +routerstatus_free_(routerstatus_t *rs) { if (!rs) return; @@ -283,7 +284,7 @@ routerstatus_free(routerstatus_t *rs) /** Free all storage held in <b>sig</b> */ void -document_signature_free(document_signature_t *sig) +document_signature_free_(document_signature_t *sig) { tor_free(sig->signature); tor_free(sig); @@ -301,7 +302,7 @@ document_signature_dup(const document_signature_t *sig) /** Free all storage held in <b>ns</b>. */ void -networkstatus_vote_free(networkstatus_t *ns) +networkstatus_vote_free_(networkstatus_t *ns) { if (!ns) return; @@ -1209,6 +1210,14 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out) return 1; } + if (we_are_hibernating()) { + if (msg_out) { + *msg_out = "We are hibernating or shutting down."; + } + log_info(LD_DIR, "Delaying dir fetches (Hibernating or shutting down)"); + return 1; + } + if (options->UseBridges) { /* If we know that none of our bridges can possibly work, avoid fetching * directory documents. But if some of them might work, try again. */ @@ -1501,6 +1510,32 @@ networkstatus_consensus_is_already_downloading(const char *resource) return answer; } +/* Does the current, reasonably live consensus have IPv6 addresses? + * Returns 1 if there is a reasonably live consensus and its consensus method + * includes IPv6 addresses in the consensus. + * Otherwise, if there is no consensus, or the method does not include IPv6 + * addresses, returns 0. */ +int +networkstatus_consensus_has_ipv6(const or_options_t* options) +{ + const networkstatus_t *cons = networkstatus_get_reasonably_live_consensus( + approx_time(), + usable_consensus_flavor()); + + /* If we have no consensus, we have no IPv6 in it */ + if (!cons) { + return 0; + } + + /* Different flavours of consensus gained IPv6 at different times */ + if (we_use_microdescriptors_for_circuits(options)) { + return + cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + } else { + return cons->consensus_method >= MIN_METHOD_FOR_A_LINES; + } +} + /** Given two router status entries for the same router identity, return 1 if * if the contents have changed between them. Otherwise, return 0. */ static int @@ -1682,7 +1717,7 @@ handle_missing_protocol_warning_impl(const networkstatus_t *c, } tor_free(protocol_warning); if (should_exit) - exit(1); + exit(1); // XXXX bad exit: should return from main. } /** Called when we have received a networkstatus <b>c</b>. If there are @@ -1731,7 +1766,7 @@ networkstatus_set_current_consensus(const char *consensus, { networkstatus_t *c=NULL; int r, result = -1; - time_t now = time(NULL); + time_t now = approx_time(); const or_options_t *options = get_options(); char *unverified_fname = NULL, *consensus_fname = NULL; int flav = networkstatus_parse_flavor_name(flavor); @@ -1796,15 +1831,15 @@ networkstatus_set_current_consensus(const char *consensus, } if (!strcmp(flavor, "ns")) { - consensus_fname = get_datadir_fname("cached-consensus"); - unverified_fname = get_datadir_fname("unverified-consensus"); + consensus_fname = get_cachedir_fname("cached-consensus"); + unverified_fname = get_cachedir_fname("unverified-consensus"); if (current_ns_consensus) { current_digests = ¤t_ns_consensus->digests; current_valid_after = current_ns_consensus->valid_after; } } else if (!strcmp(flavor, "microdesc")) { - consensus_fname = get_datadir_fname("cached-microdesc-consensus"); - unverified_fname = get_datadir_fname("unverified-microdesc-consensus"); + consensus_fname = get_cachedir_fname("cached-microdesc-consensus"); + unverified_fname = get_cachedir_fname("unverified-microdesc-consensus"); if (current_md_consensus) { current_digests = ¤t_md_consensus->digests; current_valid_after = current_md_consensus->valid_after; @@ -1813,9 +1848,9 @@ networkstatus_set_current_consensus(const char *consensus, cached_dir_t *cur; char buf[128]; tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor); - consensus_fname = get_datadir_fname(buf); + consensus_fname = get_cachedir_fname(buf); tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor); - unverified_fname = get_datadir_fname(buf); + unverified_fname = get_cachedir_fname(buf); cur = dirserv_get_consensus(flavor); if (cur) { current_digests = &cur->digests; @@ -2058,6 +2093,7 @@ networkstatus_note_certs_arrived(const char *source_dir) { int i; for (i=0; i<N_CONSENSUS_FLAVORS; ++i) { + const char *flavor_name = networkstatus_get_flavor_name(i); consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i]; if (!waiting->consensus) continue; @@ -2065,7 +2101,7 @@ networkstatus_note_certs_arrived(const char *source_dir) char *waiting_body = waiting->body; if (!networkstatus_set_current_consensus( waiting_body, - networkstatus_get_flavor_name(i), + flavor_name, NSSET_WAS_WAITING_FOR_CERTS, source_dir)) { tor_free(waiting_body); @@ -2218,7 +2254,9 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { - return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL); + return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + NULL); } /** Alloc and return a string describing routerstatuses for the most @@ -2231,13 +2269,13 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs) char * networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) { - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + const time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; char *answer; routerlist_t *rl = router_get_routerlist(); smartlist_t *statuses; - uint8_t purpose = router_purpose_from_string(purpose_string); + const uint8_t purpose = router_purpose_from_string(purpose_string); routerstatus_t rs; - int bridge_auth = authdir_mode_bridge(get_options()); + const int bridge_auth = authdir_mode_bridge(get_options()); if (purpose == ROUTER_PURPOSE_UNKNOWN) { log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.", @@ -2254,6 +2292,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) continue; if (ri->purpose != purpose) continue; + /* TODO: modifying the running flag in a getinfo is a bad idea */ if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) dirserv_set_router_is_running(ri, now); /* then generate and write out status lines for each of them */ @@ -2272,7 +2311,6 @@ void networkstatus_dump_bridge_status_to_file(time_t now) { char *status = networkstatus_getinfo_by_purpose("bridge", now); - const or_options_t *options = get_options(); char *fname = NULL; char *thresholds = NULL; char *published_thresholds_and_status = NULL; @@ -2294,8 +2332,7 @@ networkstatus_dump_bridge_status_to_file(time_t now) "published %s\nflag-thresholds %s\n%s%s", published, thresholds, fingerprint_line ? fingerprint_line : "", status); - tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges", - options->DataDirectory); + fname = get_datadir_fname("networkstatus-bridges"); write_str_to_file(fname,published_thresholds_and_status,0); tor_free(thresholds); tor_free(published_thresholds_and_status); diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 39a0f753d8..1851a55e82 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -18,8 +18,12 @@ void networkstatus_reset_warnings(void); void networkstatus_reset_download_failures(void); char *networkstatus_read_cached_consensus(const char *flavorname); int router_reload_consensus_networkstatus(void); -void routerstatus_free(routerstatus_t *rs); -void networkstatus_vote_free(networkstatus_t *ns); +void routerstatus_free_(routerstatus_t *rs); +#define routerstatus_free(rs) \ + FREE_AND_NULL(routerstatus_t, routerstatus_free_, (rs)) +void networkstatus_vote_free_(networkstatus_t *ns); +#define networkstatus_vote_free(ns) \ + FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns)) networkstatus_voter_info_t *networkstatus_get_voter_by_id( networkstatus_t *vote, const char *identity); @@ -89,6 +93,7 @@ int networkstatus_consensus_can_use_multiple_directories( MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,( const or_options_t *options)); int networkstatus_consensus_is_already_downloading(const char *resource); +int networkstatus_consensus_has_ipv6(const or_options_t* options); #define NSSET_FROM_CACHE 1 #define NSSET_WAS_WAITING_FOR_CERTS 2 @@ -124,12 +129,16 @@ int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight, int32_t default_val); const char *networkstatus_get_flavor_name(consensus_flavor_t flav); int networkstatus_parse_flavor_name(const char *flavname); -void document_signature_free(document_signature_t *sig); +void document_signature_free_(document_signature_t *sig); +#define document_signature_free(sig) \ + FREE_AND_NULL(document_signature_t, document_signature_free_, (sig)) document_signature_t *document_signature_dup(const document_signature_t *sig); void networkstatus_free_all(void); int networkstatus_get_weight_scale_param(networkstatus_t *ns); -void vote_routerstatus_free(vote_routerstatus_t *rs); +void vote_routerstatus_free_(vote_routerstatus_t *rs); +#define vote_routerstatus_free(rs) \ + FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 9a477ecf43..391b31d683 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -66,7 +66,9 @@ #include <string.h> static void nodelist_drop_node(node_t *node, int remove_from_ht); -static void node_free(node_t *node); +#define node_free(val) \ + FREE_AND_NULL(node_t, node_free_, (val)) +static void node_free_(node_t *node); /** count_usable_descriptors counts descriptors with these flag(s) */ @@ -105,6 +107,7 @@ typedef struct nodelist_t { * you should add it to this map with node_add_to_ed25519_map(). */ HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id; + /* Set of addresses that belong to nodes we believe in. */ address_set_t *node_addrs; } nodelist_t; @@ -375,27 +378,6 @@ node_set_hsdir_index(node_t *node, const networkstatus_t *ns) return; } -/** Recompute all node hsdir indices. */ -void -nodelist_recompute_all_hsdir_indices(void) -{ - networkstatus_t *consensus; - if (!the_nodelist) { - return; - } - - /* Get a live consensus. Abort if not found */ - consensus = networkstatus_get_live_consensus(approx_time()); - if (!consensus) { - return; - } - - /* Recompute all hsdir indices */ - SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { - node_set_hsdir_index(node, consensus); - } SMARTLIST_FOREACH_END(node); -} - /** Called when a node's address changes. */ static void node_addrs_changed(node_t *node) @@ -493,7 +475,7 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out) /* Setting the HSDir index requires the ed25519 identity key which can * only be found either in the ri or md. This is why this is called here. * Only nodes supporting HSDir=2 protocol version needs this index. */ - if (node->rs && node->rs->supports_v3_hsdir) { + if (node->rs && node->rs->pv.supports_v3_hsdir) { node_set_hsdir_index(node, networkstatus_get_latest_consensus()); } @@ -535,7 +517,7 @@ nodelist_add_microdesc(microdesc_t *md) /* Setting the HSDir index requires the ed25519 identity key which can * only be found either in the ri or md. This is why this is called here. * Only nodes supporting HSDir=2 protocol version needs this index. */ - if (rs->supports_v3_hsdir) { + if (rs->pv.supports_v3_hsdir) { node_set_hsdir_index(node, ns); } node_add_to_ed25519_map(node); @@ -598,7 +580,7 @@ nodelist_set_consensus(networkstatus_t *ns) } } - if (rs->supports_v3_hsdir) { + if (rs->pv.supports_v3_hsdir) { node_set_hsdir_index(node, ns); } node_set_country(node); @@ -730,7 +712,7 @@ nodelist_find_nodes_with_microdesc(const microdesc_t *md) /** Release storage held by <b>node</b> */ static void -node_free(node_t *node) +node_free_(node_t *node) { if (!node) return; @@ -983,9 +965,12 @@ node_get_ed25519_id(const node_t *node) { const ed25519_public_key_t *ri_pk = NULL; const ed25519_public_key_t *md_pk = NULL; + if (node->ri) { if (node->ri->cache_info.signing_key_cert) { ri_pk = &node->ri->cache_info.signing_key_cert->signing_key; + /* Checking whether routerinfo ed25519 is all zero. + * Our descriptor parser should make sure this never happens. */ if (BUG(ed25519_public_key_is_zero(ri_pk))) ri_pk = NULL; } @@ -994,6 +979,10 @@ node_get_ed25519_id(const node_t *node) if (node->md) { if (node->md->ed25519_identity_pkey) { md_pk = node->md->ed25519_identity_pkey; + /* Checking whether microdesc ed25519 is all zero. + * Our descriptor parser should make sure this never happens. */ + if (BUG(ed25519_public_key_is_zero(md_pk))) + md_pk = NULL; } } @@ -1027,27 +1016,47 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) } } +/** Dummy object that should be unreturnable. Used to ensure that + * node_get_protover_summary_flags() always returns non-NULL. */ +static const protover_summary_flags_t zero_protover_flags = { + 0,0,0,0,0,0,0 +}; + +/** Return the protover_summary_flags for a given node. */ +static const protover_summary_flags_t * +node_get_protover_summary_flags(const node_t *node) +{ + if (node->rs) { + return &node->rs->pv; + } else if (node->ri) { + return &node->ri->pv; + } else { + /* This should be impossible: every node should have a routerstatus or a + * router descriptor or both. But just in case we've messed up somehow, + * return a nice empty set of flags to indicate "this node supports + * nothing." */ + tor_assert_nonfatal_unreached_once(); + return &zero_protover_flags; + } +} + /** Return true iff <b>node</b> supports authenticating itself - * by ed25519 ID during the link handshake in a way that we can understand - * when we probe it. */ + * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>, + * it needs to be using a link authentication method that we understand. + * If not, any plausible link authentication method will do. */ int -node_supports_ed25519_link_authentication(const node_t *node) +node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us) { - /* XXXX Oh hm. What if some day in the future there are link handshake - * versions that aren't 3 but which are ed25519 */ if (! node_get_ed25519_id(node)) return 0; - if (node->ri) { - const char *protos = node->ri->protocol_list; - if (protos == NULL) - return 0; - return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3); - } - if (node->rs) { - return node->rs->supports_ed25519_link_handshake; - } - tor_assert_nonfatal_unreached_once(); - return 0; + + const protover_summary_flags_t *pv = node_get_protover_summary_flags(node); + + if (compatible_with_us) + return pv->supports_ed25519_link_handshake_compat; + else + return pv->supports_ed25519_link_handshake_any; } /** Return true iff <b>node</b> supports the hidden service directory version @@ -1057,27 +1066,7 @@ node_supports_v3_hsdir(const node_t *node) { tor_assert(node); - if (node->rs) { - return node->rs->supports_v3_hsdir; - } - if (node->ri) { - if (node->ri->protocol_list == NULL) { - return 0; - } - /* Bug #22447 forces us to filter on tor version: - * If platform is a Tor version, and older than 0.3.0.8, return False. - * Else, obey the protocol list. */ - if (node->ri->platform) { - if (!strcmpstart(node->ri->platform, "Tor ") && - !tor_version_as_new_as(node->ri->platform, "0.3.0.8")) { - return 0; - } - } - return protocol_list_supports_protocol(node->ri->protocol_list, - PRT_HSDIR, PROTOVER_HSDIR_V3); - } - tor_assert_nonfatal_unreached_once(); - return 0; + return node_get_protover_summary_flags(node)->supports_v3_hsdir; } /** Return true iff <b>node</b> supports ed25519 authentication as an hidden @@ -1087,18 +1076,7 @@ node_supports_ed25519_hs_intro(const node_t *node) { tor_assert(node); - if (node->rs) { - return node->rs->supports_ed25519_hs_intro; - } - if (node->ri) { - if (node->ri->protocol_list == NULL) { - return 0; - } - return protocol_list_supports_protocol(node->ri->protocol_list, - PRT_HSINTRO, PROTOVER_HS_INTRO_V3); - } - tor_assert_nonfatal_unreached_once(); - return 0; + return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro; } /** Return true iff <b>node</b> supports to be a rendezvous point for hidden @@ -1108,19 +1086,7 @@ node_supports_v3_rendezvous_point(const node_t *node) { tor_assert(node); - if (node->rs) { - return node->rs->supports_v3_rendezvous_point; - } - if (node->ri) { - if (node->ri->protocol_list == NULL) { - return 0; - } - return protocol_list_supports_protocol(node->ri->protocol_list, - PRT_HSREND, - PROTOVER_HS_RENDEZVOUS_POINT_V3); - } - tor_assert_nonfatal_unreached_once(); - return 0; + return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; } /** Return the RSA ID key's SHA1 digest for the provided node. */ @@ -1699,15 +1665,21 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md) int node_has_curve25519_onion_key(const node_t *node) { - if (!node) - return 0; + return node_get_curve25519_onion_key(node) != NULL; +} - if (node->ri) - return routerinfo_has_curve25519_onion_key(node->ri); - else if (node->md) - return microdesc_has_curve25519_onion_key(node->md); +/** Return the curve25519 key of <b>node</b>, or NULL if none. */ +const curve25519_public_key_t * +node_get_curve25519_onion_key(const node_t *node) +{ + if (!node) + return NULL; + if (routerinfo_has_curve25519_onion_key(node->ri)) + return node->ri->onion_curve25519_pkey; + else if (microdesc_has_curve25519_onion_key(node->md)) + return node->md->onion_curve25519_pkey; else - return 0; + return NULL; } /** Refresh the country code of <b>ri</b>. This function MUST be called on diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 8a0c79f86d..dc20eaf0a5 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -36,8 +36,6 @@ void nodelist_remove_routerinfo(routerinfo_t *ri); void nodelist_purge(void); smartlist_t *nodelist_find_nodes_with_microdesc(const microdesc_t *md); -void nodelist_recompute_all_hsdir_indices(void); - void nodelist_free_all(void); void nodelist_assert_ok(void); @@ -66,7 +64,8 @@ const smartlist_t *node_get_declared_family(const node_t *node); const ed25519_public_key_t *node_get_ed25519_id(const node_t *node); int node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id); -int node_supports_ed25519_link_authentication(const node_t *node); +int node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us); int node_supports_v3_hsdir(const node_t *node); int node_supports_ed25519_hs_intro(const node_t *node); int node_supports_v3_rendezvous_point(const node_t *node); @@ -86,6 +85,8 @@ int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out); int node_has_curve25519_onion_key(const node_t *node); +const curve25519_public_key_t *node_get_curve25519_onion_key( + const node_t *node); MOCK_DECL(smartlist_t *, nodelist_get_list, (void)); diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 508e5844eb..ebbe0018bd 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -195,7 +195,7 @@ nt_service_loadlibrary(void) return; err: printf("Unable to load library support for NT services: exiting.\n"); - exit(1); + exit(1); // exit ok: ntmain can't read libraries } /** If we're compiled to run as an NT service, and the service wants to @@ -318,7 +318,7 @@ nt_service_main(void) printf("Service error %d : %s\n", (int) result, errmsg); tor_free(errmsg); if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { - if (tor_init(backup_argc, backup_argv) < 0) + if (tor_init(backup_argc, backup_argv)) return; switch (get_options()->command) { case CMD_RUN_TOR: diff --git a/src/or/onion.c b/src/or/onion.c index 7e1e89df1b..bd80c2f503 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -423,7 +423,7 @@ server_onion_keys_new(void) /** Release all storage held in <b>keys</b>. */ void -server_onion_keys_free(server_onion_keys_t *keys) +server_onion_keys_free_(server_onion_keys_t *keys) { if (! keys) return; diff --git a/src/or/onion.h b/src/or/onion.h index 95544dfac1..3b738debeb 100644 --- a/src/or/onion.h +++ b/src/or/onion.h @@ -31,7 +31,9 @@ typedef struct server_onion_keys_t { #define MAX_ONIONSKIN_REPLY_LEN 255 server_onion_keys_t *server_onion_keys_new(void); -void server_onion_keys_free(server_onion_keys_t *keys); +void server_onion_keys_free_(server_onion_keys_t *keys); +#define server_onion_keys_free(keys) \ + FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys)) void onion_handshake_state_release(onion_handshake_state_t *state); diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c index 146943a273..de9103b1f5 100644 --- a/src/or/onion_fast.c +++ b/src/or/onion_fast.c @@ -23,7 +23,7 @@ * [*]Actually, it's possible that TAP _was_ a little better than TLS with * RSA1024 certificates and EDH1024 for forward secrecy, if you * hypothesize an adversary who can compute discrete logarithms on a - * small number of targetted DH1024 fields, but who can't break all that + * small number of targeted DH1024 fields, but who can't break all that * many RSA1024 keys. **/ @@ -32,7 +32,7 @@ /** Release all state held in <b>victim</b>. */ void -fast_handshake_state_free(fast_handshake_state_t *victim) +fast_handshake_state_free_(fast_handshake_state_t *victim) { if (! victim) return; diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h index 3a5aefea3f..c56712e2c2 100644 --- a/src/or/onion_fast.h +++ b/src/or/onion_fast.h @@ -19,7 +19,9 @@ typedef struct fast_handshake_state_t { uint8_t state[DIGEST_LEN]; } fast_handshake_state_t; -void fast_handshake_state_free(fast_handshake_state_t *victim); +void fast_handshake_state_free_(fast_handshake_state_t *victim); +#define fast_handshake_state_free(st) \ + FREE_AND_NULL(fast_handshake_state_t, fast_handshake_state_free_, (st)) int fast_onionskin_create(fast_handshake_state_t **handshake_state_out, uint8_t *handshake_out); diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c index 902260b54b..b167cb61fb 100644 --- a/src/or/onion_ntor.c +++ b/src/or/onion_ntor.c @@ -28,7 +28,7 @@ /** Free storage held in an ntor handshake state. */ void -ntor_handshake_state_free(ntor_handshake_state_t *state) +ntor_handshake_state_free_(ntor_handshake_state_t *state) { if (!state) return; diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h index 02dea2dfc1..f7c962b7d0 100644 --- a/src/or/onion_ntor.h +++ b/src/or/onion_ntor.h @@ -17,7 +17,9 @@ typedef struct ntor_handshake_state_t ntor_handshake_state_t; /** Length of an ntor reply, as sent from server to client. */ #define NTOR_REPLY_LEN 64 -void ntor_handshake_state_free(ntor_handshake_state_t *state); +void ntor_handshake_state_free_(ntor_handshake_state_t *state); +#define ntor_handshake_state_free(state) \ + FREE_AND_NULL(ntor_handshake_state_t, ntor_handshake_state_free_, (state)) int onion_skin_ntor_create(const uint8_t *router_id, const curve25519_public_key_t *router_key, diff --git a/src/or/or.h b/src/or/or.h index 5128fd2197..edf89be9a3 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -506,6 +506,7 @@ typedef enum { */ /** Client-side circuit purpose: Normal circuit, with cpath. */ #define CIRCUIT_PURPOSE_C_GENERAL 5 +#define CIRCUIT_PURPOSE_C_HS_MIN_ 6 /** Client-side circuit purpose: at the client, connecting to intro point. */ #define CIRCUIT_PURPOSE_C_INTRODUCING 6 /** Client-side circuit purpose: at the client, sent INTRODUCE1 to intro point, @@ -523,28 +524,46 @@ typedef enum { #define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11 /** Client-side circuit purpose: at the client, rendezvous established. */ #define CIRCUIT_PURPOSE_C_REND_JOINED 12 +/** This circuit is used for getting hsdirs */ +#define CIRCUIT_PURPOSE_C_HSDIR_GET 13 +#define CIRCUIT_PURPOSE_C_HS_MAX_ 13 /** This circuit is used for build time measurement only */ -#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13 -#define CIRCUIT_PURPOSE_C_MAX_ 13 +#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 14 +#define CIRCUIT_PURPOSE_C_MAX_ 14 + +#define CIRCUIT_PURPOSE_S_HS_MIN_ 15 /** Hidden-service-side circuit purpose: at the service, waiting for * introductions. */ -#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14 +#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 15 /** Hidden-service-side circuit purpose: at the service, successfully * established intro. */ -#define CIRCUIT_PURPOSE_S_INTRO 15 +#define CIRCUIT_PURPOSE_S_INTRO 16 /** Hidden-service-side circuit purpose: at the service, connecting to rend * point. */ -#define CIRCUIT_PURPOSE_S_CONNECT_REND 16 +#define CIRCUIT_PURPOSE_S_CONNECT_REND 17 /** Hidden-service-side circuit purpose: at the service, rendezvous * established. */ -#define CIRCUIT_PURPOSE_S_REND_JOINED 17 +#define CIRCUIT_PURPOSE_S_REND_JOINED 18 +/** This circuit is used for uploading hsdirs */ +#define CIRCUIT_PURPOSE_S_HSDIR_POST 19 +#define CIRCUIT_PURPOSE_S_HS_MAX_ 19 + /** A testing circuit; not meant to be used for actual traffic. */ -#define CIRCUIT_PURPOSE_TESTING 18 +#define CIRCUIT_PURPOSE_TESTING 20 /** A controller made this circuit and Tor should not use it. */ -#define CIRCUIT_PURPOSE_CONTROLLER 19 +#define CIRCUIT_PURPOSE_CONTROLLER 21 /** This circuit is used for path bias probing only */ -#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 20 -#define CIRCUIT_PURPOSE_MAX_ 20 +#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 22 + +/** This circuit is used for vanguards/restricted paths. + * + * This type of circuit is *only* created preemptively and never + * on-demand. When an HS operation needs to take place (e.g. connect to an + * intro point), these circuits are then cannibalized and repurposed to the + * actual needed HS purpose. */ +#define CIRCUIT_PURPOSE_HS_VANGUARDS 23 + +#define CIRCUIT_PURPOSE_MAX_ 23 /** A catch-all for unrecognized purposes. Currently we don't expect * to make or see any circuits with this purpose. */ #define CIRCUIT_PURPOSE_UNKNOWN 255 @@ -1166,8 +1185,8 @@ typedef struct packed_cell_t { /** Next cell queued on this circuit. */ TOR_SIMPLEQ_ENTRY(packed_cell_t) next; char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */ - uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high - * bits truncated) when this cell was inserted. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ } packed_cell_t; /** A queue of cells on a circuit, waiting to be added to the @@ -1182,7 +1201,8 @@ typedef struct cell_queue_t { typedef struct destroy_cell_t { TOR_SIMPLEQ_ENTRY(destroy_cell_t) next; circid_t circid; - uint32_t inserted_time; /** Timestamp when this was queued. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ uint8_t reason; } destroy_cell_t; @@ -1798,9 +1818,6 @@ typedef struct entry_connection_t { * the exit has sent a CONNECTED cell) and we have chosen to use it. */ unsigned int may_use_optimistic_data : 1; - - /** Are we a socks SocksSocket listener? */ - unsigned int is_socks_socket:1; } entry_connection_t; /** Subtype of connection_t for an "directory connection" -- that is, an HTTP @@ -2180,6 +2197,43 @@ typedef struct signed_descriptor_t { /** A signed integer representing a country code. */ typedef int16_t country_t; +/** Flags used to summarize the declared protocol versions of a relay, + * so we don't need to parse them again and again. */ +typedef struct protover_summary_flags_t { + /** True iff we have a proto line for this router, or a versions line + * from which we could infer the protocols. */ + unsigned int protocols_known:1; + + /** True iff this router has a version or protocol list that allows it to + * accept EXTEND2 cells. This requires Relay=2. */ + unsigned int supports_extend2_cells:1; + + /** True iff this router has a protocol list that allows it to negotiate + * ed25519 identity keys on a link handshake with us. This + * requires LinkAuth=3. */ + unsigned int supports_ed25519_link_handshake_compat:1; + + /** True iff this router has a protocol list that allows it to negotiate + * ed25519 identity keys on a link handshake, at all. This requires some + * LinkAuth=X for X >= 3. */ + unsigned int supports_ed25519_link_handshake_any:1; + + /** True iff this router has a protocol list that allows it to be an + * introduction point supporting ed25519 authentication key which is part of + * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */ + unsigned int supports_ed25519_hs_intro : 1; + + /** True iff this router has a protocol list that allows it to be an hidden + * service directory supporting version 3 as seen in proposal 224. This + * requires HSDir=2. */ + unsigned int supports_v3_hsdir : 1; + + /** True iff this router has a protocol list that allows it to be an hidden + * service rendezvous point supporting version 3 as seen in proposal 224. + * This requires HSRend=2. */ + unsigned int supports_v3_rendezvous_point: 1; +} protover_summary_flags_t; + /** Information about another onion router in the network. */ typedef struct { signed_descriptor_t cache_info; @@ -2248,23 +2302,26 @@ typedef struct { * this routerinfo. Used only during voting. */ unsigned int omit_from_vote:1; + /** Flags to summarize the protocol versions for this routerinfo_t. */ + protover_summary_flags_t pv; + /** Tor can use this router for general positions in circuits; we got it * from a directory server as usual, or we're an authority and a server * uploaded it. */ #define ROUTER_PURPOSE_GENERAL 0 /** Tor should avoid using this router for circuit-building: we got it - * from a crontroller. If the controller wants to use it, it'll have to + * from a controller. If the controller wants to use it, it'll have to * ask for it by identity. */ #define ROUTER_PURPOSE_CONTROLLER 1 /** Tor should use this router only for bridge positions in circuits: we got * it via a directory request from the bridge itself, or a bridge - * authority. x*/ + * authority. */ #define ROUTER_PURPOSE_BRIDGE 2 /** Tor should not use this router; it was marked in cached-descriptors with * a purpose we didn't recognize. */ #define ROUTER_PURPOSE_UNKNOWN 255 - /* In what way did we find out about this router? One of ROUTER_PURPOSE_*. + /** In what way did we find out about this router? One of ROUTER_PURPOSE_*. * Routers of different purposes are kept segregated and used for different * things; see notes on ROUTER_PURPOSE_* macros above. */ @@ -2326,38 +2383,15 @@ typedef struct routerstatus_t { unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort * or it claims to accept tunnelled dir requests. */ - /** True iff we have a proto line for this router, or a versions line - * from which we could infer the protocols. */ - unsigned int protocols_known:1; - - /** True iff this router has a version or protocol list that allows it to - * accept EXTEND2 cells */ - unsigned int supports_extend2_cells:1; - - /** True iff this router has a protocol list that allows it to negotiate - * ed25519 identity keys on a link handshake. */ - unsigned int supports_ed25519_link_handshake:1; - - /** True iff this router has a protocol list that allows it to be an - * introduction point supporting ed25519 authentication key which is part of - * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */ - unsigned int supports_ed25519_hs_intro : 1; - - /** True iff this router has a protocol list that allows it to be an hidden - * service directory supporting version 3 as seen in proposal 224. This - * requires HSDir=2. */ - unsigned int supports_v3_hsdir : 1; - - /** True iff this router has a protocol list that allows it to be an hidden - * service rendezvous point supporting version 3 as seen in proposal 224. - * This requires HSRend=2. */ - unsigned int supports_v3_rendezvous_point: 1; unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */ unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */ unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with * the Unmeasured flag set. */ + /** Flags to summarize the protocol versions for this routerstatus_t. */ + protover_summary_flags_t pv; + uint32_t bandwidth_kb; /**< Bandwidth (capacity) of the router as reported in * the vote/consensus, in kilobytes/sec. */ @@ -3105,7 +3139,7 @@ typedef struct circuit_t { /** When the circuit was first used, or 0 if the circuit is clean. * - * XXXX Note that some code will artifically adjust this value backward + * XXXX Note that some code will artificially adjust this value backward * in time in order to indicate that a circuit shouldn't be used for new * streams, but that it can stay alive as long as it has streams on it. * That's a kludge we should fix. @@ -3363,7 +3397,7 @@ typedef struct origin_circuit_t { uint32_t global_identifier; /** True if we have associated one stream to this circuit, thereby setting - * the isolation paramaters for this circuit. Note that this doesn't + * the isolation parameters for this circuit. Note that this doesn't * necessarily mean that we've <em>attached</em> any streams to the circuit: * we may only have marked up this circuit during the launch process. */ @@ -3654,10 +3688,24 @@ typedef struct { int TruncateLogFile; /**< Boolean: Should we truncate the log file before we start writing? */ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ + char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */ char *DebugLogFile; /**< Where to send verbose log messages. */ - char *DataDirectory; /**< OR only: where to store long-term data. */ + char *DataDirectory_option; /**< Where to store long-term data, as + * configured by the user. */ + char *DataDirectory; /**< Where to store long-term data, as modified. */ int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */ + + char *KeyDirectory_option; /**< Where to store keys, as + * configured by the user. */ + char *KeyDirectory; /**< Where to store keys data, as modified. */ + int KeyDirectoryGroupReadable; /**< Boolean: Is the KeyDirectory g+r? */ + + char *CacheDirectory_option; /**< Where to store cached data, as + * configured by the user. */ + char *CacheDirectory; /**< Where to store cached data, as modified. */ + int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ + char *Nickname; /**< OR only: nickname of this onion router. */ char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ @@ -3693,6 +3741,7 @@ typedef struct { * interface addresses? * Includes OutboundBindAddresses and * configured ports. */ + int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */ config_line_t *SocksPolicy; /**< Lists of socks policy components */ config_line_t *DirPolicy; /**< Lists of dir policy components */ /** Local address to bind outbound sockets */ @@ -3742,7 +3791,7 @@ typedef struct { * for control connections. */ int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ - int SocksSocketsGroupWritable; /**< Boolean: Are SOCKS sockets g+rw? */ + int UnixSocksGroupWritable; /**< Boolean: Are SOCKS Unix sockets g+rw? */ /** Ports to listen on for directory connections. */ config_line_t *DirPort_lines; config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */ @@ -3860,6 +3909,14 @@ typedef struct { /** A routerset that should be used when picking RPs for HS circuits. */ routerset_t *Tor2webRendezvousPoints; + /** A routerset that should be used when picking middle nodes for HS + * circuits. */ + routerset_t *HSLayer2Nodes; + + /** A routerset that should be used when picking third-hop nodes for HS + * circuits. */ + routerset_t *HSLayer3Nodes; + /** Onion Services in HiddenServiceSingleHopMode make one-hop (direct) * circuits between the onion service server, and the introduction and * rendezvous points. (Onion service descriptors are still posted using @@ -3972,6 +4029,8 @@ typedef struct { int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds * have passed. */ + int MainloopStats; /**< Log main loop statistics as part of the + * heartbeat messages. */ char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */ tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */ @@ -4089,6 +4148,8 @@ typedef struct { /** Process specifier for a controller that ‘owns’ this Tor * instance. Tor will terminate if its owning controller does. */ char *OwningControllerProcess; + /** FD specifier for a controller that owns this Tor instance. */ + int OwningControllerFD; int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how * long do we wait before exiting? */ @@ -4118,7 +4179,7 @@ typedef struct { int UseEntryGuards_option; /** Internal variable to remember whether we're actually acting on * UseEntryGuards_option -- when we're a non-anonymous Tor2web client or - * Single Onion Service, it is alwasy false, otherwise we use the value of + * Single Onion Service, it is always false, otherwise we use the value of * UseEntryGuards_option. */ int UseEntryGuards; @@ -4647,6 +4708,14 @@ typedef struct { /* An ordered list of scheduler_types mapped from Schedulers. */ smartlist_t *SchedulerTypes_; + /** List of files that were opened by %include in torrc and torrc-defaults */ + smartlist_t *FilesOpenedByIncludes; + + /** If true, Tor shouldn't install any posix signal handlers, since it is + * running embedded inside another process. + */ + int DisableSignalHandlers; + /** Autobool: Is the circuit creation DoS mitigation subsystem enabled? */ int DoSCircuitCreationEnabled; /** Minimum concurrent connection needed from one single address before any @@ -5308,7 +5377,7 @@ typedef struct rend_intro_point_t { */ int accepted_introduce2_count; - /** (Service side only) Number of maximum INTRODUCE2 cells that this IP + /** (Service side only) Maximum number of INTRODUCE2 cells that this IP * will accept. This is a random value between * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */ diff --git a/src/or/policies.c b/src/or/policies.c index 3bfea3a57c..f718ded326 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -18,6 +18,7 @@ #define POLICIES_PRIVATE #include "or.h" +#include "bridges.h" #include "config.h" #include "dirserv.h" #include "microdesc.h" @@ -81,7 +82,8 @@ static int policies_parse_exit_policy_internal( const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy); + int add_default_policy, + int add_reduced_policy); /** Replace all "private" entries in *<b>policy</b> with their expanded * equivalents. */ @@ -894,9 +896,10 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, pref_ipv6, ap); } -/* The microdescriptor consensus has no IPv6 addresses in rs: they are in - * the microdescriptors. This means we can't rely on the node's IPv6 address - * until its microdescriptor is available (when using microdescs). +/* Some microdescriptor consensus methods have no IPv6 addresses in rs: they + * are in the microdescriptors. For these consensus methods, we can't rely on + * the node's IPv6 address until its microdescriptor is available (when using + * microdescs). * But for bridges, rewrite_node_address_for_bridge() updates node->ri with * the configured address, so we can trust bridge addresses. * (Bridges could gain an IPv6 address if their microdescriptor arrives, but @@ -914,11 +917,26 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node) return 0; } + /* If the node has an IPv6 address, we're not waiting */ + if (node_has_ipv6_addr(node)) { + return 0; + } + + /* If the current consensus method and flavour has IPv6 addresses, we're not + * waiting */ + if (networkstatus_consensus_has_ipv6(options)) { + return 0; + } + + /* Bridge clients never use the address from a bridge's md, so there's no + * need to wait for it. */ + if (node_is_a_configured_bridge(node)) { + return 0; + } + /* We are waiting if we_use_microdescriptors_for_circuits() and we have no - * md. Bridges have a ri based on their config. They would never use the - * address from their md, so there's no need to wait for it. */ - return (!node->md && we_use_microdescriptors_for_circuits(options) && - !node->ri); + * md. */ + return (!node->md && we_use_microdescriptors_for_circuits(options)); } /** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. @@ -1146,7 +1164,7 @@ validate_addr_policies(const or_options_t *options, char **msg) "to 1 to disable this warning, and for forward compatibility.", options->ExitPolicy == NULL ? " with the default exit policy" : ""); - if (options->ExitPolicy == NULL) { + if (options->ExitPolicy == NULL && options->ReducedExitPolicy == 0) { log_warn(LD_CONFIG, "In a future version of Tor, ExitRelay 0 may become the " "default when no ExitPolicy is given."); @@ -1793,14 +1811,14 @@ policies_parse_exit_policy_reject_private( /* Reject public IPv4 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); /* Don't look for IPv6 addresses if we're configured as IPv4-only */ if (ipv6_exit) { /* Reject public IPv6 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); } } @@ -1879,6 +1897,24 @@ policies_log_first_redundant_entry(const smartlist_t *policy) "reject *:563,reject *:1214,reject *:4661-4666," \ "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*" +#define REDUCED_EXIT_POLICY \ + "accept *:20-23,accept *:43,accept *:53,accept *:79-81,accept *:88," \ + "accept *:110,accept *:143,accept *:194,accept *:220,accept *:389," \ + "accept *:443,accept *:464,accept *:465,accept *:531,accept *:543-544," \ + "accept *:554,accept *:563,accept *:587,accept *:636,accept *:706," \ + "accept *:749,accept *:873,accept *:902-904,accept *:981,accept *:989-995," \ + "accept *:1194,accept *:1220,accept *:1293,accept *:1500,accept *:1533," \ + "accept *:1677,accept *:1723,accept *:1755,accept *:1863," \ + "accept *:2082-2083,accept *:2086-2087,accept *:2095-2096," \ + "accept *:2102-2104,accept *:3128,accept *:3389,accept *:3690," \ + "accept *:4321,accept *:4643,accept *:5050,accept *:5190," \ + "accept *:5222-5223,accept *:5228,accept *:5900,accept *:6660-6669," \ + "accept *:6679,accept *:6697,accept *:8000,accept *:8008,accept *:8074," \ + "accept *:8080,accept *:8082,accept *:8087-8088,accept *:8232-8233," \ + "accept *:8332-8333,accept *:8443,accept *:8888,accept *:9418," \ + "accept *:9999,accept *:10000,accept *:11371,accept *:19294," \ + "accept *:19638,accept *:50002,accept *:64738,reject *:*" + /** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. * * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy. @@ -1914,7 +1950,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy) + int add_default_policy, + int add_reduced_policy) { if (!ipv6_exit) { append_exit_policy_string(dest, "reject *6:*"); @@ -1940,7 +1977,9 @@ policies_parse_exit_policy_internal(config_line_t *cfg, * effect, and are most likely an error. */ policies_log_first_redundant_entry(*dest); - if (add_default_policy) { + if (add_reduced_policy) { + append_exit_policy_string(dest, REDUCED_EXIT_POLICY); + } else if (add_default_policy) { append_exit_policy_string(dest, DEFAULT_EXIT_POLICY); } else { append_exit_policy_string(dest, "reject *4:*"); @@ -1981,13 +2020,15 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0; int reject_local_interfaces = (options & EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0; + int add_reduced = (options & EXIT_POLICY_ADD_REDUCED) ? 1 : 0; return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled, reject_private, configured_addresses, reject_local_interfaces, reject_local_interfaces, - add_default); + add_default, + add_reduced); } /** Helper function that adds a copy of addr to a smartlist as long as it is @@ -2097,7 +2138,10 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, } if (!or_options->BridgeRelay) { - parser_cfg |= EXIT_POLICY_ADD_DEFAULT; + if (or_options->ReducedExitPolicy) + parser_cfg |= EXIT_POLICY_ADD_REDUCED; + else + parser_cfg |= EXIT_POLICY_ADD_DEFAULT; } if (or_options->ExitPolicyRejectLocalInterfaces) { @@ -2363,7 +2407,7 @@ policy_summary_item_split(policy_summary_item_t* old, uint16_t new_starts) #define REJECT_CUTOFF_SCALE_IPV6 (64) /* Ports are rejected in an IPv6 summary if they are rejected in more than one * IPv6 /16 address block. - * This is rougly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and + * This is roughly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and * some scattered smaller blocks) have been allocated to the RIRs. * Network providers are typically allocated one or more IPv6 /32s. */ @@ -2769,7 +2813,7 @@ write_short_policy(const short_policy_t *policy) /** Release all storage held in <b>policy</b>. */ void -short_policy_free(short_policy_t *policy) +short_policy_free_(short_policy_t *policy) { tor_free(policy); } @@ -3019,7 +3063,7 @@ getinfo_helper_policies(control_connection_t *conn, /** Release all storage held by <b>p</b>. */ void -addr_policy_list_free(smartlist_t *lst) +addr_policy_list_free_(smartlist_t *lst) { if (!lst) return; @@ -3029,7 +3073,7 @@ addr_policy_list_free(smartlist_t *lst) /** Release all storage held by <b>p</b>. */ void -addr_policy_free(addr_policy_t *p) +addr_policy_free_(addr_policy_t *p) { if (!p) return; diff --git a/src/or/policies.h b/src/or/policies.h index 52ff4e2f99..35220a812f 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -22,7 +22,8 @@ #define EXIT_POLICY_REJECT_PRIVATE (1 << 1) #define EXIT_POLICY_ADD_DEFAULT (1 << 2) #define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3) -#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES +#define EXIT_POLICY_ADD_REDUCED (1 << 4) +#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_ADD_REDUCED /* All options set: used for unit testing */ #define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1) @@ -114,15 +115,21 @@ int getinfo_helper_policies(control_connection_t *conn, int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item, int format_for_desc); -void addr_policy_list_free(smartlist_t *p); -void addr_policy_free(addr_policy_t *p); +void addr_policy_list_free_(smartlist_t *p); +#define addr_policy_list_free(lst) \ + FREE_AND_NULL(smartlist_t, addr_policy_list_free_, (lst)) +void addr_policy_free_(addr_policy_t *p); +#define addr_policy_free(p) \ + FREE_AND_NULL(addr_policy_t, addr_policy_free_, (p)) void policies_free_all(void); char *policy_summarize(smartlist_t *policy, sa_family_t family); short_policy_t *parse_short_policy(const char *summary); char *write_short_policy(const short_policy_t *policy); -void short_policy_free(short_policy_t *policy); +void short_policy_free_(short_policy_t *policy); +#define short_policy_free(p) \ + FREE_AND_NULL(short_policy_t, short_policy_free_, (p)) int short_policy_is_reject_star(const short_policy_t *policy); addr_policy_result_t compare_tor_addr_to_short_policy( const tor_addr_t *addr, uint16_t port, diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c index 7649fcc4be..91633d02af 100644 --- a/src/or/proto_socks.c +++ b/src/or/proto_socks.c @@ -66,7 +66,7 @@ socks_request_new(void) /** Free all storage held in the socks_request_t <b>req</b>. */ void -socks_request_free(socks_request_t *req) +socks_request_free_(socks_request_t *req) { if (!req) return; diff --git a/src/or/proto_socks.h b/src/or/proto_socks.h index a714151414..02e0aca7e9 100644 --- a/src/or/proto_socks.h +++ b/src/or/proto_socks.h @@ -11,7 +11,9 @@ struct socks_request_t; struct buf_t; struct socks_request_t *socks_request_new(void); -void socks_request_free(struct socks_request_t *req); +void socks_request_free_(struct socks_request_t *req); +#define socks_request_free(req) \ + FREE_AND_NULL(socks_request_t, socks_request_free_, (req)) int fetch_from_buf_socks(struct buf_t *buf, socks_request_t *req, int log_sockstype, int safe_socks); int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason); diff --git a/src/or/protover.c b/src/or/protover.c index 45f0377d61..cb168085c6 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -27,11 +27,14 @@ #include "protover.h" #include "routerparse.h" +#ifndef HAVE_RUST + static const smartlist_t *get_supported_protocol_list(void); static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver); /** Mapping between protocol type string and protocol type. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES` static const struct { protocol_type_t protover_type; const char *name; @@ -93,7 +96,7 @@ str_to_protocol_type(const char *s, protocol_type_t *pr_out) * Release all space held by a single proto_entry_t structure */ STATIC void -proto_entry_free(proto_entry_t *entry) +proto_entry_free_(proto_entry_t *entry) { if (!entry) return; @@ -292,8 +295,45 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, return contains; } +/** + * Return true iff "list" encodes a protocol list that includes support for + * the indicated protocol and version, or some later version. + */ +int +protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version) +{ + /* NOTE: This is a pretty inefficient implementation. If it ever shows + * up in profiles, we should memoize it. + */ + smartlist_t *protocols = parse_protocol_list(list); + if (!protocols) { + return 0; + } + const char *pr_name = protocol_type_to_str(tp); + + int contains = 0; + SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) { + if (strcasecmp(proto->name, pr_name)) + continue; + SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) { + if (range->high >= version) { + contains = 1; + goto found; + } + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(proto); + + found: + SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(protocols); + return contains; +} + /** Return the canonical string containing the list of protocols * that we support. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS` const char * protover_get_supported_protocols(void) { @@ -377,6 +417,8 @@ encode_protocol_list(const smartlist_t *sl) /* We treat any protocol list with more than this many subprotocols in it * as a DoS attempt. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `MAX_PROTOCOLS_TO_EXPAND` static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16); /** Voting helper: Given a list of proto_entry_t, return a newly allocated @@ -714,6 +756,7 @@ protocol_list_contains(const smartlist_t *protos, * Note that this is only used to infer protocols for Tor versions that * can't declare their own. **/ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor` const char * protover_compute_for_old_tor(const char *version) { @@ -763,3 +806,5 @@ protover_free_all(void) } } +#endif /* !defined(HAVE_RUST) */ + diff --git a/src/or/protover.h b/src/or/protover.h index 657977279e..477274e293 100644 --- a/src/or/protover.h +++ b/src/or/protover.h @@ -15,6 +15,8 @@ * descriptors. Authorities should use this to decide whether to * guess proto lines. */ /* This is a guess. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` #define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" /** The protover version number that signifies HSDir support for HSv3 */ @@ -25,6 +27,8 @@ #define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 /** List of recognized subprotocols. */ +/// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust` +/// C_RUST_COUPLED: src/rust/protover/protover.rs `Proto` typedef enum protocol_type_t { PRT_LINK, PRT_LINKAUTH, @@ -47,6 +51,9 @@ char *protover_compute_vote(const smartlist_t *list_of_proto_strings, const char *protover_compute_for_old_tor(const char *version); int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version); +int protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version); void protover_free_all(void); @@ -70,11 +77,17 @@ typedef struct proto_entry_t { smartlist_t *ranges; } proto_entry_t; +#if !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) STATIC smartlist_t *parse_protocol_list(const char *s); -STATIC void proto_entry_free(proto_entry_t *entry); STATIC char *encode_protocol_list(const smartlist_t *sl); STATIC const char *protocol_type_to_str(protocol_type_t pr); STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out); +STATIC void proto_entry_free_(proto_entry_t *entry); +#endif /* !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) */ + +#define proto_entry_free(entry) \ + FREE_AND_NULL(proto_entry_t, proto_entry_free_, (entry)) + #endif /* defined(PROTOVER_PRIVATE) */ #endif /* !defined(TOR_PROTOVER_H) */ diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c new file mode 100644 index 0000000000..26e21cc1c5 --- /dev/null +++ b/src/or/protover_rust.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * \file protover_rust.c + * \brief Provide a C wrapper for functions exposed in /src/rust/protover, + * and safe translation/handling between the Rust/C boundary. + */ + +#include "or.h" +#include "protover.h" + +#ifdef HAVE_RUST + +/* Define for compatibility, used in main.c */ +void protover_free_all(void) {} + +#endif /* defined(HAVE_RUST) */ + diff --git a/src/or/reasons.c b/src/or/reasons.c index 03d49418da..ce1259b8f3 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -428,7 +428,7 @@ socks5_response_code_to_string(uint8_t code) } } -/** Return a string corresponding to a bandwidht_weight_rule_t */ +/** Return a string corresponding to a bandwidth_weight_rule_t */ const char * bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule) { diff --git a/src/or/relay.c b/src/or/relay.c index 275746ac93..506b7eccb3 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1158,7 +1158,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell, /** Drop all storage held by <b>addr</b>. */ STATIC void -address_ttl_free(address_ttl_t *addr) +address_ttl_free_(address_ttl_t *addr) { if (!addr) return; @@ -2425,7 +2425,7 @@ packed_cell_new(void) /** Return a packed cell used outside by channel_t lower layer */ void -packed_cell_free(packed_cell_t *cell) +packed_cell_free_(packed_cell_t *cell) { if (!cell) return; @@ -2482,7 +2482,7 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, (void)exitward; (void)use_stats; - copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + copy->inserted_timestamp = monotime_coarse_get_stamp(); cell_queue_append(queue, copy); } @@ -2565,7 +2565,7 @@ destroy_cell_queue_append(destroy_cell_queue_t *queue, cell->circid = circid; cell->reason = reason; /* Not yet used, but will be required for OOM handling. */ - cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + cell->inserted_timestamp = monotime_coarse_get_stamp(); TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next); ++queue->n; @@ -2596,7 +2596,7 @@ packed_cell_mem_cost(void) } /* DOCDOC */ -STATIC size_t +size_t cell_queues_get_total_allocation(void) { return total_cells_allocated * packed_cell_mem_cost(); @@ -2631,7 +2631,7 @@ cell_queues_check_size(void) if (rend_cache_total > get_options()->MaxMemInQueues / 5) { const size_t bytes_to_remove = rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10); - alloc -= hs_cache_handle_oom(time(NULL), bytes_to_remove); + alloc -= hs_cache_handle_oom(now, bytes_to_remove); } if (geoip_client_cache_total > get_options()->MaxMemInQueues / 5) { const size_t bytes_to_remove = @@ -2830,8 +2830,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) tor_assert(dcell); /* frees dcell */ cell = destroy_cell_to_packed_cell(dcell, chan->wide_circ_ids); - /* frees cell */ - channel_write_packed_cell(chan, cell); + /* Send the DESTROY cell. It is very unlikely that this fails but just + * in case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed. */ + channel_mark_for_close(chan); + continue; + } /* Update the cmux destroy counter */ circuitmux_notify_xmit_destroy(cmux); cell = NULL; @@ -2874,9 +2879,10 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics || get_options()->TestingEnableCellStatsEvent) { - uint32_t msec_waiting; - uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec(); - msec_waiting = msec_now - cell->inserted_time; + uint32_t timestamp_now = monotime_coarse_get_stamp(); + uint32_t msec_waiting = + (uint32_t) monotime_coarse_stamp_units_to_approx_msec( + timestamp_now - cell->inserted_timestamp); if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { or_circ = TO_OR_CIRCUIT(circ); @@ -2907,8 +2913,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); - /* Now send the cell */ - channel_write_packed_cell(chan, cell); + /* Now send the cell. It is very unlikely that this fails but just in + * case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed at this point. */ + channel_mark_for_close(chan); + continue; + } cell = NULL; /* @@ -2943,22 +2954,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) return n_flushed; } -#if 0 -/** Indicate the current preferred cap for middle circuits; zero disables - * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but - * the logic in append_cell_to_circuit_queue() is written to be correct - * if we want to base it on a consensus param or something that might change - * in the future. - */ -static int -get_max_middle_cells(void) -{ - return ORCIRC_MAX_MIDDLE_CELLS; -} -#endif /* 0 */ - /** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b> - * transmitting in <b>direction</b>. */ + * transmitting in <b>direction</b>. + * + * The given <b>cell</b> is copied over the circuit queue so the caller must + * cleanup the memory. + * + * This function is part of the fast path. */ void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, @@ -2967,10 +2969,6 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, or_circuit_t *orcirc = NULL; cell_queue_t *queue; int streams_blocked; -#if 0 - uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells; -#endif - int exitward; if (circ->marked_for_close) return; @@ -2985,93 +2983,14 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, streams_blocked = circ->streams_blocked_on_p_chan; } - /* - * Disabling this for now because of a possible guard discovery attack - */ -#if 0 - /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */ - if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) { - orcirc = TO_OR_CIRCUIT(circ); - if (orcirc->p_chan) { - /* We are a middle circuit if we have both n_chan and p_chan */ - /* We'll need to know the current preferred maximum */ - tgt_max_middle_cells = get_max_middle_cells(); - if (tgt_max_middle_cells > 0) { - /* Do we need to initialize middle_max_cells? */ - if (orcirc->max_middle_cells == 0) { - orcirc->max_middle_cells = tgt_max_middle_cells; - } else { - if (tgt_max_middle_cells > orcirc->max_middle_cells) { - /* If we want to increase the cap, we can do so right away */ - orcirc->max_middle_cells = tgt_max_middle_cells; - } else if (tgt_max_middle_cells < orcirc->max_middle_cells) { - /* - * If we're shrinking the cap, we can't shrink past either queue; - * compare tgt_max_middle_cells rather than tgt_max_middle_cells * - * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough - * to generate spurious warnings, either. - */ - n_len = circ->n_chan_cells.n; - p_len = orcirc->p_chan_cells.n; - tmp = tgt_max_middle_cells; - if (tmp < n_len) tmp = n_len; - if (tmp < p_len) tmp = p_len; - orcirc->max_middle_cells = tmp; - } - /* else no change */ - } - } else { - /* tgt_max_middle_cells == 0 indicates we should disable the cap */ - orcirc->max_middle_cells = 0; - } - - /* Now we know orcirc->max_middle_cells is set correctly */ - if (orcirc->max_middle_cells > 0) { - hard_max_middle_cells = - (uint32_t)(((double)orcirc->max_middle_cells) * - ORCIRC_MAX_MIDDLE_KILL_THRESH); - - if ((unsigned)queue->n + 1 >= hard_max_middle_cells) { - /* Queueing this cell would put queue over the kill theshold */ - log_warn(LD_CIRC, - "Got a cell exceeding the hard cap of %u in the " - "%s direction on middle circ ID %u on chan ID " - U64_FORMAT "; killing the circuit.", - hard_max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - return; - } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) { - /* Only use ==, not >= for this test so we don't spam the log */ - log_warn(LD_CIRC, - "While trying to queue a cell, reached the soft cap of %u " - "in the %s direction on middle circ ID %u " - "on chan ID " U64_FORMAT ".", - orcirc->max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - } - } - } - } -#endif /* 0 */ - + /* Very important that we copy to the circuit queue because all calls to + * this function use the stack for the cell memory. */ cell_queue_append_packed_copy(circ, queue, exitward, cell, chan->wide_circ_ids, 1); + /* Check and run the OOM if needed. */ if (PREDICT_UNLIKELY(cell_queues_check_size())) { - /* We ran the OOM handler */ + /* We ran the OOM handler which might have closed this circuit. */ if (circ->marked_for_close) return; } diff --git a/src/or/relay.h b/src/or/relay.h index 4cc1a0fbdb..f0fa7e9870 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -17,6 +17,7 @@ extern uint64_t stats_n_relay_cells_delivered; int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction); +size_t cell_queues_get_total_allocation(void); void relay_header_pack(uint8_t *dest, const relay_header_t *src); void relay_header_unpack(relay_header_t *dest, const uint8_t *src); @@ -51,7 +52,9 @@ size_t packed_cell_mem_cost(void); int have_been_under_memory_pressure(void); /* For channeltls.c */ -void packed_cell_free(packed_cell_t *cell); +void packed_cell_free_(packed_cell_t *cell); +#define packed_cell_free(cell) \ + FREE_AND_NULL(packed_cell_t, packed_cell_free_, (cell)) void cell_queue_init(cell_queue_t *queue); void cell_queue_clear(cell_queue_t *queue); @@ -101,7 +104,9 @@ typedef struct address_ttl_s { char *hostname; int ttl; } address_ttl_t; -STATIC void address_ttl_free(address_ttl_t *addr); +STATIC void address_ttl_free_(address_ttl_t *addr); +#define address_ttl_free(addr) \ + FREE_AND_NULL(address_ttl_t, address_ttl_free_, (addr)) STATIC int resolved_cell_parse(const cell_t *cell, const relay_header_t *rh, smartlist_t *addresses_out, int *errcode_out); STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn, @@ -110,7 +115,6 @@ STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn, STATIC packed_cell_t *packed_cell_new(void); STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue); STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue); -STATIC size_t cell_queues_get_total_allocation(void); STATIC int cell_queues_check_size(void); #endif /* defined(RELAY_PRIVATE) */ diff --git a/src/or/rendcache.c b/src/or/rendcache.c index b98b2bccfa..d27e1c293f 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -43,12 +43,12 @@ STATIC digestmap_t *rend_cache_v2_dir = NULL; * ID, that were NOT present in the descriptor are removed from this cache. * Which means that if at least one IP was not in this cache, thus usuable, * it's considered a new descriptor so we keep it. Else, if all IPs were in - * this cache, we discard the descriptor as it's considered unsuable. + * this cache, we discard the descriptor as it's considered unusable. * * Once a descriptor is removed from the rend cache or expires, the entry * in this cache is also removed for the service ID. * - * This scheme allows us to not realy on the descriptor's timestamp (which + * This scheme allows us to not rely on the descriptor's timestamp (which * is rounded down to the hour) to know if we have a newer descriptor. We * only rely on the usability of intro points from an internal state. */ STATIC strmap_t *rend_cache_failure = NULL; @@ -120,7 +120,7 @@ rend_cache_increment_allocation(size_t n) /** Helper: free a rend cache failure intro object. */ STATIC void -rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) +rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t *entry) { if (entry == NULL) { return; @@ -129,9 +129,9 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) } static void -rend_cache_failure_intro_entry_free_(void *entry) +rend_cache_failure_intro_entry_free_void(void *entry) { - rend_cache_failure_intro_entry_free(entry); + rend_cache_failure_intro_entry_free_(entry); } /** Allocate a rend cache failure intro object and return it. <b>failure</b> @@ -147,7 +147,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) /** Helper: free a rend cache failure object. */ STATIC void -rend_cache_failure_entry_free(rend_cache_failure_t *entry) +rend_cache_failure_entry_free_(rend_cache_failure_t *entry) { if (entry == NULL) { return; @@ -155,7 +155,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /* Free and remove every intro failure object. */ digestmap_free(entry->intro_failures, - rend_cache_failure_intro_entry_free_); + rend_cache_failure_intro_entry_free_void); tor_free(entry); } @@ -163,9 +163,9 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), * which requires a function pointer whose argument is void*). */ STATIC void -rend_cache_failure_entry_free_(void *entry) +rend_cache_failure_entry_free_void(void *entry) { - rend_cache_failure_entry_free(entry); + rend_cache_failure_entry_free_(entry); } /** Allocate a rend cache failure object and return it. This function can @@ -201,7 +201,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc) /** Helper: free storage held by a single service descriptor cache entry. */ STATIC void -rend_cache_entry_free(rend_cache_entry_t *e) +rend_cache_entry_free_(rend_cache_entry_t *e) { if (!e) return; @@ -217,19 +217,19 @@ rend_cache_entry_free(rend_cache_entry_t *e) /** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which * requires a function pointer whose argument is void*). */ static void -rend_cache_entry_free_(void *p) +rend_cache_entry_free_void(void *p) { - rend_cache_entry_free(p); + rend_cache_entry_free_(p); } /** Free all storage held by the service descriptor cache. */ void rend_cache_free_all(void) { - strmap_free(rend_cache, rend_cache_entry_free_); - digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); - strmap_free(rend_cache_local_service, rend_cache_entry_free_); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); + digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_void); + strmap_free(rend_cache_local_service, rend_cache_entry_free_void); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); rend_cache = NULL; rend_cache_v2_dir = NULL; rend_cache_local_service = NULL; @@ -304,7 +304,7 @@ rend_cache_purge(void) { if (rend_cache) { log_info(LD_REND, "Purging HS v2 descriptor cache"); - strmap_free(rend_cache, rend_cache_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); } rend_cache = strmap_new(); } @@ -316,7 +316,7 @@ rend_cache_failure_purge(void) { if (rend_cache_failure) { log_info(LD_REND, "Purging HS v2 failure cache"); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); } rend_cache_failure = strmap_new(); } diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 5b13eadfa1..8b6fd5b671 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -36,7 +36,7 @@ typedef struct rend_cache_entry_t { /* Introduction point failure type. */ typedef struct rend_cache_failure_intro_t { - /* When this intro point failure occured thus we allocated this object and + /* When this intro point failure occurred thus we allocated this object and * cache it. */ time_t created_ts; rend_intro_point_failure_t failure_type; @@ -90,10 +90,18 @@ void rend_cache_increment_allocation(size_t n); #ifdef RENDCACHE_PRIVATE STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e); -STATIC void rend_cache_entry_free(rend_cache_entry_t *e); -STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t - *entry); -STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry); +STATIC void rend_cache_entry_free_(rend_cache_entry_t *e); +#define rend_cache_entry_free(e) \ + FREE_AND_NULL(rend_cache_entry_t, rend_cache_entry_free_, (e)) +STATIC void rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t + *entry); +#define rend_cache_failure_intro_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_intro_t, \ + rend_cache_failure_intro_entry_free_, (e)) +STATIC void rend_cache_failure_entry_free_(rend_cache_failure_t *entry); +#define rend_cache_failure_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_t, \ + rend_cache_failure_entry_free_, (e)) STATIC int cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, rend_cache_failure_intro_t @@ -108,7 +116,7 @@ STATIC void cache_failure_intro_add(const uint8_t *identity, STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, const char *service_id); -STATIC void rend_cache_failure_entry_free_(void *entry); +STATIC void rend_cache_failure_entry_free_void(void *entry); #ifdef TOR_UNIT_TESTS extern strmap_t *rend_cache; diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3274819241..57815815b9 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -459,7 +459,8 @@ directory_get_from_hs_dir(const char *desc_id, hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32); if (!hs_dir) { /* No suitable hs dir can be found, stop right now. */ - control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR"); + control_event_hsv2_descriptor_failed(rend_query, NULL, + "QUERY_NO_HSDIR"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, NULL, NULL); return 0; @@ -482,7 +483,7 @@ directory_get_from_hs_dir(const char *desc_id, REND_DESC_COOKIE_LEN, 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); - control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); + control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, hsdir_fp, NULL); return 0; @@ -515,9 +516,10 @@ directory_get_from_hs_dir(const char *desc_id, (rend_data->auth_type == REND_NO_AUTH ? "[none]" : escaped_safe_str_client(descriptor_cookie_base64)), routerstatus_describe(hs_dir)); - control_event_hs_descriptor_requested(rend_query, + control_event_hs_descriptor_requested(rend_data->onion_address, + rend_data->auth_type, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); return 1; } @@ -569,7 +571,7 @@ fetch_v2_desc_by_descid(const char *desc_id, /** Fetch a v2 descriptor using the onion address in the given query object. * This will compute the descriptor id for each replicas and fetch it on the - * given hsdir(s) if any or the responsible ones that are choosen + * given hsdir(s) if any or the responsible ones that are chosen * automatically. * * On success, 1 is returned. If no hidden service is left to ask, return 0. @@ -1102,18 +1104,22 @@ rend_client_lookup_service_authorization(const char *onion_address) return strmap_get(auth_hid_servs, onion_address); } +#define rend_service_authorization_free(val) \ + FREE_AND_NULL(rend_service_authorization_t, \ + rend_service_authorization_free_, (val)) + /** Helper: Free storage held by rend_service_authorization_t. */ static void -rend_service_authorization_free(rend_service_authorization_t *auth) +rend_service_authorization_free_(rend_service_authorization_t *auth) { tor_free(auth); } /** Helper for strmap_free. */ static void -rend_service_authorization_strmap_item_free(void *service_auth) +rend_service_authorization_free_void(void *service_auth) { - rend_service_authorization_free(service_auth); + rend_service_authorization_free_(service_auth); } /** Release all the storage held in auth_hid_servs. @@ -1124,7 +1130,7 @@ rend_service_authorization_free_all(void) if (!auth_hid_servs) { return; } - strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free); + strmap_free(auth_hid_servs, rend_service_authorization_free_void); auth_hid_servs = NULL; } @@ -1199,7 +1205,7 @@ rend_parse_service_authorization(const or_options_t *options, rend_service_authorization_free_all(); auth_hid_servs = parsed; } else { - strmap_free(parsed, rend_service_authorization_strmap_item_free); + strmap_free(parsed, rend_service_authorization_free_void); } return res; } diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 458a90058f..230da4be5c 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -37,7 +37,7 @@ rend_cmp_service_ids(const char *one, const char *two) /** Free the storage held by the service descriptor <b>desc</b>. */ void -rend_service_descriptor_free(rend_service_descriptor_t *desc) +rend_service_descriptor_free_(rend_service_descriptor_t *desc) { if (!desc) return; @@ -419,7 +419,7 @@ rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc) /** Free the storage held by an encoded v2 service descriptor. */ void -rend_encoded_v2_service_descriptor_free( +rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc) { if (!desc) @@ -430,7 +430,7 @@ rend_encoded_v2_service_descriptor_free( /** Free the storage held by an introduction point info. */ void -rend_intro_point_free(rend_intro_point_t *intro) +rend_intro_point_free_(rend_intro_point_t *intro) { if (!intro) return; diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index c35d7272fd..1ed0f62609 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -24,11 +24,19 @@ void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, int command, size_t length, const uint8_t *payload); -void rend_service_descriptor_free(rend_service_descriptor_t *desc); +void rend_service_descriptor_free_(rend_service_descriptor_t *desc); +#define rend_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_service_descriptor_t, rend_service_descriptor_free_, \ + (desc)) int rend_get_service_id(crypto_pk_t *pk, char *out); -void rend_encoded_v2_service_descriptor_free( +void rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc); -void rend_intro_point_free(rend_intro_point_t *intro); +#define rend_encoded_v2_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_encoded_v2_service_descriptor_t, \ + rend_encoded_v2_service_descriptor_free_, (desc)) +void rend_intro_point_free_(rend_intro_point_t *intro); +#define rend_intro_point_free(intro) \ + FREE_AND_NULL(rend_intro_point_t, rend_intro_point_free_, (intro)) int rend_valid_v2_service_id(const char *query); int rend_valid_descriptor_id(const char *query); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 2c5c5840a1..cc22429777 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -157,7 +157,7 @@ rend_num_services(void) /** Helper: free storage held by a single service authorized client entry. */ void -rend_authorized_client_free(rend_authorized_client_t *client) +rend_authorized_client_free_(rend_authorized_client_t *client) { if (!client) return; @@ -172,15 +172,15 @@ rend_authorized_client_free(rend_authorized_client_t *client) /** Helper for strmap_free. */ static void -rend_authorized_client_strmap_item_free(void *authorized_client) +rend_authorized_client_free_void(void *authorized_client) { - rend_authorized_client_free(authorized_client); + rend_authorized_client_free_(authorized_client); } /** Release the storage held by <b>service</b>. */ STATIC void -rend_service_free(rend_service_t *service) +rend_service_free_(rend_service_t *service) { if (!service) return; @@ -470,7 +470,7 @@ rend_service_parse_port_config(const char *string, const char *sep, /** Release all storage held in a rend_service_port_config_t. */ void -rend_service_port_config_free(rend_service_port_config_t *p) +rend_service_port_config_free_(rend_service_port_config_t *p) { tor_free(p); } @@ -847,9 +847,9 @@ rend_config_service(const config_line_t *line_, * after calling this routine, and may assume that correct cleanup has * been done on failure. * - * Return an appropriate rend_service_add_ephemeral_status_t. + * Return an appropriate hs_service_add_ephemeral_status_t. */ -rend_service_add_ephemeral_status_t +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, @@ -1360,7 +1360,7 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst, } /** Derive all rend_service_t internal material based on the service's key. - * Returns 0 on sucess, -1 on failure. + * Returns 0 on success, -1 on failure. */ static int rend_service_derive_key_digests(struct rend_service_t *s) @@ -1675,7 +1675,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) memwipe(client_keys_str, 0, strlen(client_keys_str)); tor_free(client_keys_str); } - strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); + strmap_free(parsed_clients, rend_authorized_client_free_void); if (cfname) { memwipe(cfname, 0, strlen(cfname)); @@ -2224,7 +2224,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro, * rend_service_parse_intro(). */ void -rend_service_free_intro(rend_intro_cell_t *request) +rend_service_free_intro_(rend_intro_cell_t *request) { if (!request) { return; @@ -3196,7 +3196,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) /* If we already have enough introduction circuits for this service, * redefine this one as a general circuit or close it, depending. - * Substract the amount of expiring nodes here because the circuits are + * Subtract the amount of expiring nodes here because the circuits are * still opened. */ if (valid_ip_circuits > service->n_intro_points_wanted) { const or_options_t *options = get_options(); @@ -3222,7 +3222,12 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) "circuit, but we already have enough. Redefining purpose to " "general; leaving as internal."); - circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL); + if (circuit_should_use_vanguards(TO_CIRCUIT(circuit)->purpose)) { + circuit_change_purpose(TO_CIRCUIT(circuit), + CIRCUIT_PURPOSE_HS_VANGUARDS); + } else { + circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL); + } { rend_data_free(circuit->rend_data); @@ -3576,7 +3581,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, "directories to post descriptors to."); control_event_hs_descriptor_upload(service_id, "UNKNOWN", - "UNKNOWN"); + "UNKNOWN", NULL); goto done; } } @@ -3631,7 +3636,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, hs_dir->or_port); control_event_hs_descriptor_upload(service_id, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); tor_free(hs_dir_ip); /* Remember successful upload to this router for next time. */ if (!smartlist_contains_digest(successful_uploads, @@ -3728,7 +3733,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); /* Update next upload time. */ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS @@ -3759,7 +3764,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); } } @@ -4116,7 +4121,7 @@ rend_consider_services_intro_points(time_t now) n_intro_points_to_open); break; } - /* Add the choosen node to the exclusion list in order to avoid picking + /* Add the chosen node to the exclusion list in order to avoid picking * it again in the next iteration. */ smartlist_add(exclude_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); @@ -4281,7 +4286,7 @@ rend_service_dump_stats(int severity) } /** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for - * 'circ', and look up the port and address based on conn-\>port. + * <b>circ</b>, and look up the port and address based on conn-\>port. * Assign the actual conn-\>addr and conn-\>port. Return -2 on failure * for which the circuit should be closed, -1 on other failure, * or 0 for success. diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 5946e31861..cc872ab575 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -108,7 +108,7 @@ typedef struct rend_service_t { /** If true, we don't close circuits for making requests to unsupported * ports. */ int allow_unknown_ports; - /** The maximum number of simultanious streams-per-circuit that are allowed + /** The maximum number of simultaneous streams-per-circuit that are allowed * to be established, or 0 if no limit is set. */ int max_streams_per_circuit; @@ -117,7 +117,9 @@ typedef struct rend_service_t { int max_streams_close_circuit; } rend_service_t; -STATIC void rend_service_free(rend_service_t *service); +STATIC void rend_service_free_(rend_service_t *service); +#define rend_service_free(s) \ + FREE_AND_NULL(rend_service_t, rend_service_free_, (s)) STATIC char *rend_service_sos_poison_path(const rend_service_t *service); STATIC int rend_service_verify_single_onion_poison( const rend_service_t *s, @@ -160,7 +162,11 @@ int rend_service_receive_introduction(origin_circuit_t *circuit, int rend_service_decrypt_intro(rend_intro_cell_t *request, crypto_pk_t *key, char **err_msg_out); -void rend_service_free_intro(rend_intro_cell_t *request); +void rend_service_free_intro_(rend_intro_cell_t *request); +#define rend_service_free_intro(req) do { \ + rend_service_free_intro_(req); \ + (req) = NULL; \ + } while (0) rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request, size_t request_len, uint8_t type, @@ -183,20 +189,17 @@ void rend_service_init(void); rend_service_port_config_t *rend_service_parse_port_config(const char *string, const char *sep, char **err_msg_out); -void rend_service_port_config_free(rend_service_port_config_t *p); - -void rend_authorized_client_free(rend_authorized_client_t *client); - -/** Return value from rend_service_add_ephemeral. */ -typedef enum { - RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ - RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ - RSAE_ADDREXISTS = -3, /**< Onion address collision */ - RSAE_BADPRIVKEY = -2, /**< Invalid public key */ - RSAE_INTERNAL = -1, /**< Internal error */ - RSAE_OKAY = 0 /**< Service added as expected */ -} rend_service_add_ephemeral_status_t; -rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, +void rend_service_port_config_free_(rend_service_port_config_t *p); +#define rend_service_port_config_free(p) \ + FREE_AND_NULL(rend_service_port_config_t, rend_service_port_config_free_, \ + (p)) + +void rend_authorized_client_free_(rend_authorized_client_t *client); +#define rend_authorized_client_free(client) \ + FREE_AND_NULL(rend_authorized_client_t, rend_authorized_client_free_, \ + (client)) + +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, int max_streams_close_circuit, diff --git a/src/or/rephist.c b/src/or/rephist.c index 345722d8ce..43494692cb 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -110,38 +110,12 @@ uint32_t rephist_total_num=0; * 20X as much as one that ended a month ago, and routers that have had no * uptime data for about half a year will get forgotten.) */ -/** History of an OR-\>OR link. */ -typedef struct link_history_t { - /** When did we start tracking this list? */ - time_t since; - /** When did we most recently note a change to this link */ - time_t changed; - /** How many times did extending from OR1 to OR2 succeed? */ - unsigned long n_extend_ok; - /** How many times did extending from OR1 to OR2 fail? */ - unsigned long n_extend_fail; -} link_history_t; - /** History of an OR. */ typedef struct or_history_t { /** When did we start tracking this OR? */ time_t since; /** When did we most recently note a change to this OR? */ time_t changed; - /** How many times did we successfully connect? */ - unsigned long n_conn_ok; - /** How many times did we try to connect and fail?*/ - unsigned long n_conn_fail; - /** How many seconds have we been connected to this OR before - * 'up_since'? */ - unsigned long uptime; - /** How many seconds have we been unable to connect to this OR before - * 'down_since'? */ - unsigned long downtime; - /** If nonzero, we have been connected since this time. */ - time_t up_since; - /** If nonzero, we have been unable to connect since this time. */ - time_t down_since; /** The address at which we most recently connected to this OR * successfully. */ @@ -163,10 +137,6 @@ typedef struct or_history_t { time_t start_of_downtime; unsigned long weighted_uptime; unsigned long total_weighted_time; - - /** Map from hex OR2 identity digest to a link_history_t for the link - * from this OR to OR2. */ - digestmap_t *link_history_map; } or_history_t; /** @@ -232,7 +202,6 @@ get_or_history(const char* id) hist = tor_malloc_zero(sizeof(or_history_t)); rephist_total_alloc += sizeof(or_history_t); rephist_total_num++; - hist->link_history_map = digestmap_new(); hist->since = hist->changed = time(NULL); tor_addr_make_unspec(&hist->last_reached_addr); digestmap_set(history_map, id, hist); @@ -240,66 +209,16 @@ get_or_history(const char* id) return hist; } -/** Return the link_history_t for the link from the first named OR to - * the second, creating it if necessary. (ORs are identified by - * identity digest.) - */ -static link_history_t * -get_link_history(const char *from_id, const char *to_id) -{ - or_history_t *orhist; - link_history_t *lhist; - orhist = get_or_history(from_id); - if (!orhist) - return NULL; - if (tor_digest_is_zero(to_id)) - return NULL; - lhist = digestmap_get(orhist->link_history_map, to_id); - if (!lhist) { - lhist = tor_malloc_zero(sizeof(link_history_t)); - rephist_total_alloc += sizeof(link_history_t); - lhist->since = lhist->changed = time(NULL); - digestmap_set(orhist->link_history_map, to_id, lhist); - } - return lhist; -} - -/** Helper: free storage held by a single link history entry. */ -static void -free_link_history_(void *val) -{ - rephist_total_alloc -= sizeof(link_history_t); - tor_free(val); -} - /** Helper: free storage held by a single OR history entry. */ static void free_or_history(void *_hist) { or_history_t *hist = _hist; - digestmap_free(hist->link_history_map, free_link_history_); rephist_total_alloc -= sizeof(or_history_t); rephist_total_num--; tor_free(hist); } -/** Update an or_history_t object <b>hist</b> so that its uptime/downtime - * count is up-to-date as of <b>when</b>. - */ -static void -update_or_history(or_history_t *hist, time_t when) -{ - tor_assert(hist); - if (hist->up_since) { - tor_assert(!hist->down_since); - hist->uptime += (when - hist->up_since); - hist->up_since = when; - } else if (hist->down_since) { - hist->downtime += (when - hist->down_since); - hist->down_since = when; - } -} - /** Initialize the static data structures for tracking history. */ void rep_hist_init(void) @@ -309,99 +228,6 @@ rep_hist_init(void) predicted_ports_alloc(); } -/** Helper: note that we are no longer connected to the router with history - * <b>hist</b>. If <b>failed</b>, the connection failed; otherwise, it was - * closed correctly. */ -static void -mark_or_down(or_history_t *hist, time_t when, int failed) -{ - if (hist->up_since) { - hist->uptime += (when - hist->up_since); - hist->up_since = 0; - } - if (failed && !hist->down_since) { - hist->down_since = when; - } -} - -/** Helper: note that we are connected to the router with history - * <b>hist</b>. */ -static void -mark_or_up(or_history_t *hist, time_t when) -{ - if (hist->down_since) { - hist->downtime += (when - hist->down_since); - hist->down_since = 0; - } - if (!hist->up_since) { - hist->up_since = when; - } -} - -/** Remember that an attempt to connect to the OR with identity digest - * <b>id</b> failed at <b>when</b>. - */ -void -rep_hist_note_connect_failed(const char* id, time_t when) -{ - or_history_t *hist; - hist = get_or_history(id); - if (!hist) - return; - ++hist->n_conn_fail; - mark_or_down(hist, when, 1); - hist->changed = when; -} - -/** Remember that an attempt to connect to the OR with identity digest - * <b>id</b> succeeded at <b>when</b>. - */ -void -rep_hist_note_connect_succeeded(const char* id, time_t when) -{ - or_history_t *hist; - hist = get_or_history(id); - if (!hist) - return; - ++hist->n_conn_ok; - mark_or_up(hist, when); - hist->changed = when; -} - -/** Remember that we intentionally closed our connection to the OR - * with identity digest <b>id</b> at <b>when</b>. - */ -void -rep_hist_note_disconnect(const char* id, time_t when) -{ - or_history_t *hist; - hist = get_or_history(id); - if (!hist) - return; - mark_or_down(hist, when, 0); - hist->changed = when; -} - -/** Remember that our connection to the OR with identity digest - * <b>id</b> had an error and stopped working at <b>when</b>. - */ -void -rep_hist_note_connection_died(const char* id, time_t when) -{ - or_history_t *hist; - if (!id) { - /* If conn has no identity, it didn't complete its handshake, or something - * went wrong. Ignore it. - */ - return; - } - hist = get_or_history(id); - if (!hist) - return; - mark_or_down(hist, when, 1); - hist->changed = when; -} - /** We have just decided that this router with identity digest <b>id</b> is * reachable, meaning we will give it a "Running" flag for the next while. */ void @@ -538,7 +364,6 @@ rep_hist_make_router_pessimal(const char *id, time_t when) tor_assert(hist); rep_hist_note_router_unreachable(id, when); - mark_or_down(hist, when, 1); hist->weighted_run_length = 0; hist->weighted_uptime = 0; @@ -715,57 +540,17 @@ rep_hist_have_measured_enough_stability(void) return started_tracking_stability < time(NULL) - 4*60*60; } -/** Remember that we successfully extended from the OR with identity - * digest <b>from_id</b> to the OR with identity digest - * <b>to_name</b>. - */ -void -rep_hist_note_extend_succeeded(const char *from_id, const char *to_id) -{ - link_history_t *hist; - /* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */ - hist = get_link_history(from_id, to_id); - if (!hist) - return; - ++hist->n_extend_ok; - hist->changed = time(NULL); -} - -/** Remember that we tried to extend from the OR with identity digest - * <b>from_id</b> to the OR with identity digest <b>to_name</b>, but - * failed. - */ -void -rep_hist_note_extend_failed(const char *from_id, const char *to_id) -{ - link_history_t *hist; - /* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */ - hist = get_link_history(from_id, to_id); - if (!hist) - return; - ++hist->n_extend_fail; - hist->changed = time(NULL); -} - /** Log all the reliability data we have remembered, with the chosen * severity. */ void rep_hist_dump_stats(time_t now, int severity) { - digestmap_iter_t *lhist_it; digestmap_iter_t *orhist_it; - const char *name1, *name2, *digest1, *digest2; + const char *name1, *digest1; char hexdigest1[HEX_DIGEST_LEN+1]; - char hexdigest2[HEX_DIGEST_LEN+1]; or_history_t *or_history; - link_history_t *link_history; - void *or_history_p, *link_history_p; - double uptime; - char buffer[2048]; - size_t len; - int ret; - unsigned long upt, downt; + void *or_history_p; const node_t *node; rep_history_clean(now - get_options()->RephistTrackTime); @@ -785,52 +570,12 @@ rep_hist_dump_stats(time_t now, int severity) else name1 = "(unknown)"; base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN); - update_or_history(or_history, now); - upt = or_history->uptime; - downt = or_history->downtime; s = get_stability(or_history, now); stability = (long)s; - if (upt+downt) { - uptime = ((double)upt) / (upt+downt); - } else { - uptime=1.0; - } tor_log(severity, LD_HIST, - "OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%); " - "wmtbf %lu:%02lu:%02lu", + "OR %s [%s]: wmtbf %lu:%02lu:%02lu", name1, hexdigest1, - or_history->n_conn_ok, or_history->n_conn_fail+or_history->n_conn_ok, - upt, upt+downt, uptime*100.0, stability/3600, (stability/60)%60, stability%60); - - if (!digestmap_isempty(or_history->link_history_map)) { - strlcpy(buffer, " Extend attempts: ", sizeof(buffer)); - len = strlen(buffer); - for (lhist_it = digestmap_iter_init(or_history->link_history_map); - !digestmap_iter_done(lhist_it); - lhist_it = digestmap_iter_next(or_history->link_history_map, - lhist_it)) { - digestmap_iter_get(lhist_it, &digest2, &link_history_p); - if ((node = node_get_by_id(digest2)) && node_get_nickname(node)) - name2 = node_get_nickname(node); - else - name2 = "(unknown)"; - - link_history = (link_history_t*) link_history_p; - - base16_encode(hexdigest2, sizeof(hexdigest2), digest2, DIGEST_LEN); - ret = tor_snprintf(buffer+len, 2048-len, "%s [%s](%ld/%ld); ", - name2, - hexdigest2, - link_history->n_extend_ok, - link_history->n_extend_ok+link_history->n_extend_fail); - if (ret<0) - break; - else - len += ret; - } - tor_log(severity, LD_HIST, "%s", buffer); - } } } @@ -842,10 +587,9 @@ rep_history_clean(time_t before) { int authority = authdir_mode(get_options()); or_history_t *or_history; - link_history_t *link_history; - void *or_history_p, *link_history_p; - digestmap_iter_t *orhist_it, *lhist_it; - const char *d1, *d2; + void *or_history_p; + digestmap_iter_t *orhist_it; + const char *d1; orhist_it = digestmap_iter_init(history_map); while (!digestmap_iter_done(orhist_it)) { @@ -862,19 +606,6 @@ rep_history_clean(time_t before) free_or_history(or_history); continue; } - for (lhist_it = digestmap_iter_init(or_history->link_history_map); - !digestmap_iter_done(lhist_it); ) { - digestmap_iter_get(lhist_it, &d2, &link_history_p); - link_history = link_history_p; - if (link_history->changed < before) { - lhist_it = digestmap_iter_next_rmv(or_history->link_history_map, - lhist_it); - rephist_total_alloc -= sizeof(link_history_t); - tor_free(link_history); - continue; - } - lhist_it = digestmap_iter_next(or_history->link_history_map,lhist_it); - } orhist_it = digestmap_iter_next(history_map, orhist_it); } } @@ -1358,9 +1089,12 @@ bw_array_new(void) return b; } +#define bw_array_free(val) \ + FREE_AND_NULL(bw_array_t, bw_array_free_, (val)) + /** Free storage held by bandwidth array <b>b</b>. */ static void -bw_array_free(bw_array_t *b) +bw_array_free_(bw_array_t *b) { if (!b) { return; @@ -1885,7 +1619,7 @@ predicted_ports_init(void) * be used. */ static void -predicted_ports_free(void) +predicted_ports_free_all(void) { rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(predicted_port_t); @@ -2733,7 +2467,7 @@ rep_hist_desc_stats_write(time_t now) } /** Called to note that we've served a given descriptor (by - * digest). Incrememnts the count of descriptors served, and the number + * digest). Increments the count of descriptors served, and the number * of times we've served this descriptor. */ void rep_hist_note_desc_served(const char * desc) @@ -2830,7 +2564,7 @@ HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, /* DOCDOC bidi_map_free */ static void -bidi_map_free(void) +bidi_map_free_all(void) { bidi_map_entry_t **ptr, **next, *ent; for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { @@ -2850,7 +2584,7 @@ rep_hist_reset_conn_stats(time_t now) mostly_read = 0; mostly_written = 0; both_read_and_written = 0; - bidi_map_free(); + bidi_map_free_all(); } /** Stop collecting connection stats in a way that we can re-start doing @@ -3039,9 +2773,12 @@ hs_stats_new(void) return new_hs_stats; } +#define hs_stats_free(val) \ + FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val)) + /** Free an hs_stats_t structure. */ static void -hs_stats_free(hs_stats_t *victim_hs_stats) +hs_stats_free_(hs_stats_t *victim_hs_stats) { if (!victim_hs_stats) { return; @@ -3453,8 +3190,8 @@ rep_hist_free_all(void) tor_free(exit_bytes_read); tor_free(exit_bytes_written); tor_free(exit_streams); - predicted_ports_free(); - bidi_map_free(); + predicted_ports_free_all(); + bidi_map_free_all(); if (circuits_for_buffer_stats) { SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s, diff --git a/src/or/rephist.h b/src/or/rephist.h index 496e366865..5072721592 100644 --- a/src/or/rephist.h +++ b/src/or/rephist.h @@ -13,13 +13,6 @@ #define TOR_REPHIST_H void rep_hist_init(void); -void rep_hist_note_connect_failed(const char* nickname, time_t when); -void rep_hist_note_connect_succeeded(const char* nickname, time_t when); -void rep_hist_note_disconnect(const char* nickname, time_t when); -void rep_hist_note_connection_died(const char* nickname, time_t when); -void rep_hist_note_extend_succeeded(const char *from_name, - const char *to_name); -void rep_hist_note_extend_failed(const char *from_name, const char *to_name); void rep_hist_dump_stats(time_t now, int severity); void rep_hist_note_bytes_read(size_t num_bytes, time_t when); void rep_hist_note_bytes_written(size_t num_bytes, time_t when); diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 3d42deb90a..a9a6709937 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -26,9 +26,8 @@ /** Free the replaycache r and all of its entries. */ - void -replaycache_free(replaycache_t *r) +replaycache_free_(replaycache_t *r) { if (!r) { log_info(LD_BUG, "replaycache_free() called on NULL"); @@ -44,7 +43,6 @@ replaycache_free(replaycache_t *r) * for entries to age out and interval is the time after which the cache * should be scrubbed for old entries. */ - replaycache_t * replaycache_new(time_t horizon, time_t interval) { @@ -72,9 +70,8 @@ replaycache_new(time_t horizon, time_t interval) return r; } -/** See documentation for replaycache_add_and_test() +/** See documentation for replaycache_add_and_test(). */ - STATIC int replaycache_add_and_test_internal( time_t present, replaycache_t *r, const void *data, size_t len, @@ -136,9 +133,8 @@ replaycache_add_and_test_internal( return rv; } -/** See documentation for replaycache_scrub_if_needed() +/** See documentation for replaycache_scrub_if_needed(). */ - STATIC void replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) { @@ -186,7 +182,6 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) * and the function will return 1 if it was already seen within the cache's * horizon, or 0 otherwise. */ - int replaycache_add_and_test(replaycache_t *r, const void *data, size_t len) { @@ -196,7 +191,6 @@ replaycache_add_and_test(replaycache_t *r, const void *data, size_t len) /** Like replaycache_add_and_test(), but if it's a hit also return the time * elapsed since this digest was last seen. */ - int replaycache_add_test_and_elapsed( replaycache_t *r, const void *data, size_t len, time_t *elapsed) @@ -207,7 +201,6 @@ replaycache_add_test_and_elapsed( /** Scrub aged entries out of r if sufficiently long has elapsed since r was * last scrubbed. */ - void replaycache_scrub_if_needed(replaycache_t *r) { diff --git a/src/or/replaycache.h b/src/or/replaycache.h index 1cae3497ae..81a8d907fd 100644 --- a/src/or/replaycache.h +++ b/src/or/replaycache.h @@ -33,7 +33,9 @@ struct replaycache_s { /* replaycache_t free/new */ -void replaycache_free(replaycache_t *r); +void replaycache_free_(replaycache_t *r); +#define replaycache_free(r) \ + FREE_AND_NULL(replaycache_t, replaycache_free_, (r)) replaycache_t * replaycache_new(time_t horizon, time_t interval); #ifdef REPLAYCACHE_PRIVATE diff --git a/src/or/router.c b/src/or/router.c index 010ee339a0..9c053cad46 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -174,7 +174,7 @@ expire_old_onion_keys(void) tor_mutex_release(key_lock); - fname = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old onion key file %s: %s", @@ -183,7 +183,7 @@ expire_old_onion_keys(void) } tor_free(fname); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old ntor onion key file %s: %s", @@ -233,7 +233,7 @@ ntor_key_map_free_helper(void *arg) } /** Release all storage from a keymap returned by construct_ntor_key_map. */ void -ntor_key_map_free(di_digest256_map_t *map) +ntor_key_map_free_(di_digest256_map_t *map) { if (!map) return; @@ -378,8 +378,8 @@ rotate_onion_key(void) or_state_t *state = get_or_state(); curve25519_keypair_t new_curve25519_keypair; time_t now; - fname = get_datadir_fname2("keys", "secret_onion_key"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key"); + fname_prev = get_keydir_fname("secret_onion_key.old"); /* There isn't much point replacing an old key with an empty file */ if (file_status(fname) == FN_FILE) { if (replace_file(fname, fname_prev)) @@ -399,8 +399,8 @@ rotate_onion_key(void) } tor_free(fname); tor_free(fname_prev); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor"); + fname_prev = get_keydir_fname("secret_onion_key_ntor.old"); if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0) goto error; /* There isn't much point replacing an old key with an empty file */ @@ -624,7 +624,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, crypto_pk_t *signing_key = NULL; authority_cert_t *parsed = NULL; - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_signing_key" : "authority_signing_key"); signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { @@ -632,7 +632,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, goto done; } tor_free(fname); - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_certificate" : "authority_certificate"); cert = read_file_to_str(fname, 0, NULL); if (!cert) { @@ -932,22 +932,9 @@ init_keys(void) } if (init_keys_common() < 0) return -1; - /* Make sure DataDirectory exists, and is private. */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - return -1; - } - /* Check the key directory. */ - keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); + + if (create_keys_directory(options) < 0) return -1; - } - tor_free(keydir); /* 1a. Read v3 directory authority key/cert information. */ memset(v3_digest, 0, sizeof(v3_digest)); @@ -971,7 +958,7 @@ init_keys(void) } /* 1b. Read identity key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_id_key"); + keydir = get_keydir_fname("secret_id_key"); log_info(LD_GENERAL,"Reading/making identity key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -999,7 +986,7 @@ init_keys(void) return -1; /* 2. Read onion key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_onion_key"); + keydir = get_keydir_fname("secret_onion_key"); log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -1024,7 +1011,7 @@ init_keys(void) } } - keydir = get_datadir_fname2("keys", "secret_onion_key.old"); + keydir = get_keydir_fname("secret_onion_key.old"); if (!lastonionkey && file_status(keydir) == FN_FILE) { /* Load keys from non-empty files only. * Missing old keys won't be replaced with freshly generated keys. */ @@ -1037,14 +1024,14 @@ init_keys(void) { /* 2b. Load curve25519 onion keys. */ int r; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor"); + keydir = get_keydir_fname("secret_onion_key_ntor"); r = init_curve25519_keypair_from_file(&curve25519_onion_key, keydir, 1, LOG_ERR, "onion"); tor_free(keydir); if (r<0) return -1; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + keydir = get_keydir_fname("secret_onion_key_ntor.old"); if (tor_mem_is_zero((const char *) last_curve25519_onion_key.pubkey.public_key, CURVE25519_PUBKEY_LEN) && @@ -3698,6 +3685,7 @@ router_free_all(void) crypto_pk_free(lastonionkey); crypto_pk_free(server_identitykey); crypto_pk_free(client_identitykey); + tor_mutex_free(key_lock); routerinfo_free(desc_routerinfo); extrainfo_free(desc_extrainfo); diff --git a/src/or/router.h b/src/or/router.h index 3351400911..696e983662 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -36,7 +36,9 @@ int get_onion_key_lifetime(void); int get_onion_key_grace_period(void); di_digest256_map_t *construct_ntor_key_map(void); -void ntor_key_map_free(di_digest256_map_t *map); +void ntor_key_map_free_(di_digest256_map_t *map); +#define ntor_key_map_free(map) \ + FREE_AND_NULL(di_digest256_map_t, ntor_key_map_free_, (map)) int router_initialize_tls_context(void); int init_keys(void); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index f0973044b5..1933aaf4b6 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -719,7 +719,7 @@ load_ed_keys(const or_options_t *options, time_t now) /* First try to get the signing key to see how it is. */ { char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); sign = ed_key_init_from_file( fname, INIT_ED_KEY_NEEDCERT| @@ -814,26 +814,15 @@ load_ed_keys(const or_options_t *options, time_t now) flags |= INIT_ED_KEY_TRY_ENCRYPTED; /* Check/Create the key directory */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - goto err; - } - char *fname = get_datadir_fname("keys"); - if (check_private_dir(fname, CPD_CREATE, options->User) < 0) { - log_err(LD_OR, "Problem creating/checking key directory %s", fname); - tor_free(fname); - goto err; - } - tor_free(fname); + if (create_keys_directory(options) < 0) + return -1; + + char *fname; if (options->master_key_fname) { fname = tor_strdup(options->master_key_fname); flags |= INIT_ED_KEY_EXPLICIT_FNAME; } else { - fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id"); + fname = options_get_keydir_fname(options, "ed25519_master_id"); } id = ed_key_init_from_file( fname, @@ -853,8 +842,8 @@ load_ed_keys(const or_options_t *options, time_t now) id = tor_malloc_zero(sizeof(*id)); memcpy(&id->pubkey, &check_signing_cert->signing_key, sizeof(ed25519_public_key_t)); - fname = options_get_datadir_fname2(options, "keys", - "ed25519_master_id_public_key"); + fname = options_get_keydir_fname(options, + "ed25519_master_id_public_key"); if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { log_warn(LD_OR, "Error while attempting to write master public key " "to disk"); @@ -899,7 +888,7 @@ load_ed_keys(const or_options_t *options, time_t now) INIT_ED_KEY_NEEDCERT| INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); ed25519_keypair_free(sign); tor_cert_free(sign_cert); sign = ed_key_init_from_file(fname, @@ -1190,7 +1179,7 @@ log_master_signing_key_cert_expiration(const or_options_t *options) int failed = 0; time_t now = approx_time(); - fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert"); + fn = options_get_keydir_fname(options, "ed25519_signing_cert"); /* Try to grab our cached copy of the key. */ signing_key = get_master_signing_key_cert(); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index af4f67dc12..696bb454ff 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -143,6 +143,10 @@ DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t) #define DSMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \ valvar) +#define eimap_free(map, fn) MAP_FREE_AND_NULL(eimap, (map), (fn)) +#define rimap_free(map, fn) MAP_FREE_AND_NULL(rimap, (map), (fn)) +#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn)) +#define sdmap_free(map, fn) MAP_FREE_AND_NULL(sdmap, (map), (fn)) /* Forward declaration for cert_list_t */ typedef struct cert_list_t cert_list_t; @@ -159,7 +163,6 @@ static const routerstatus_t *router_pick_dirserver_generic( smartlist_t *sourcelist, dirinfo_type_t type, int flags); static void mark_all_dirservers_up(smartlist_t *server_list); -static void dir_server_free(dir_server_t *ds); static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static const char *signed_descriptor_get_body_impl( const signed_descriptor_t *desc, @@ -232,7 +235,7 @@ get_n_authorities(dirinfo_type_t type) return n; } -/** Initialise schedule, want_authority, and increment on in the download +/** Initialise schedule, want_authority, and increment_on in the download * status dlstatus, then call download_status_reset() on it. * It is safe to call this function or download_status_reset() multiple times * on a new dlstatus. But it should *not* be called after a dlstatus has been @@ -364,7 +367,7 @@ list_authority_ids_with_downloads, (void)) smartlist_add(ids, tmp); } } - /* else definitely no downlaods going since nothing even has a cert list */ + /* else definitely no downloads going since nothing even has a cert list */ return ids; } @@ -443,9 +446,12 @@ download_status_for_authority_id_and_sk,(const char *id_digest, return dl; } +#define cert_list_free(val) \ + FREE_AND_NULL(cert_list_t, cert_list_free_, (val)) + /** Release all space held by a cert_list_t */ static void -cert_list_free(cert_list_t *cl) +cert_list_free_(cert_list_t *cl) { if (!cl) return; @@ -459,9 +465,9 @@ cert_list_free(cert_list_t *cl) /** Wrapper for cert_list_free so we can pass it to digestmap_free */ static void -cert_list_free_(void *cl) +cert_list_free_void(void *cl) { - cert_list_free(cl); + cert_list_free_(cl); } /** Reload the cached v3 key certificates from the cached-certs file in @@ -473,7 +479,7 @@ trusted_dirs_reload_certs(void) char *contents; int r; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!contents) @@ -662,7 +668,7 @@ trusted_dirs_flush_certs_to_disk(void) }); } DIGESTMAP_FOREACH_END; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); if (write_chunks_to_file(filename, chunks, 0, 0)) { log_warn(LD_FS, "Error writing certificates to disk."); } @@ -1164,7 +1170,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, } SMARTLIST_FOREACH_END(voter); } - /* Bridge clients look up the node for the dir_hint */ + /* Bridge clients look up the node for the dir_hint */ const node_t *node = NULL; /* All clients, including bridge clients, look up the routerstatus for the * dir_hint */ @@ -1339,7 +1345,7 @@ static int signed_desc_append_to_journal(signed_descriptor_t *desc, desc_store_t *store) { - char *fname = get_datadir_fname_suffix(store->fname_base, ".new"); + char *fname = get_cachedir_fname_suffix(store->fname_base, ".new"); const char *body = signed_descriptor_get_body_impl(desc,1); size_t len = desc->signed_descriptor_len + desc->annotations_len; @@ -1410,8 +1416,8 @@ router_rebuild_store(int flags, desc_store_t *store) log_info(LD_DIR, "Rebuilding %s cache", store->description); - fname = get_datadir_fname(store->fname_base); - fname_tmp = get_datadir_fname_suffix(store->fname_base, ".tmp"); + fname = get_cachedir_fname(store->fname_base); + fname_tmp = get_cachedir_fname_suffix(store->fname_base, ".tmp"); chunk_list = smartlist_new(); @@ -1508,7 +1514,7 @@ router_rebuild_store(int flags, desc_store_t *store) } SMARTLIST_FOREACH_END(sd); tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); write_str_to_file(fname, "", 1); r = 0; @@ -1538,7 +1544,7 @@ router_reload_router_list_impl(desc_store_t *store) int extrainfo = (store->type == EXTRAINFO_STORE); store->journal_len = store->store_len = 0; - fname = get_datadir_fname(store->fname_base); + fname = get_cachedir_fname(store->fname_base); if (store->mmap) { /* get rid of it first */ @@ -1565,7 +1571,7 @@ router_reload_router_list_impl(desc_store_t *store) } tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); /* don't load empty files - we wouldn't get any data, even if we tried */ if (file_status(fname) == FN_FILE) contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); @@ -2646,7 +2652,7 @@ compute_weighted_bandwidths(const smartlist_t *sl, is_dir = node_is_dir(node); if (node->rs) { if (!node->rs->has_bandwidth) { - /* This should never happen, unless all the authorites downgrade + /* This should never happen, unless all the authorities downgrade * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */ if (! warned_missing_bw) { log_warn(LD_BUG, @@ -3099,7 +3105,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "We couldn't read a descriptor that is supposedly " "mmaped in our cache. Is another process running in our data " "directory? Exiting."); - exit(1); + exit(1); // XXXX bad exit: should recover. } } if (!r) /* no mmap, or not in cache. */ @@ -3113,7 +3119,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "descriptor at %p begins with unexpected string %s. " "Is another process running in our data directory? Exiting.", desc, escaped(cp)); - exit(1); + exit(1); // XXXX bad exit: should recover. } } @@ -3167,7 +3173,7 @@ router_get_routerlist(void) /** Free all storage held by <b>router</b>. */ void -routerinfo_free(routerinfo_t *router) +routerinfo_free_(routerinfo_t *router) { if (!router) return; @@ -3197,7 +3203,7 @@ routerinfo_free(routerinfo_t *router) /** Release all storage held by <b>extrainfo</b> */ void -extrainfo_free(extrainfo_t *extrainfo) +extrainfo_free_(extrainfo_t *extrainfo) { if (!extrainfo) return; @@ -3209,9 +3215,12 @@ extrainfo_free(extrainfo_t *extrainfo) tor_free(extrainfo); } +#define signed_descriptor_free(val) \ + FREE_AND_NULL(signed_descriptor_t, signed_descriptor_free_, (val)) + /** Release storage held by <b>sd</b>. */ static void -signed_descriptor_free(signed_descriptor_t *sd) +signed_descriptor_free_(signed_descriptor_t *sd) { if (!sd) return; @@ -3265,21 +3274,21 @@ signed_descriptor_from_routerinfo(routerinfo_t *ri) /** Helper: free the storage held by the extrainfo_t in <b>e</b>. */ static void -extrainfo_free_(void *e) +extrainfo_free_void(void *e) { - extrainfo_free(e); + extrainfo_free_(e); } /** Free all storage held by a routerlist <b>rl</b>. */ void -routerlist_free(routerlist_t *rl) +routerlist_free_(routerlist_t *rl) { if (!rl) return; rimap_free(rl->identity_map, NULL); sdmap_free(rl->desc_digest_map, NULL); sdmap_free(rl->desc_by_eid_map, NULL); - eimap_free(rl->extra_info_map, extrainfo_free_); + eimap_free(rl->extra_info_map, extrainfo_free_void); SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, routerinfo_free(r)); SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd, @@ -3771,7 +3780,7 @@ routerlist_free_all(void) smartlist_free(fallback_dir_servers); trusted_dir_servers = fallback_dir_servers = NULL; if (trusted_dir_certs) { - digestmap_free(trusted_dir_certs, cert_list_free_); + digestmap_free(trusted_dir_certs, cert_list_free_void); trusted_dir_certs = NULL; } } @@ -4548,7 +4557,7 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { - if (get_options()->DisableNetwork) + if (should_delay_dir_fetches(get_options(), NULL)) return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); @@ -4739,7 +4748,7 @@ dir_server_add(dir_server_t *ent) /** Free storage held in <b>cert</b>. */ void -authority_cert_free(authority_cert_t *cert) +authority_cert_free_(authority_cert_t *cert) { if (!cert) return; @@ -4751,9 +4760,12 @@ authority_cert_free(authority_cert_t *cert) tor_free(cert); } +#define dir_server_free(val) \ + FREE_AND_NULL(dir_server_t, dir_server_free_, (val)) + /** Free storage held in <b>ds</b>. */ static void -dir_server_free(dir_server_t *ds) +dir_server_free_(dir_server_t *ds) { if (!ds) return; @@ -5195,15 +5207,30 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, SMARTLIST_FOREACH_BEGIN(no_longer_old, signed_descriptor_t *, sd) { const char *msg; was_router_added_t r; + time_t tmp_cert_expiration_time; routerinfo_t *ri = routerlist_reparse_old(rl, sd); if (!ri) { log_warn(LD_BUG, "Failed to re-parse a router."); continue; } + /* need to remember for below, since add_to_routerlist may free. */ + tmp_cert_expiration_time = ri->cert_expiration_time; + r = router_add_to_routerlist(ri, &msg, 1, 0); if (WRA_WAS_OUTDATED(r)) { - log_warn(LD_DIR, "Couldn't add re-parsed router: %s", + log_warn(LD_DIR, "Couldn't add re-parsed router: %s. This isn't " + "usually a big deal, but you should make sure that your " + "clock and timezone are set correctly.", msg?msg:"???"); + if (r == ROUTER_CERTS_EXPIRED) { + char time_cons[ISO_TIME_LEN+1]; + char time_cert_expires[ISO_TIME_LEN+1]; + format_iso_time(time_cons, consensus->valid_after); + format_iso_time(time_cert_expires, tmp_cert_expiration_time); + log_warn(LD_DIR, " (I'm looking at a consensus from %s; This " + "router's certificates began expiring at %s.)", + time_cons, time_cert_expires); + } } } SMARTLIST_FOREACH_END(sd); routerlist_assert_ok(rl); @@ -5380,8 +5407,10 @@ update_extrainfo_downloads(time_t now) smartlist_free(wanted); } -/** Reset the descriptor download failure count on all routers, so that we - * can retry any long-failed routers immediately. +/** Reset the consensus and extra-info download failure count on all routers. + * When we get a new consensus, + * routers_update_status_from_consensus_networkstatus() will reset the + * download statuses on the descriptors in that consensus. */ void router_reset_descriptor_download_failures(void) @@ -5393,6 +5422,8 @@ router_reset_descriptor_download_failures(void) last_descriptor_download_attempted = 0; if (!routerlist) return; + /* We want to download *all* extra-info descriptors, not just those in + * the consensus we currently have (or are about to have) */ SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri, { download_status_reset(&ri->cache_info.ei_dl_status); @@ -5634,11 +5665,11 @@ routerstatus_version_supports_extend2_cells(const routerstatus_t *rs, return allow_unknown_versions; } - if (!rs->protocols_known) { + if (!rs->pv.protocols_known) { return allow_unknown_versions; } - return rs->supports_extend2_cells; + return rs->pv.supports_extend2_cells; } /** Assert that the internal representation of <b>rl</b> is diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 8384c7eb8c..83f4d1002f 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -96,9 +96,13 @@ MOCK_DECL(signed_descriptor_t *,extrainfo_get_by_descriptor_digest, const char *signed_descriptor_get_body(const signed_descriptor_t *desc); const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc); routerlist_t *router_get_routerlist(void); -void routerinfo_free(routerinfo_t *router); -void extrainfo_free(extrainfo_t *extrainfo); -void routerlist_free(routerlist_t *rl); +void routerinfo_free_(routerinfo_t *router); +#define routerinfo_free(router) \ + FREE_AND_NULL(routerinfo_t, routerinfo_free_, (router)) +void extrainfo_free_(extrainfo_t *extrainfo); +#define extrainfo_free(ei) FREE_AND_NULL(extrainfo_t, extrainfo_free_, (ei)) +void routerlist_free_(routerlist_t *rl); +#define routerlist_free(rl) FREE_AND_NULL(routerlist_t, routerlist_free_, (rl)) void dump_routerlist_mem_usage(int severity); void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old, time_t now); @@ -191,7 +195,9 @@ dir_server_t *fallback_dir_server_new(const tor_addr_t *addr, const char *id_digest, double weight); void dir_server_add(dir_server_t *ent); -void authority_cert_free(authority_cert_t *cert); +void authority_cert_free_(authority_cert_t *cert); +#define authority_cert_free(cert) \ + FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert)) void clear_dir_servers(void); void update_consensus_router_descriptor_downloads(time_t now, int is_vote, networkstatus_t *consensus); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 3eda024f0f..1b79a6fe24 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -1895,12 +1895,19 @@ router_parse_entry_from_string(const char *s, const char *end, } } - if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) { - router->platform = tor_strdup(tok->args[0]); - } + { + const char *version = NULL, *protocols = NULL; + if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) { + router->platform = tor_strdup(tok->args[0]); + version = tok->args[0]; + } + + if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { + router->protocol_list = tor_strdup(tok->args[0]); + protocols = tok->args[0]; + } - if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { - router->protocol_list = tor_strdup(tok->args[0]); + summarize_protover_flags(&router->pv, protocols, version); } if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) { @@ -2530,6 +2537,50 @@ routerstatus_parse_guardfraction(const char *guardfraction_str, return 0; } +/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>, + * falling back or correcting them based on <b>version</b> as appropriate. + */ +STATIC void +summarize_protover_flags(protover_summary_flags_t *out, + const char *protocols, + const char *version) +{ + tor_assert(out); + memset(out, 0, sizeof(*out)); + if (protocols) { + out->protocols_known = 1; + out->supports_extend2_cells = + protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_ed25519_link_handshake_compat = + protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_link_handshake_any = + protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_hs_intro = + protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); + out->supports_v3_hsdir = + protocol_list_supports_protocol(protocols, PRT_HSDIR, + PROTOVER_HSDIR_V3); + out->supports_v3_rendezvous_point = + protocol_list_supports_protocol(protocols, PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V3); + } + if (version && !strcmpstart(version, "Tor ")) { + if (!out->protocols_known) { + /* The version is a "Tor" version, and where there is no + * list of protocol versions that we should be looking at instead. */ + + out->supports_extend2_cells = + tor_version_as_new_as(version, "0.2.4.8-alpha"); + out->protocols_known = 1; + } else { + /* Bug #22447 forces us to filter on this version. */ + if (!tor_version_as_new_as(version, "0.3.0.8")) { + out->supports_v3_hsdir = 0; + } + } + } +} + /** Given a string at *<b>s</b>, containing a routerstatus object, and an * empty smartlist at <b>tokens</b>, parse and return the first router status * object in the string, and advance *<b>s</b> to just after the end of the @@ -2695,42 +2746,21 @@ routerstatus_parse_entry_from_string(memarea_t *area, if (consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES) rs->is_valid = 1; } - int found_protocol_list = 0; - if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { - found_protocol_list = 1; - rs->protocols_known = 1; - rs->supports_extend2_cells = - protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2); - rs->supports_ed25519_link_handshake = - protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3); - rs->supports_ed25519_hs_intro = - protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4); - rs->supports_v3_hsdir = - protocol_list_supports_protocol(tok->args[0], PRT_HSDIR, - PROTOVER_HSDIR_V3); - rs->supports_v3_rendezvous_point = - protocol_list_supports_protocol(tok->args[0], PRT_HSREND, - PROTOVER_HS_RENDEZVOUS_POINT_V3); - } - if ((tok = find_opt_by_keyword(tokens, K_V))) { - tor_assert(tok->n_args == 1); - if (!strcmpstart(tok->args[0], "Tor ") && !found_protocol_list) { - /* We only do version checks like this in the case where - * the version is a "Tor" version, and where there is no - * list of protocol versions that we should be looking at instead. */ - rs->supports_extend2_cells = - tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha"); - rs->protocols_known = 1; - } - if (!strcmpstart(tok->args[0], "Tor ") && found_protocol_list) { - /* Bug #22447 forces us to filter on this version. */ - if (!tor_version_as_new_as(tok->args[0], "0.3.0.8")) { - rs->supports_v3_hsdir = 0; + { + const char *protocols = NULL, *version = NULL; + if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { + tor_assert(tok->n_args == 1); + protocols = tok->args[0]; + } + if ((tok = find_opt_by_keyword(tokens, K_V))) { + tor_assert(tok->n_args == 1); + version = tok->args[0]; + if (vote_rs) { + vote_rs->version = tor_strdup(tok->args[0]); } } - if (vote_rs) { - vote_rs->version = tor_strdup(tok->args[0]); - } + + summarize_protover_flags(&rs->pv, protocols, version); } /* handle weighting/bandwidth info */ @@ -4027,7 +4057,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /** Return the common_digests_t that holds the digests of the * <b>flavor_name</b>-flavored networkstatus according to the detached * signatures document <b>sigs</b>, allocating a new common_digests_t as - * neeeded. */ + * needed. */ static common_digests_t * detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) { @@ -4041,7 +4071,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) /** Return the list of signatures of the <b>flavor_name</b>-flavored * networkstatus according to the detached signatures document <b>sigs</b>, - * allocating a new common_digests_t as neeeded. */ + * allocating a new common_digests_t as needed. */ static smartlist_t * detached_get_signatures(ns_detached_signatures_t *sigs, const char *flavor_name) diff --git a/src/or/routerparse.h b/src/or/routerparse.h index c4c8635f4b..418fd3acdb 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -133,6 +133,10 @@ MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest, digest_algorithm_t alg)); MOCK_DECL(STATIC int, signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len)); + +STATIC void summarize_protover_flags(protover_summary_flags_t *out, + const char *protocols, + const char *version); #endif /* defined(ROUTERPARSE_PRIVATE) */ #define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" diff --git a/src/or/routerset.c b/src/or/routerset.c index 54e26ef943..a2599b316c 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -437,7 +437,7 @@ routerset_equal(const routerset_t *old, const routerset_t *new) /** Free all storage held in <b>routerset</b>. */ void -routerset_free(routerset_t *routerset) +routerset_free_(routerset_t *routerset) { if (!routerset) return; diff --git a/src/or/routerset.h b/src/or/routerset.h index d8819ef3fd..53e8c66c5e 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -40,7 +40,8 @@ void routerset_subtract_nodes(smartlist_t *out, char *routerset_to_string(const routerset_t *routerset); int routerset_equal(const routerset_t *old, const routerset_t *new); -void routerset_free(routerset_t *routerset); +void routerset_free_(routerset_t *routerset); +#define routerset_free(rs) FREE_AND_NULL(routerset_t, routerset_free_, (rs)) int routerset_len(const routerset_t *set); #ifdef ROUTERSET_PRIVATE diff --git a/src/or/scheduler.c b/src/or/scheduler.c index 1984084feb..382b3e3ca9 100644 --- a/src/or/scheduler.c +++ b/src/or/scheduler.c @@ -305,7 +305,7 @@ select_scheduler(void) * wishes of using what it has been configured and don't do a sneaky * fallback. Because this can be changed at runtime, we have to stop tor * right now. */ - exit(1); + exit(1); // XXXX bad exit } /* Set the chosen scheduler. */ @@ -362,6 +362,36 @@ set_scheduler(void) * Functions that can only be accessed from scheduler*.c *****************************************************************************/ +/** Returns human readable string for the given channel scheduler state. */ +const char * +get_scheduler_state_string(int scheduler_state) +{ + switch (scheduler_state) { + case SCHED_CHAN_IDLE: + return "IDLE"; + case SCHED_CHAN_WAITING_FOR_CELLS: + return "WAITING_FOR_CELLS"; + case SCHED_CHAN_WAITING_TO_WRITE: + return "WAITING_TO_WRITE"; + case SCHED_CHAN_PENDING: + return "PENDING"; + default: + return "(invalid)"; + } +} + +/** Helper that logs channel scheduler_state changes. Use this instead of + * setting scheduler_state directly. */ +void +scheduler_set_channel_state(channel_t *chan, int new_state) +{ + log_debug(LD_SCHED, "chan %" PRIu64 " changed from scheduler state %s to %s", + chan->global_identifier, + get_scheduler_state_string(chan->scheduler_state), + get_scheduler_state_string(new_state)); + chan->scheduler_state = new_state; +} + /** Return the pending channel list. */ smartlist_t * get_channels_pending(void) @@ -498,11 +528,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan)) scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from pending " - "to waiting_to_write", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } else { /* * It's not in pending, so it can't become waiting_to_write; it's @@ -510,10 +536,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan)) * waiting_for_cells (remove it, can't write any more). */ if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) { - chan->scheduler_state = SCHED_CHAN_IDLE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p left waiting_for_cells", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } } } @@ -536,17 +559,13 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) * the other lists. It has waiting cells now, so it goes to * channels_pending. */ - chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { smartlist_pqueue_add(channels_pending, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); } - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from waiting_for_cells " - "to pending", - U64_PRINTF_ARG(chan->global_identifier), chan); /* If we made a channel pending, we potentially have scheduling work to * do. */ the_scheduler->schedule(); @@ -558,10 +577,7 @@ scheduler_channel_has_waiting_cells,(channel_t *chan)) */ if (!(chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE || chan->scheduler_state == SCHED_CHAN_PENDING)) { - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p entered waiting_to_write", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } } @@ -648,7 +664,7 @@ scheduler_release_channel,(channel_t *chan)) if (the_scheduler->on_channel_free) { the_scheduler->on_channel_free(chan); } - chan->scheduler_state = SCHED_CHAN_IDLE; + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } /** Mark a channel as ready to accept writes */ @@ -668,19 +684,13 @@ scheduler_channel_wants_writes(channel_t *chan) /* * It can write now, so it goes to channels_pending. */ - log_debug(LD_SCHED, "chan=%" PRIu64 " became pending", - chan->global_identifier); + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { smartlist_pqueue_add(channels_pending, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); } - chan->scheduler_state = SCHED_CHAN_PENDING; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p went from waiting_to_write " - "to pending", - U64_PRINTF_ARG(chan->global_identifier), chan); /* We just made a channel pending, we have scheduling work to do. */ the_scheduler->schedule(); } else { @@ -690,10 +700,7 @@ scheduler_channel_wants_writes(channel_t *chan) */ if (!(chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS || chan->scheduler_state == SCHED_CHAN_PENDING)) { - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p entered waiting_for_cells", - U64_PRINTF_ARG(chan->global_identifier), chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } } } @@ -710,11 +717,12 @@ scheduler_bug_occurred(const channel_t *chan) const size_t outbuf_len = buf_datalen(TO_CONN(BASE_CHAN_TO_TLS((channel_t *) chan)->conn)->outbuf); tor_snprintf(buf, sizeof(buf), - "Channel %" PRIu64 " in state %s and scheduler state %d." + "Channel %" PRIu64 " in state %s and scheduler state %s." " Num cells on cmux: %d. Connection outbuf len: %lu.", chan->global_identifier, channel_state_to_string(chan->state), - chan->scheduler_state, circuitmux_num_cells(chan->cmux), + get_scheduler_state_string(chan->scheduler_state), + circuitmux_num_cells(chan->cmux), (unsigned long)outbuf_len); } diff --git a/src/or/scheduler.h b/src/or/scheduler.h index 559f1c8afc..aeba9e2b75 100644 --- a/src/or/scheduler.h +++ b/src/or/scheduler.h @@ -142,6 +142,9 @@ MOCK_DECL(void, scheduler_channel_has_waiting_cells, (channel_t *chan)); * Defined in scheduler.c *********************************/ +void scheduler_set_channel_state(channel_t *chan, int new_state); +const char *get_scheduler_state_string(int scheduler_state); + /* Triggers a BUG() and extra information with chan if available. */ #define SCHED_BUG(cond, chan) \ (PREDICT_UNLIKELY(cond) ? \ diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c index c79b413b88..6d6490077d 100644 --- a/src/or/scheduler_kist.c +++ b/src/or/scheduler_kist.c @@ -373,7 +373,7 @@ set_scheduler_run_interval(void) } } -/* Return true iff the channel hasn’t hit its kist-imposed write limit yet */ +/* Return true iff the channel hasn't hit its kist-imposed write limit yet */ static int socket_can_write(socket_table_t *table, const channel_t *chan) { @@ -474,7 +474,7 @@ kist_free_all(void) /* Function of the scheduler interface: on_channel_free() */ static void -kist_on_channel_free(const channel_t *chan) +kist_on_channel_free_fn(const channel_t *chan) { free_socket_info_by_chan(&socket_table, chan); } @@ -620,7 +620,7 @@ kist_scheduler_run(void) if (!CHANNEL_IS_OPEN(chan)) { /* Channel isn't open so we put it back in IDLE mode. It is either * renegotiating its TLS session or about to be released. */ - chan->scheduler_state = SCHED_CHAN_IDLE; + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); continue; } /* flush_result has the # cells flushed */ @@ -636,12 +636,12 @@ kist_scheduler_run(void) log_debug(LD_SCHED, "We didn't flush anything on a chan that we think " "can write and wants to write. The channel's state is '%s' " - "and in scheduler state %d. We're going to mark it as " + "and in scheduler state '%s'. We're going to mark it as " "waiting_for_cells (as that's most likely the issue) and " "stop scheduling it this round.", channel_state_to_string(chan->state), - chan->scheduler_state); - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; + get_scheduler_state_string(chan->scheduler_state)); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); continue; } } @@ -668,16 +668,12 @@ kist_scheduler_run(void) * SCHED_CHAN_WAITING_FOR_CELLS to SCHED_CHAN_IDLE and seeing if Tor * starts having serious throughput issues. Best done in shadow/chutney. */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells", - chan->global_identifier); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else if (!channel_more_to_flush(chan)) { /* Case 2: no more cells to send, but still open for writes */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells", - chan->global_identifier); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else if (!socket_can_write(&socket_table, chan)) { /* Case 3: cells to send, but cannot write */ @@ -693,13 +689,11 @@ kist_scheduler_run(void) to_readd = smartlist_new(); } smartlist_add(to_readd, chan); - log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_to_write", - chan->global_identifier); } else { /* Case 4: cells to send, and still open for writes */ - chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(chan, SCHED_CHAN_PENDING); if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { smartlist_pqueue_add(cp, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), chan); @@ -721,7 +715,7 @@ kist_scheduler_run(void) /* Re-add any channels we need to */ if (to_readd) { SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) { - readd_chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING); if (!smartlist_contains(cp, readd_chan)) { if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) { /* XXXX Note that the check above is in theory redundant with @@ -746,7 +740,7 @@ kist_scheduler_run(void) static scheduler_t kist_scheduler = { .type = SCHEDULER_KIST, .free_all = kist_free_all, - .on_channel_free = kist_on_channel_free, + .on_channel_free = kist_on_channel_free_fn, .init = kist_scheduler_init, .on_new_consensus = kist_scheduler_on_new_consensus, .schedule = kist_scheduler_schedule, diff --git a/src/or/scheduler_vanilla.c b/src/or/scheduler_vanilla.c index 303b3dbba8..7a83b9da18 100644 --- a/src/or/scheduler_vanilla.c +++ b/src/or/scheduler_vanilla.c @@ -89,12 +89,7 @@ vanilla_scheduler_run(void) if (flushed < n_cells) { /* We ran out of cells to flush */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_for_cells from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else { /* The channel may still have some cells */ if (channel_more_to_flush(chan)) { @@ -110,12 +105,7 @@ vanilla_scheduler_run(void) chan); } else { /* It's waiting to be able to write more */ - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_to_write from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } else { /* No cells left; it can go to idle or waiting_for_cells */ @@ -124,23 +114,13 @@ vanilla_scheduler_run(void) * It can still accept writes, so it goes to * waiting_for_cells */ - chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "entered waiting_for_cells from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS); } else { /* * We exactly filled up the output queue with all available * cells; go to idle. */ - chan->scheduler_state = SCHED_CHAN_IDLE; - log_debug(LD_SCHED, - "Channel " U64_FORMAT " at %p " - "become idle from pending", - U64_PRINTF_ARG(chan->global_identifier), - chan); + scheduler_set_channel_state(chan, SCHED_CHAN_IDLE); } } } @@ -156,14 +136,14 @@ vanilla_scheduler_run(void) "no cells writeable", U64_PRINTF_ARG(chan->global_identifier), chan); /* Put it back to WAITING_TO_WRITE */ - chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE; + scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE); } } /* Readd any channels we need to */ if (to_readd) { SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) { - readd_chan->scheduler_state = SCHED_CHAN_PENDING; + scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING); smartlist_pqueue_add(cp, scheduler_compare_channels, offsetof(channel_t, sched_heap_idx), diff --git a/src/or/shared_random.c b/src/or/shared_random.c index b3f62a8fd8..13416d6bc7 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -384,7 +384,7 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len) static void sr_cleanup(void) { - sr_state_free(); + sr_state_free_all(); } /* Using <b>commit</b>, return a newly allocated string containing the commit @@ -897,7 +897,7 @@ sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) /* Free a commit object. */ void -sr_commit_free(sr_commit_t *commit) +sr_commit_free_(sr_commit_t *commit) { if (commit == NULL) { return; @@ -1071,7 +1071,7 @@ sr_parse_srv(const smartlist_t *args) srv = tor_malloc_zero(sizeof(*srv)); srv->num_reveals = num_reveals; - /* We substract one byte from the srclen because the function ignores the + /* We subtract one byte from the srclen because the function ignores the * '=' character in the given buffer. This is broken but it's a documented * behavior of the implementation. */ ret = base64_decode((char *) srv->value, sizeof(srv->value), value, diff --git a/src/or/shared_random.h b/src/or/shared_random.h index c0992489cb..675a8d8b06 100644 --- a/src/or/shared_random.h +++ b/src/or/shared_random.h @@ -113,7 +113,8 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args); char *sr_get_string_for_vote(void); char *sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements); -void sr_commit_free(sr_commit_t *commit); +void sr_commit_free_(sr_commit_t *commit); +#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr)) void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); /* Private methods (only used by shared_random_state.c): */ diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c index ae904cfda3..53782af59a 100644 --- a/src/or/shared_random_state.c +++ b/src/or/shared_random_state.c @@ -271,12 +271,15 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state) static void commit_free_(void *p) { - sr_commit_free(p); + sr_commit_free_(p); } +#define state_free(val) \ + FREE_AND_NULL(sr_state_t, state_free_, (val)) + /* Free a state that was allocated with state_new(). */ static void -state_free(sr_state_t *state) +state_free_(sr_state_t *state) { if (state == NULL) { return; @@ -318,9 +321,12 @@ state_set(sr_state_t *state) sr_state = state; } +#define disk_state_free(val) \ + FREE_AND_NULL(sr_disk_state_t, disk_state_free_, (val)) + /* Free an allocated disk state. */ static void -disk_state_free(sr_disk_state_t *state) +disk_state_free_(sr_disk_state_t *state) { if (state == NULL) { return; @@ -1095,7 +1101,7 @@ sr_state_get_previous_srv(void) } /* Set the current SRV value from our state. Value CAN be NULL. The srv - * object ownership is transfered to the state object. */ + * object ownership is transferred to the state object. */ void sr_state_set_previous_srv(const sr_srv_t *srv) { @@ -1114,7 +1120,7 @@ sr_state_get_current_srv(void) } /* Set the current SRV value from our state. Value CAN be NULL. The srv - * object ownership is transfered to the state object. */ + * object ownership is transferred to the state object. */ void sr_state_set_current_srv(const sr_srv_t *srv) { @@ -1219,7 +1225,7 @@ sr_state_get_commit(const char *rsa_identity) } /* Add <b>commit</b> to the permanent state. The commit object ownership is - * transfered to the state so the caller MUST not free it. */ + * transferred to the state so the caller MUST not free it. */ void sr_state_add_commit(sr_commit_t *commit) { @@ -1286,7 +1292,7 @@ sr_state_srv_is_fresh(void) /* Cleanup and free our disk and memory state. */ void -sr_state_free(void) +sr_state_free_all(void) { state_free(sr_state); disk_state_free(sr_disk_state); diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h index 866725c435..fdbbf4919a 100644 --- a/src/or/shared_random_state.h +++ b/src/or/shared_random_state.h @@ -119,7 +119,7 @@ void sr_state_unset_fresh_srv(void); int sr_state_init(int save_to_disk, int read_from_disk); int sr_state_is_initialized(void); void sr_state_save(void); -void sr_state_free(void); +void sr_state_free_all(void); time_t sr_state_get_start_time_of_current_protocol_run(time_t now); unsigned int sr_state_get_phase_duration(void); diff --git a/src/or/statefile.c b/src/or/statefile.c index 97bd9cac36..cc114f0a2b 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -681,7 +681,7 @@ save_transport_to_state(const char *transport, } STATIC void -or_state_free(or_state_t *state) +or_state_free_(or_state_t *state) { if (!state) return; diff --git a/src/or/statefile.h b/src/or/statefile.h index 574afb3622..b4cc4d1dc6 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -20,7 +20,8 @@ void or_state_free_all(void); #ifdef STATEFILE_PRIVATE STATIC config_line_t *get_transport_in_state_by_name(const char *transport); -STATIC void or_state_free(or_state_t *state); +STATIC void or_state_free_(or_state_t *state); +#define or_state_free(st) FREE_AND_NULL(or_state_t, or_state_free_, (st)) STATIC or_state_t *or_state_new(void); #endif diff --git a/src/or/status.c b/src/or/status.c index 52763a7042..4f7be164b1 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -27,6 +27,8 @@ #include "hibernate.h" #include "rephist.h" #include "statefile.h" +#include "hs_stats.h" +#include "hs_service.h" #include "dos.h" static void log_accounting(const time_t now, const or_options_t *options); @@ -40,7 +42,7 @@ count_circuits(void) } /** Take seconds <b>secs</b> and return a newly allocated human-readable - * uptime string */ + * uptime string. */ STATIC char * secs_to_uptime(long secs) { @@ -86,6 +88,26 @@ bytes_to_usage(uint64_t bytes) return bw_string; } +/** Log some usage info about our hidden service */ +static void +log_onion_service_stats(void) +{ + unsigned int num_services = hs_service_get_num_services(); + + /* If there are no active hidden services, no need to print logs */ + if (num_services == 0) { + return; + } + + log_notice(LD_HEARTBEAT, + "Our hidden service%s received %u v2 and %u v3 INTRODUCE2 cells " + "and attempted to launch %d rendezvous circuits.", + num_services == 1 ? "" : "s", + hs_stats_get_n_introduce2_v2_cells(), + hs_stats_get_n_introduce2_v3_cells(), + hs_stats_get_n_rendezvous_launches()); +} + /** Log a "heartbeat" message describing Tor's status and history so that the * user can know that there is indeed a running Tor. Return 0 on success and * -1 on failure. */ @@ -159,6 +181,23 @@ log_heartbeat(time_t now) tor_free(msg); } + if (options->MainloopStats) { + const uint64_t main_loop_success_count = get_main_loop_success_count(); + const uint64_t main_loop_error_count = get_main_loop_error_count(); + const uint64_t main_loop_idle_count = get_main_loop_idle_count(); + + log_fn(LOG_NOTICE, LD_HEARTBEAT, "Main event loop statistics: " + U64_FORMAT " successful returns, " + U64_FORMAT " erroneous returns, and " + U64_FORMAT " idle returns.", + U64_PRINTF_ARG(main_loop_success_count), + U64_PRINTF_ARG(main_loop_error_count), + U64_PRINTF_ARG(main_loop_idle_count)); + } + + /** Now, if we are an HS service, log some stats about our usage */ + log_onion_service_stats(); + tor_free(uptime); tor_free(bw_sent); tor_free(bw_rcvd); diff --git a/src/or/tor_api.c b/src/or/tor_api.c new file mode 100644 index 0000000000..4260cc88f4 --- /dev/null +++ b/src/or/tor_api.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.c + **/ + +#include "tor_api.h" +#include "tor_api_internal.h" + +// Include this after the above headers, to insure that they don't +// depend on anything else. +#include "orconfig.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// We don't want to use tor_malloc and tor_free here, since this needs +// to run before anything is initialized at all, and ought to run when +// we're not linked to anything at all. + +#define raw_malloc malloc +#define raw_free free + +tor_main_configuration_t * +tor_main_configuration_new(void) +{ + static const char *fake_argv[] = { "tor" }; + tor_main_configuration_t *cfg = raw_malloc(sizeof(*cfg)); + if (cfg == NULL) + return NULL; + + memset(cfg, 0, sizeof(*cfg)); + + cfg->argc = 1; + cfg->argv = (char **) fake_argv; + + return cfg; +} + +int +tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]) +{ + if (cfg == NULL) + return -1; + cfg->argc = argc; + cfg->argv = argv; + return 0; +} + +void +tor_main_configuration_free(tor_main_configuration_t *cfg) +{ + if (cfg == NULL) + return; + raw_free(cfg); +} + +/* Main entry point for the Tor process. Called from main(). + * + * This function is distinct from main() only so we can link main.c into + * the unittest binary without conflicting with the unittests' main. + * + * Some embedders have historically called this function; but that usage is + * deprecated: they should use tor_run_main() instead. + */ +int +tor_main(int argc, char *argv[]) +{ + tor_main_configuration_t *cfg = tor_main_configuration_new(); + if (!cfg) { + puts("INTERNAL ERROR: Allocation failure. Cannot proceed"); + return 1; + } + if (tor_main_configuration_set_command_line(cfg, argc, argv) < 0) { + puts("INTERNAL ERROR: Can't set command line. Cannot proceed."); + return 1; + } + int rv = tor_run_main(cfg); + tor_main_configuration_free(cfg); + return rv; +} + diff --git a/src/or/tor_api.h b/src/or/tor_api.h new file mode 100644 index 0000000000..7e86c7fec5 --- /dev/null +++ b/src/or/tor_api.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.h + * \brief Public C API for the Tor network service. + * + * This interface is intended for use by programs that need to link Tor as + * a library, and launch it in a separate thread. If you have the ability + * to run Tor as a separate executable, you should probably do that instead + * of embedding it as a library. + * + * To use this API, first construct a tor_main_configuration_t object using + * tor_main_configuration_new(). Then, you use one or more other function + * calls (such as tor_main_configuration_set_command_line() to configure how + * Tor should be run. Finally, you pass the configuration object to + * tor_run_main(). + * + * At this point, tor_run_main() will block its thread to run a Tor daemon; + * when the Tor daemon exits, it will return. See notes on bugs and + * limitations below. + * + * There is no other public C API to Tor: calling any C Tor function not + * documented in this file is not guaranteed to be stable. + **/ + +#ifndef TOR_API_H +#define TOR_API_H + +typedef struct tor_main_configuration_t tor_main_configuration_t; + +/** + * Create and return a new tor_main_configuration(). + */ +tor_main_configuration_t *tor_main_configuration_new(void); + +/** + * Set the command-line arguments in <b>cfg</b>. + * + * The <b>argc</b> and <b>argv</b> values here are as for main(). The + * contents of the argv pointer must remain unchanged until tor_run_main() has + * finished and you call tor_main_configuration_free(). + * + * Return 0 on success, -1 on failure. + */ +int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]); + +/** + * Release all storage held in <b>cfg</b>. + * + * Once you have passed a tor_main_configuration_t to tor_run_main(), you + * must not free it until tor_run_main() has finished. + */ +void tor_main_configuration_free(tor_main_configuration_t *cfg); + +/** + * Run the tor process, as if from the command line. + * + * The command line arguments from tor_main_configuration_set_command_line() + * are taken as if they had been passed to main(). + * + * This function will not return until Tor is done running. It returns zero + * on success, and nonzero on failure. + * + * BUG 23848: In many cases, tor_main will call exit() or abort() instead of + * returning. This is not the intended long-term behavior; we are trying to + * fix it. + * + * BUG 23847: You can only call tor_main() once in a single process; if it + * returns and you call it again, you may crash, or you may encounter other + * unexpected behavior, including possible security issues. This is not + * intended long-term behavior; we are trying to fix it. + * + * LIMITATION: You cannot run more than one instance of Tor in the same + * process at the same time. Concurrent calls will cause undefined behavior. + * We do not currently have plans to change this. + * + * LIMITATION: While we will try to fix any problems found here, you + * should be aware that Tor was originally written to run as its own + * process, and that the functionality of this file was added later. If + * you find any bugs or strange behavior, please report them, and we'll + * try to straighten them out. + */ +int tor_run_main(const tor_main_configuration_t *); + +/** + * Run the tor process, as if from the command line. + * + * @deprecated Using this function from outside Tor is deprecated; you should + * use tor_run_main() instead. + * + * BUGS: This function has all the same bugs as tor_run_main(). + * + * LIMITATIONS: This function has all the limitations of tor_run_main(). + */ +int tor_main(int argc, char **argv); + +#endif /* !defined(TOR_API_H) */ + diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h new file mode 100644 index 0000000000..10b6278b7b --- /dev/null +++ b/src/or/tor_api_internal.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_API_INTERNAL_H +#define TOR_API_INTERNAL_H + +/* The contents of this type are private; don't mess with them from outside + * Tor. */ +struct tor_main_configuration_t { + /** As in main() */ + int argc; + /** As in main(). This pointer is owned by the caller */ + char **argv; +}; + +#endif /* !defined(TOR_API_INTERNAL_H) */ + diff --git a/src/or/tor_main.c b/src/or/tor_main.c index a3a8838602..703669ac99 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -3,17 +3,10 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; - -/** String describing which Tor Git repository version the source was - * built from. This string is generated by a bit of shell kludging in - * src/or/include.am, and is usually right. - */ -const char tor_git_revision[] = -#ifndef _MSC_VER -#include "micro-revision.i" +#include "orconfig.h" +#ifdef ENABLE_RESTART_DEBUGGING +#include <stdlib.h> #endif - ""; /** * \file tor_main.c @@ -26,14 +19,23 @@ const char tor_git_revision[] = int tor_main(int argc, char *argv[]); /** We keep main() in a separate file so that our unit tests can use - * functions from main.c) + * functions from main.c. */ int main(int argc, char *argv[]) { - int r = tor_main(argc, argv); + int r; +#ifdef ENABLE_RESTART_DEBUGGING + int restart_count = getenv("TOR_DEBUG_RESTART") ? 1 : 0; + again: +#endif + r = tor_main(argc, argv); if (r < 0 || r > 255) return 1; +#ifdef ENABLE_RESTART_DEBUGGING + else if (r == 0 && restart_count--) + goto again; +#endif else return r; } diff --git a/src/or/torcert.c b/src/or/torcert.c index 212534d311..51935ddf72 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -138,7 +138,7 @@ tor_cert_create(const ed25519_keypair_t *signing_key, /** Release all storage held for <b>cert</b>. */ void -tor_cert_free(tor_cert_t *cert) +tor_cert_free_(tor_cert_t *cert) { if (! cert) return; @@ -472,7 +472,7 @@ or_handshake_certs_new(void) /** Release all storage held in <b>certs</b> */ void -or_handshake_certs_free(or_handshake_certs_t *certs) +or_handshake_certs_free_(or_handshake_certs_t *certs) { if (!certs) return; diff --git a/src/or/torcert.h b/src/or/torcert.h index ac227db209..18ca60b5a8 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -57,7 +57,8 @@ tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); -void tor_cert_free(tor_cert_t *cert); +void tor_cert_free_(tor_cert_t *cert); +#define tor_cert_free(cert) FREE_AND_NULL(tor_cert_t, tor_cert_free_, (cert)) int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, const tor_cert_t *out, @@ -84,7 +85,9 @@ rsa_ed25519_crosscert_check, (const uint8_t *crosscert, const time_t reject_if_expired_before)); or_handshake_certs_t *or_handshake_certs_new(void); -void or_handshake_certs_free(or_handshake_certs_t *certs); +void or_handshake_certs_free_(or_handshake_certs_t *certs); +#define or_handshake_certs_free(certs) \ + FREE_AND_NULL(or_handshake_certs_t, or_handshake_certs_free_, (certs)) int or_handshake_certs_rsa_ok(int severity, or_handshake_certs_t *certs, tor_tls_t *tls, diff --git a/src/or/transports.c b/src/or/transports.c index 5fb24e11a0..b08dcd1613 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -154,7 +154,7 @@ transport_new(const tor_addr_t *addr, uint16_t port, /** Free the pluggable transport struct <b>transport</b>. */ void -transport_free(transport_t *transport) +transport_free_(transport_t *transport) { if (!transport) return; @@ -590,7 +590,7 @@ pt_configure_remaining_proxies(void) } /* If the proxy is not fully configured, try to configure it - futher. */ + further. */ if (!proxy_configuration_finished(mp)) if (configure_proxy(mp) == 1) at_least_a_proxy_config_finished = 1; diff --git a/src/or/transports.h b/src/or/transports.h index e368e447c3..1b2786472c 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -35,7 +35,8 @@ void sweep_transport_list(void); MOCK_DECL(int, transport_add_from_config, (const tor_addr_t *addr, uint16_t port, const char *name, int socks_ver)); -void transport_free(transport_t *transport); +void transport_free_(transport_t *transport); +#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr)) transport_t *transport_get_by_name(const char *name); diff --git a/src/rust/.cargo/config.in b/src/rust/.cargo/config.in index 414b253a57..301e7fdbe7 100644 --- a/src/rust/.cargo/config.in +++ b/src/rust/.cargo/config.in @@ -5,4 +5,4 @@ @RUST_DL@ replace-with = 'vendored-sources' @RUST_DL@ [source.vendored-sources] -@RUST_DL@ directory = '@RUST_DEPENDENCIES@' +@RUST_DL@ directory = '@TOR_RUST_DEPENDENCIES@' diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index e208e14320..224d2135bf 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -1,13 +1,54 @@ +[root] +name = "tor_util" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tor_allocate 0.0.1", +] + +[[package]] +name = "external" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "tor_util" +name = "protover" version = "0.0.1" dependencies = [ + "external 0.0.1", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "smartlist 0.0.1", + "tor_allocate 0.0.1", + "tor_util 0.0.1", +] + +[[package]] +name = "smartlist" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tor_allocate" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tor_rust" +version = "0.1.0" +dependencies = [ + "protover 0.0.1", + "tor_util 0.0.1", ] [metadata] diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index fc4377e8b4..953c9b96b7 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["tor_util"] +members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"] [profile.release] debug = true diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml new file mode 100644 index 0000000000..bccd7033a7 --- /dev/null +++ b/src/rust/external/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "external" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "external" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs new file mode 100644 index 0000000000..b9e17f021d --- /dev/null +++ b/src/rust/external/external.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use libc::{c_char, c_int}; +use std::ffi::CString; + +extern "C" { + fn tor_version_as_new_as( + platform: *const c_char, + cutoff: *const c_char, + ) -> c_int; +} + +/// Wrap calls to tor_version_as_new_as, defined in src/or/routerparse.c +pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool { + // CHK: These functions should log a warning if an error occurs. This + // can be added when integration with tor's logger is added to rust + let c_platform = match CString::new(platform) { + Ok(n) => n, + Err(_) => return false, + }; + + let c_cutoff = match CString::new(cutoff) { + Ok(n) => n, + Err(_) => return false, + }; + + let result: c_int = unsafe { + tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()) + }; + + result == 1 +} diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs new file mode 100644 index 0000000000..0af0d6452d --- /dev/null +++ b/src/rust/external/lib.rs @@ -0,0 +1,14 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Interface for external calls to tor C ABI +//! +//! The purpose of this module is to provide a clean interface for when Rust +//! modules need to interact with functionality in tor C code rather than each +//! module implementing this functionality repeatedly. + +extern crate libc; + +mod external; + +pub use external::*; diff --git a/src/rust/include.am b/src/rust/include.am index 20afc6c4db..9c4337484a 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -1,6 +1,26 @@ -include src/rust/tor_util/include.am +include src/rust/tor_rust/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ src/rust/Cargo.lock \ - src/rust/.cargo/config.in + src/rust/.cargo/config.in \ + src/rust/external/Cargo.toml \ + src/rust/external/external.rs \ + src/rust/external/lib.rs \ + src/rust/protover/Cargo.toml \ + src/rust/protover/ffi.rs \ + src/rust/protover/lib.rs \ + src/rust/protover/protover.rs \ + src/rust/protover/tests/protover.rs \ + src/rust/smartlist/Cargo.toml \ + src/rust/smartlist/lib.rs \ + src/rust/smartlist/smartlist.rs \ + src/rust/tor_allocate/Cargo.toml \ + src/rust/tor_allocate/lib.rs \ + src/rust/tor_allocate/tor_allocate.rs \ + src/rust/tor_rust/Cargo.toml \ + src/rust/tor_rust/include.am \ + src/rust/tor_rust/lib.rs \ + src/rust/tor_util/Cargo.toml \ + src/rust/tor_util/ffi.rs \ + src/rust/tor_util/lib.rs diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml new file mode 100644 index 0000000000..04d2f2ed7d --- /dev/null +++ b/src/rust/protover/Cargo.toml @@ -0,0 +1,25 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "protover" + +[dependencies] +libc = "0.2.22" + +[dependencies.smartlist] +path = "../smartlist" + +[dependencies.external] +path = "../external" + +[dependencies.tor_util] +path = "../tor_util" + +[dependencies.tor_allocate] +path = "../tor_allocate" + +[lib] +name = "protover" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs new file mode 100644 index 0000000000..2ee0286ecf --- /dev/null +++ b/src/rust/protover/ffi.rs @@ -0,0 +1,238 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! FFI functions, only to be called from C. +//! +//! Equivalent C versions of this api are in `src/or/protover.c` + +use libc::{c_char, c_int, uint32_t}; +use std::ffi::CStr; +use std::ffi::CString; + +use protover::*; +use smartlist::*; +use tor_allocate::allocate_and_copy_string; +use tor_util::strings::byte_slice_is_c_like; +use tor_util::strings::empty_static_cstr; + + +/// Translate C enums to Rust Proto enums, using the integer value of the C +/// enum to map to its associated Rust enum +/// +/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t` +fn translate_to_rust(c_proto: uint32_t) -> Result<Proto, &'static str> { + match c_proto { + 0 => Ok(Proto::Link), + 1 => Ok(Proto::LinkAuth), + 2 => Ok(Proto::Relay), + 3 => Ok(Proto::DirCache), + 4 => Ok(Proto::HSDir), + 5 => Ok(Proto::HSIntro), + 6 => Ok(Proto::HSRend), + 7 => Ok(Proto::Desc), + 8 => Ok(Proto::Microdesc), + 9 => Ok(Proto::Cons), + _ => Err("Invalid protocol type"), + } +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::all_supported +#[no_mangle] +pub extern "C" fn protover_all_supported( + c_relay_version: *const c_char, + missing_out: *mut *mut c_char, +) -> c_int { + + if c_relay_version.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) }; + + let relay_version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let (is_supported, unsupported) = all_supported(relay_version); + + if unsupported.len() > 0 { + let c_unsupported = match CString::new(unsupported) { + Ok(n) => n, + Err(_) => return 1, + }; + + let ptr = c_unsupported.into_raw(); + unsafe { *missing_out = ptr }; + } + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::list_supports_protocol +#[no_mangle] +pub extern "C" fn protocol_list_supports_protocol( + c_protocol_list: *const c_char, + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + if c_protocol_list.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) }; + + let protocol_list = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = + protover_string_supports_protocol(protocol_list, protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::list_supports_protocol_or_later +#[no_mangle] +pub extern "C" fn protocol_list_supports_protocol_or_later( + c_protocol_list: *const c_char, + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + if c_protocol_list.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) }; + + let protocol_list = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = + protover_string_supports_protocol_or_later( + protocol_list, protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::get_supported_protocols +#[no_mangle] +pub extern "C" fn protover_get_supported_protocols() -> *const c_char { + let supported: &'static CStr; + + // If we're going to pass it to C, there cannot be any intermediate NUL + // bytes. An assert is okay here, since changing the const byte slice + // in protover.rs to contain a NUL byte somewhere in the middle would be a + // programming error. + assert!(byte_slice_is_c_like(SUPPORTED_PROTOCOLS)); + + // It's okay to unwrap the result of this function because + // we can see that the bytes we're passing into it 1) are valid UTF-8, + // 2) have no intermediate NUL bytes, and 3) are terminated with a NUL + // byte. + supported = CStr::from_bytes_with_nul(SUPPORTED_PROTOCOLS).unwrap(); + + supported.as_ptr() +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_vote +#[no_mangle] +pub extern "C" fn protover_compute_vote( + list: *const Stringlist, + threshold: c_int, +) -> *mut c_char { + + if list.is_null() { + let empty = String::new(); + return allocate_and_copy_string(&empty); + } + + // Dereference of raw pointer requires an unsafe block. The pointer is + // checked above to ensure it is not null. + let data: Vec<String> = unsafe { (*list).get_list() }; + + let vote = compute_vote(data, threshold); + + allocate_and_copy_string(&vote) +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::is_supported_here +#[no_mangle] +pub extern "C" fn protover_is_supported_here( + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = is_supported_here(protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_for_old_tor +#[no_mangle] +pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char { + let supported: &'static CStr; + let elder_protocols: &'static [u8]; + let empty: &'static CStr; + + empty = empty_static_cstr(); + + if version.is_null() { + return empty.as_ptr(); + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(version) }; + + let version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return empty.as_ptr(), + }; + + elder_protocols = compute_for_old_tor(&version); + + // If we're going to pass it to C, there cannot be any intermediate NUL + // bytes. An assert is okay here, since changing the const byte slice + // in protover.rs to contain a NUL byte somewhere in the middle would be a + // programming error. + assert!(byte_slice_is_c_like(elder_protocols)); + + // It's okay to unwrap the result of this function because + // we can see that the bytes we're passing into it 1) are valid UTF-8, + // 2) have no intermediate NUL bytes, and 3) are terminated with a NUL + // byte. + supported = CStr::from_bytes_with_nul(elder_protocols).unwrap_or(empty); + + supported.as_ptr() +} diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs new file mode 100644 index 0000000000..fe8c0f9bb2 --- /dev/null +++ b/src/rust/protover/lib.rs @@ -0,0 +1,34 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Versioning information for different pieces of the Tor protocol. +//! +//! The below description is taken from src/rust/protover.c, which is currently +//! enabled by default. We are in the process of experimenting with Rust in +//! tor, and this protover module is implemented to help achieve this goal. +//! +//! Starting in version 0.2.9.3-alpha, Tor places separate version numbers on +//! each of the different components of its protocol. Relays use these numbers +//! to advertise what versions of the protocols they can support, and clients +//! use them to find what they can ask a given relay to do. Authorities vote +//! on the supported protocol versions for each relay, and also vote on the +//! which protocols you should have to support in order to be on the Tor +//! network. All Tor instances use these required/recommended protocol versions +//! to tell what level of support for recent protocols each relay has, and +//! to decide whether they should be running given their current protocols. +//! +//! The main advantage of these protocol versions numbers over using Tor +//! version numbers is that they allow different implementations of the Tor +//! protocols to develop independently, without having to claim compatibility +//! with specific versions of Tor. + +extern crate libc; +extern crate smartlist; +extern crate external; +extern crate tor_allocate; +extern crate tor_util; + +mod protover; +pub mod ffi; + +pub use protover::*; diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs new file mode 100644 index 0000000000..e5dc69b9a0 --- /dev/null +++ b/src/rust/protover/protover.rs @@ -0,0 +1,943 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use external::c_tor_version_as_new_as; + +use std::str; +use std::str::FromStr; +use std::fmt; +use std::collections::{HashMap, HashSet}; +use std::ops::Range; +use std::string::String; +use std::u32; + +use tor_util::strings::NUL_BYTE; + +/// The first version of Tor that included "proto" entries in its descriptors. +/// Authorities should use this to decide whether to guess proto lines. +/// +/// C_RUST_COUPLED: +/// src/or/protover.h `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` +const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha"; + +/// The maximum number of subprotocol version numbers we will attempt to expand +/// before concluding that someone is trying to DoS us +/// +/// C_RUST_COUPLED: src/or/protover.c `MAX_PROTOCOLS_TO_EXPAND` +const MAX_PROTOCOLS_TO_EXPAND: usize = (1<<16); + +/// Currently supported protocols and their versions, as a byte-slice. +/// +/// # Warning +/// +/// This byte-slice ends in a NUL byte. This is so that we can directly convert +/// it to an `&'static CStr` in the FFI code, in order to hand the static string +/// to C in a way that is compatible with C static strings. +/// +/// Rust code which wishes to accesses this string should use +/// `protover::get_supported_protocols()` instead. +/// +/// C_RUST_COUPLED: src/or/protover.c `protover_get_supported_protocols` +pub(crate) const SUPPORTED_PROTOCOLS: &'static [u8] = + b"Cons=1-2 \ + Desc=1-2 \ + DirCache=1-2 \ + HSDir=1-2 \ + HSIntro=3-4 \ + HSRend=1-2 \ + Link=1-5 \ + LinkAuth=1,3 \ + Microdesc=1-2 \ + Relay=1-2\0"; + +/// Known subprotocols in Tor. Indicates which subprotocol a relay supports. +/// +/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t` +#[derive(Hash, Eq, PartialEq, Debug)] +pub enum Proto { + Cons, + Desc, + DirCache, + HSDir, + HSIntro, + HSRend, + Link, + LinkAuth, + Microdesc, + Relay, +} + +impl fmt::Display for Proto { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Translates a string representation of a protocol into a Proto type. +/// Error if the string is an unrecognized protocol name. +/// +/// C_RUST_COUPLED: src/or/protover.c `PROTOCOL_NAMES` +impl FromStr for Proto { + type Err = &'static str; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Cons" => Ok(Proto::Cons), + "Desc" => Ok(Proto::Desc), + "DirCache" => Ok(Proto::DirCache), + "HSDir" => Ok(Proto::HSDir), + "HSIntro" => Ok(Proto::HSIntro), + "HSRend" => Ok(Proto::HSRend), + "Link" => Ok(Proto::Link), + "LinkAuth" => Ok(Proto::LinkAuth), + "Microdesc" => Ok(Proto::Microdesc), + "Relay" => Ok(Proto::Relay), + _ => Err("Not a valid protocol type"), + } + } +} + +/// Get the string representation of current supported protocols +/// +/// # Returns +/// +/// A `String` whose value is the existing protocols supported by tor. +/// Returned data is in the format as follows: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +pub fn get_supported_protocols() -> &'static str { + // The `len() - 1` is to remove the NUL byte. + // The `unwrap` is safe becauase we SUPPORTED_PROTOCOLS is under + // our control. + str::from_utf8(&SUPPORTED_PROTOCOLS[..SUPPORTED_PROTOCOLS.len() - 1]) + .unwrap_or("") +} + +pub struct SupportedProtocols(HashMap<Proto, Versions>); + +impl SupportedProtocols { + pub fn from_proto_entries<I, S>(protocol_strs: I) -> Result<Self, &'static str> + where + I: Iterator<Item = S>, + S: AsRef<str>, + { + let mut parsed = HashMap::new(); + for subproto in protocol_strs { + let (name, version) = get_proto_and_vers(subproto.as_ref())?; + parsed.insert(name, version); + } + Ok(SupportedProtocols(parsed)) + } + + /// Translates a string representation of a protocol list to a + /// SupportedProtocols instance. + /// + /// # Examples + /// + /// ``` + /// use protover::SupportedProtocols; + /// + /// let supported_protocols = SupportedProtocols::from_proto_entries_string( + /// "HSDir=1-2 HSIntro=3-4" + /// ); + /// ``` + pub fn from_proto_entries_string( + proto_entries: &str, + ) -> Result<Self, &'static str> { + Self::from_proto_entries(proto_entries.split(" ")) + } + + /// Translate the supported tor versions from a string into a + /// HashMap, which is useful when looking up a specific + /// subprotocol. + /// + fn tor_supported() -> Result<Self, &'static str> { + Self::from_proto_entries_string(get_supported_protocols()) + } +} + +type Version = u32; + +/// Set of versions for a protocol. +#[derive(Debug, PartialEq, Eq)] +pub struct Versions(HashSet<Version>); + +impl Versions { + /// Get the unique version numbers supported by a subprotocol. + /// + /// # Inputs + /// + /// * `version_string`, a string comprised of "[0-9,-]" + /// + /// # Returns + /// + /// A `Result` whose `Ok` value is a `HashSet<u32>` holding all of the unique + /// version numbers. If there were ranges in the `version_string`, then these + /// are expanded, i.e. `"1-3"` would expand to `HashSet<u32>::new([1, 2, 3])`. + /// The returned HashSet is *unordered*. + /// + /// The returned `Result`'s `Err` value is an `&'static str` with a description + /// of the error. + /// + /// # Errors + /// + /// This function will error if: + /// + /// * the `version_string` is empty or contains an equals (`"="`) sign, + /// * the expansion of a version range produces an error (see + /// `expand_version_range`), + /// * any single version number is not parseable as an `u32` in radix 10, or + /// * there are greater than 2^16 version numbers to expand. + /// + fn from_version_string( + version_string: &str, + ) -> Result<Self, &'static str> { + let mut versions = HashSet::<Version>::new(); + + for piece in version_string.split(",") { + if piece.contains("-") { + for p in expand_version_range(piece)? { + versions.insert(p); + } + } else if piece == "" { + continue; + } else { + let v = u32::from_str(piece).or( + Err("invalid protocol entry"), + )?; + if v == u32::MAX { + return Err("invalid protocol entry"); + } + versions.insert(v); + } + + if versions.len() > MAX_PROTOCOLS_TO_EXPAND { + return Err("Too many versions to expand"); + } + } + Ok(Versions(versions)) + } +} + + +/// Parse the subprotocol type and its version numbers. +/// +/// # Inputs +/// +/// * A `protocol_entry` string, comprised of a keyword, an "=" sign, and one +/// or more version numbers. +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a tuple of `(Proto, HashSet<u32>)`, where the +/// first element is the subprotocol type (see `protover::Proto`) and the last +/// element is a(n unordered) set of unique version numbers which are supported. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +fn get_proto_and_vers<'a>( + protocol_entry: &'a str, +) -> Result<(Proto, Versions), &'static str> { + let mut parts = protocol_entry.splitn(2, "="); + + let proto = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = Versions::from_version_string(vers)?; + let proto_name = proto.parse()?; + + Ok((proto_name, versions)) +} + +/// Parses a single subprotocol entry string into subprotocol and version +/// parts, and then checks whether any of those versions are unsupported. +/// Helper for protover::all_supported +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1" +/// +/// # Returns +/// +/// Returns `true` if the protocol entry is well-formatted and only contains +/// versions that are also supported in tor. Otherwise, returns false +/// +fn contains_only_supported_protocols(proto_entry: &str) -> bool { + let (name, mut vers) = match get_proto_and_vers(proto_entry) { + Ok(n) => n, + Err(_) => return false, + }; + + let currently_supported = match SupportedProtocols::tor_supported() { + Ok(n) => n.0, + Err(_) => return false, + }; + + let supported_versions = match currently_supported.get(&name) { + Some(n) => n, + None => return false, + }; + + vers.0.retain(|x| !supported_versions.0.contains(x)); + vers.0.is_empty() +} + +/// Determine if we support every protocol a client supports, and if not, +/// determine which protocols we do not have support for. +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1 LinkAuth=1-2" +/// +/// # Returns +/// +/// Return `true` if every protocol version is one that we support. +/// Otherwise, return `false`. +/// Optionally, return parameters which the client supports but which we do not +/// +/// # Examples +/// ``` +/// use protover::all_supported; +/// +/// let (is_supported, unsupported) = all_supported("Link=1"); +/// assert_eq!(true, is_supported); +/// +/// let (is_supported, unsupported) = all_supported("Link=5-6"); +/// assert_eq!(false, is_supported); +/// assert_eq!("Link=5-6", unsupported); +/// +pub fn all_supported(protocols: &str) -> (bool, String) { + let unsupported = protocols + .split_whitespace() + .filter(|v| !contains_only_supported_protocols(v)) + .collect::<Vec<&str>>(); + + (unsupported.is_empty(), unsupported.join(" ")) +} + +/// Return true iff the provided protocol list includes support for the +/// indicated protocol and version. +/// Otherwise, return false +/// +/// # Inputs +/// +/// * `list`, a string representation of a list of protocol entries. +/// * `proto`, a `Proto` to test support for +/// * `vers`, a `Version` version which we will go on to determine whether the +/// specified protocol supports. +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,1); +/// assert_eq!(true, is_supported); +/// +/// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,5); +/// assert_eq!(false, is_not_supported) +/// ``` +pub fn protover_string_supports_protocol( + list: &str, + proto: Proto, + vers: Version, +) -> bool { + let supported = match SupportedProtocols::from_proto_entries_string(list) { + Ok(result) => result.0, + Err(_) => return false, + }; + + let supported_versions = match supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.0.contains(&vers) +} + +/// As protover_string_supports_protocol(), but also returns True if +/// any later version of the protocol is supported. +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 5); +/// +/// assert_eq!(true, is_supported); +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 4); +/// +/// assert_eq!(true, is_supported); +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 6); +/// +/// assert_eq!(false, is_supported); +/// ``` +pub fn protover_string_supports_protocol_or_later( + list: &str, + proto: Proto, + vers: u32, +) -> bool { + let supported = match SupportedProtocols::from_proto_entries_string(list) { + Ok(result) => result.0, + Err(_) => return false, + }; + + let supported_versions = match supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.0.iter().any(|v| v >= &vers) +} + +/// Fully expand a version range. For example, 1-3 expands to 1,2,3 +/// Helper for Versions::from_version_string +/// +/// # Inputs +/// +/// `range`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `Result` whose `Ok` value a vector of unsigned integers representing the +/// expanded range of supported versions by a single protocol. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +/// # Errors +/// +/// This function will error if: +/// +/// * the specified range is empty +/// * the version range does not contain both a valid lower and upper bound. +/// +fn expand_version_range(range: &str) -> Result<Range<u32>, &'static str> { + if range.is_empty() { + return Err("version string empty"); + } + + let mut parts = range.split("-"); + + let lower_string = parts.next().ok_or( + "cannot parse protocol range lower bound", + )?; + + let lower = u32::from_str_radix(lower_string, 10).or(Err( + "cannot parse protocol range lower bound", + ))?; + + let higher_string = parts.next().ok_or( + "cannot parse protocol range upper bound", + )?; + + let higher = u32::from_str_radix(higher_string, 10).or(Err( + "cannot parse protocol range upper bound", + ))?; + + if lower == u32::MAX || higher == u32::MAX { + return Err("protocol range value out of range"); + } + + if lower > higher { + return Err("protocol range is badly formed"); + } + + // We can use inclusive range syntax when it becomes stable. + let result = lower..higher + 1; + + if result.len() > MAX_PROTOCOLS_TO_EXPAND { + Err("Too many protocols in expanded range") + } else { + Ok(result) + } +} + +/// Checks to see if there is a continuous range of integers, starting at the +/// first in the list. Returns the last integer in the range if a range exists. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `bool` indicating whether the list contains a range, starting at the +/// first in the list, and an `u32` of the last integer in the range. +/// +/// For example, if given vec![1, 2, 3, 5], find_range will return true, +/// as there is a continuous range, and 3, which is the last number in the +/// continuous range. +/// +fn find_range(list: &Vec<u32>) -> (bool, u32) { + if list.len() == 0 { + return (false, 0); + } + + let mut iterable = list.iter().peekable(); + let mut range_end = match iterable.next() { + Some(n) => *n, + None => return (false, 0), + }; + + let mut has_range = false; + + while iterable.peek().is_some() { + let n = *iterable.next().unwrap(); + if n != range_end + 1 { + break; + } + + has_range = true; + range_end = n; + } + + (has_range, range_end) +} + +/// Contracts a HashSet representation of supported versions into a string. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `supported_set`, a set of integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `String` representation of this set in ascending order. +/// +fn contract_protocol_list<'a>(supported_set: &'a HashSet<Version>) -> String { + let mut supported: Vec<Version> = + supported_set.iter().map(|x| *x).collect(); + supported.sort(); + + let mut final_output: Vec<String> = Vec::new(); + + while supported.len() != 0 { + let (has_range, end) = find_range(&supported); + let current = supported.remove(0); + + if has_range { + final_output.push(format!( + "{}-{}", + current.to_string(), + &end.to_string(), + )); + supported.retain(|&x| x > end); + } else { + final_output.push(current.to_string()); + } + } + + final_output.join(",") +} + +/// Parses a protocol list without validating the protocol names +/// +/// # Inputs +/// +/// * `protocol_string`, a string comprised of keys and values, both which are +/// strings. The keys are the protocol names while values are a string +/// representation of the supported versions. +/// +/// The input is _not_ expected to be a subset of the Proto types +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashSet<Version>` holding all of the +/// unique version numbers. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +/// # Errors +/// +/// This function will error if: +/// +/// * The protocol string does not follow the "protocol_name=version_list" +/// expected format +/// * If the version string is malformed. See `Versions::from_version_string`. +/// +fn parse_protocols_from_string_with_no_validation<'a>( + protocol_string: &'a str, +) -> Result<HashMap<String, Versions>, &'static str> { + let mut parsed: HashMap<String, Versions> = HashMap::new(); + + for subproto in protocol_string.split(" ") { + let mut parts = subproto.splitn(2, "="); + + let name = match parts.next() { + Some("") => return Err("invalid protover entry"), + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = Versions::from_version_string(vers)?; + + parsed.insert(String::from(name), versions); + } + Ok(parsed) +} + +/// Protocol voting implementation. +/// +/// Given a list of strings describing protocol versions, return a new +/// string encoding all of the protocols that are listed by at +/// least threshold of the inputs. +/// +/// The string is sorted according to the following conventions: +/// - Protocols names are alphabetized +/// - Protocols are in order low to high +/// - Individual and ranges are listed together. For example, +/// "3, 5-10,13" +/// - All entries are unique +/// +/// # Examples +/// ``` +/// use protover::compute_vote; +/// +/// let protos = vec![String::from("Link=3-4"), String::from("Link=3")]; +/// let vote = compute_vote(protos, 2); +/// assert_eq!("Link=3", vote) +/// ``` +pub fn compute_vote( + list_of_proto_strings: Vec<String>, + threshold: i32, +) -> String { + let empty = String::from(""); + + if list_of_proto_strings.is_empty() { + return empty; + } + + // all_count is a structure to represent the count of the number of + // supported versions for a specific protocol. For example, in JSON format: + // { + // "FirstSupportedProtocol": { + // "1": "3", + // "2": "1" + // } + // } + // means that FirstSupportedProtocol has three votes which support version + // 1, and one vote that supports version 2 + let mut all_count: HashMap<String, HashMap<Version, usize>> = + HashMap::new(); + + // parse and collect all of the protos and their versions and collect them + for vote in list_of_proto_strings { + let this_vote: HashMap<String, Versions> = + match parse_protocols_from_string_with_no_validation(&vote) { + Ok(result) => result, + Err(_) => continue, + }; + for (protocol, versions) in this_vote { + let supported_vers: &mut HashMap<Version, usize> = + all_count.entry(protocol).or_insert(HashMap::new()); + + for version in versions.0 { + let counter: &mut usize = + supported_vers.entry(version).or_insert(0); + *counter += 1; + } + } + } + + let mut final_output: HashMap<String, String> = + HashMap::with_capacity(get_supported_protocols().split(" ").count()); + + // Go through and remove verstions that are less than the threshold + for (protocol, versions) in all_count { + let mut meets_threshold = HashSet::new(); + for (version, count) in versions { + if count >= threshold as usize { + meets_threshold.insert(version); + } + } + + // For each protocol, compress its version list into the expected + // protocol version string format + let contracted = contract_protocol_list(&meets_threshold); + if !contracted.is_empty() { + final_output.insert(protocol, contracted); + } + } + + write_vote_to_string(&final_output) +} + +/// Return a String comprised of protocol entries in alphabetical order +/// +/// # Inputs +/// +/// * `vote`, a `HashMap` comprised of keys and values, both which are strings. +/// The keys are the protocol names while values are a string representation of +/// the supported versions. +/// +/// # Returns +/// +/// A `String` whose value is series of pairs, comprising of the protocol name +/// and versions that it supports. The string takes the following format: +/// +/// "first_protocol_name=1,2-5, second_protocol_name=4,5" +/// +/// Sorts the keys in alphabetical order and creates the expected subprotocol +/// entry format. +/// +fn write_vote_to_string(vote: &HashMap<String, String>) -> String { + let mut keys: Vec<&String> = vote.keys().collect(); + keys.sort(); + + let mut output = Vec::new(); + for key in keys { + // TODO error in indexing here? + output.push(format!("{}={}", key, vote[key])); + } + output.join(" ") +} + +/// Returns a boolean indicating whether the given protocol and version is +/// supported in any of the existing Tor protocols +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = is_supported_here(Proto::Link, 10); +/// assert_eq!(false, is_supported); +/// +/// let is_supported = is_supported_here(Proto::Link, 1); +/// assert_eq!(true, is_supported); +/// ``` +pub fn is_supported_here(proto: Proto, vers: Version) -> bool { + let currently_supported = match SupportedProtocols::tor_supported() { + Ok(result) => result.0, + Err(_) => return false, + }; + + let supported_versions = match currently_supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.0.contains(&vers) +} + +/// Older versions of Tor cannot infer their own subprotocols +/// Used to determine which subprotocols are supported by older Tor versions. +/// +/// # Inputs +/// +/// * `version`, a string comprised of "[0-9a-z.-]" +/// +/// # Returns +/// +/// A `&'static [u8]` encoding a list of protocol names and supported +/// versions. The string takes the following format: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +/// This function returns the protocols that are supported by the version input, +/// only for tor versions older than FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS. +/// +/// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor` +pub fn compute_for_old_tor(version: &str) -> &'static [u8] { + if c_tor_version_as_new_as(version, FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS) { + return NUL_BYTE; + } + + if c_tor_version_as_new_as(version, "0.2.9.1-alpha") { + return b"Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2\0"; + } + + if c_tor_version_as_new_as(version, "0.2.7.5") { + return b"Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2\0"; + } + + if c_tor_version_as_new_as(version, "0.2.4.19") { + return b"Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2\0"; + } + + NUL_BYTE +} + +#[cfg(test)] +mod test { + use super::Version; + + #[test] + fn test_versions_from_version_string() { + use std::collections::HashSet; + + use super::Versions; + + assert_eq!(Err("invalid protocol entry"), Versions::from_version_string("a,b")); + assert_eq!(Err("invalid protocol entry"), Versions::from_version_string("1,!")); + + { + let mut versions: HashSet<Version> = HashSet::new(); + versions.insert(1); + assert_eq!(versions, Versions::from_version_string("1").unwrap().0); + } + { + let mut versions: HashSet<Version> = HashSet::new(); + versions.insert(1); + versions.insert(2); + assert_eq!(versions, Versions::from_version_string("1,2").unwrap().0); + } + { + let mut versions: HashSet<Version> = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + assert_eq!(versions, Versions::from_version_string("1-3").unwrap().0); + } + { + let mut versions: HashSet<Version> = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(5); + assert_eq!(versions, Versions::from_version_string("1-2,5").unwrap().0); + } + { + let mut versions: HashSet<Version> = HashSet::new(); + versions.insert(1); + versions.insert(3); + versions.insert(4); + versions.insert(5); + assert_eq!(versions, Versions::from_version_string("1,3-5").unwrap().0); + } + } + + #[test] + fn test_contains_only_supported_protocols() { + use super::contains_only_supported_protocols; + + assert_eq!(false, contains_only_supported_protocols("")); + assert_eq!(true, contains_only_supported_protocols("Cons=")); + assert_eq!(true, contains_only_supported_protocols("Cons=1")); + assert_eq!(false, contains_only_supported_protocols("Cons=0")); + assert_eq!(false, contains_only_supported_protocols("Cons=0-1")); + assert_eq!(false, contains_only_supported_protocols("Cons=5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1-5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5")); + assert_eq!(false, contains_only_supported_protocols("Cons=5,6")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5,6")); + assert_eq!(true, contains_only_supported_protocols("Cons=1,2")); + assert_eq!(true, contains_only_supported_protocols("Cons=1-2")); + } + + #[test] + fn test_find_range() { + use super::find_range; + + assert_eq!((false, 0), find_range(&vec![])); + assert_eq!((false, 1), find_range(&vec![1])); + assert_eq!((true, 2), find_range(&vec![1, 2])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3, 5])); + } + + #[test] + fn test_expand_version_range() { + use super::expand_version_range; + + assert_eq!(Err("version string empty"), expand_version_range("")); + assert_eq!(Ok(1..3), expand_version_range("1-2")); + assert_eq!(Ok(1..5), expand_version_range("1-4")); + assert_eq!( + Err("cannot parse protocol range lower bound"), + expand_version_range("a") + ); + assert_eq!( + Err("cannot parse protocol range upper bound"), + expand_version_range("1-a") + ); + assert_eq!(Ok(1000..66536), expand_version_range("1000-66535")); + assert_eq!(Err("Too many protocols in expanded range"), + expand_version_range("1000-66536")); + } + + #[test] + fn test_contract_protocol_list() { + use std::collections::HashSet; + use super::contract_protocol_list; + + { + let mut versions = HashSet::<Version>::new(); + assert_eq!(String::from(""), contract_protocol_list(&versions)); + + versions.insert(1); + assert_eq!(String::from("1"), contract_protocol_list(&versions)); + + versions.insert(2); + assert_eq!(String::from("1-2"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<Version>::new(); + versions.insert(1); + versions.insert(3); + assert_eq!(String::from("1,3"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<Version>::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(4); + assert_eq!(String::from("1-4"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<Version>::new(); + versions.insert(1); + versions.insert(3); + versions.insert(5); + versions.insert(6); + versions.insert(7); + assert_eq!( + String::from("1,3,5-7"), + contract_protocol_list(&versions) + ); + } + + { + let mut versions = HashSet::<Version>::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(500); + assert_eq!( + String::from("1-3,500"), + contract_protocol_list(&versions) + ); + } + } +} diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs new file mode 100644 index 0000000000..f4e394b3e2 --- /dev/null +++ b/src/rust/protover/tests/protover.rs @@ -0,0 +1,291 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +extern crate protover; + +#[test] +fn parse_protocol_list_with_single_proto_and_single_version() { + let protocol = "Cons=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_multiple_versions() { + let protocol = "Cons=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_different_single_protocol_and_single_version() { + let protocol = "HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_supported_version() { + let protocol = "Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_two_protocols_and_single_version() { + let protocols = "Cons=1 HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions() { + let protocol = "Desc=1,2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_sequential_versions() { + let protocol = "Desc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_range_returns_set() { + let protocol = "Link=1-4"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_set() { + let protocols = "Link=3-4 Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_two_values() { + let protocols = "Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_one_value() { + let protocols = "Microdesc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_empty() { + let protocols = ""; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_three_values() { + let protocols = "LinkAuth=1 Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_unsupported_protocol() { + let protocols = "Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_versions() { + let protocols = "Link=3-999"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Link=3-999", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_low_version() { + let protocols = "Cons=0-1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=0-1", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_high_version() { + let protocols = "Cons=1-3"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=1-3", &unsupported); +} + +#[test] +fn protover_all_supported_with_mix_of_supported_and_unsupproted() { + let protocols = "Link=3-4 Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_string_supports_protocol_returns_true_for_single_supported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 1, + ); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_for_single_unsupported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_for_unsupported() { + let protocols = "Link=3-4"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_all_supported_with_unexpected_characters() { + let protocols = "Cons=*-%"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=*-%", &unsupported); +} + +#[test] +fn protover_compute_vote_returns_empty_for_empty_string() { + let protocols = vec![String::from("")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("", listed); +} + +#[test] +fn protover_compute_vote_returns_single_protocol_for_matching() { + let protocols = vec![String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_two_protocols_for_two_matching() { + let protocols = vec![String::from("Link=1 Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1 Link=1", listed); +} + +#[test] +fn protover_compute_vote_returns_one_protocol_when_one_out_of_two_matches() { + let protocols = vec![String::from("Cons=1 Link=2"), String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() { + let protocols = vec![String::from("Foo=1 Cons=2"), String::from("Bar=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Bar=1 Cons=2 Foo=1", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_mix() { + let protocols = vec![String::from("Link=1-10,500 Cons=1,3-7,8")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1,3-8 Link=1-10,500", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1-8 Desc=1-10,500 Link=78,123-456", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=3-6,8 Desc=9", listed); +} + +#[test] +fn protover_compute_vote_handles_duplicated_versions() { + let protocols = vec![String::from("Cons=1"), String::from("Cons=1")]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![String::from("Cons=1-2"), String::from("Cons=1-2")]; + assert_eq!("Cons=1-2", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_compute_vote_handles_invalid_proto_entries() { + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=a"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=1-%"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_is_supported_here_returns_true_for_supported_protocol() { + assert_eq!(true, protover::is_supported_here(protover::Proto::Cons, 1)); +} + +#[test] +fn protover_is_supported_here_returns_false_for_unsupported_protocol() { + assert_eq!(false, protover::is_supported_here(protover::Proto::Cons, 5)); +} diff --git a/src/rust/smartlist/Cargo.toml b/src/rust/smartlist/Cargo.toml new file mode 100644 index 0000000000..51f486c4d7 --- /dev/null +++ b/src/rust/smartlist/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "smartlist" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "smartlist" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/smartlist/lib.rs b/src/rust/smartlist/lib.rs new file mode 100644 index 0000000000..14a8148315 --- /dev/null +++ b/src/rust/smartlist/lib.rs @@ -0,0 +1,8 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +extern crate libc; + +mod smartlist; + +pub use smartlist::*; diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs new file mode 100644 index 0000000000..2a822d89f4 --- /dev/null +++ b/src/rust/smartlist/smartlist.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use std::slice; +use libc::{c_char, c_int}; +use std::ffi::CStr; + +/// Smartlists are a type used in C code in tor to define a collection of a +/// generic type, which has a capacity and a number used. Each Smartlist +/// defines how to extract the list of values from the underlying C structure +/// +/// Implementations are required to have a C representation, as this module +/// serves purely to translate smartlists as defined in tor to vectors in Rust. +pub trait Smartlist<T> { + fn get_list(&self) -> Vec<T>; +} + +#[repr(C)] +pub struct Stringlist { + pub list: *const *const c_char, + pub num_used: c_int, + pub capacity: c_int, +} + +impl Smartlist<String> for Stringlist { + fn get_list(&self) -> Vec<String> { + let empty: Vec<String> = Vec::new(); + let mut rust_list: Vec<String> = Vec::new(); + + if self.list.is_null() || self.num_used == 0 { + return empty; + } + + // unsafe, as we need to extract the smartlist list into a vector of + // pointers, and then transform each element into a Rust string. + let elems: &[*const c_char] = + unsafe { slice::from_raw_parts(self.list, self.num_used as usize) }; + + for elem in elems.iter() { + if elem.is_null() { + continue; + } + + // unsafe, as we need to create a cstring from the referenced + // element + let c_string = unsafe { CStr::from_ptr(*elem) }; + + let r_string = match c_string.to_str() { + Ok(n) => n, + Err(_) => return empty, + }; + + rust_list.push(String::from(r_string)); + } + + rust_list + } +} + +// TODO: CHK: this module maybe should be tested from a test in C with a +// smartlist as defined in tor. +#[cfg(test)] +mod test { + #[test] + fn test_get_list_of_strings() { + extern crate libc; + + use std::ffi::CString; + use libc::c_char; + + use super::Smartlist; + use super::Stringlist; + + { + // test to verify that null pointers are gracefully handled + use std::ptr; + + let sl = Stringlist { + list: ptr::null(), + num_used: 0, + capacity: 0, + }; + + let data = sl.get_list(); + assert_eq!(0, data.len()); + } + + { + let args = vec![String::from("a"), String::from("b")]; + + // for each string, transform it into a CString + let c_strings: Vec<_> = args.iter() + .map(|arg| CString::new(arg.as_str()).unwrap()) + .collect(); + + // then, collect a pointer for each CString + let p_args: Vec<_> = + c_strings.iter().map(|arg| arg.as_ptr()).collect(); + + let p: *const *const c_char = p_args.as_ptr(); + + // This is the representation that we expect when receiving a + // smartlist at the Rust/C FFI layer. + let sl = Stringlist { + list: p, + num_used: 2, + capacity: 2, + }; + + let data = sl.get_list(); + assert_eq!("a", &data[0]); + assert_eq!("b", &data[1]); + } + } +} diff --git a/src/rust/tor_allocate/Cargo.toml b/src/rust/tor_allocate/Cargo.toml new file mode 100644 index 0000000000..ceb08b78ab --- /dev/null +++ b/src/rust/tor_allocate/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "tor_allocate" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "tor_allocate" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs new file mode 100644 index 0000000000..937a5dcf63 --- /dev/null +++ b/src/rust/tor_allocate/lib.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! Allocation helper functions that allow data to be allocated in Rust +//! using tor's specified allocator. In doing so, this can be later freed +//! from C. +//! +//! This is currently a temporary solution, we will later use tor's allocator +//! by default for any allocation that occurs in Rust. However, as this will +//! stabalize in 2018, we can use this as a temporary measure. + +extern crate libc; + +mod tor_allocate; +pub use tor_allocate::*; diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs new file mode 100644 index 0000000000..359df1cd7a --- /dev/null +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -0,0 +1,97 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use libc::{c_char, c_void}; +use std::{ptr, slice, mem}; + +#[cfg(not(test))] +extern "C" { + fn tor_malloc_(size: usize) -> *mut c_void; +} + +// Defined only for tests, used for testing purposes, so that we don't need +// to link to tor C files. Uses the system allocator +#[cfg(test)] +unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void { + use libc::malloc; + malloc(size) +} + +/// Allocate memory using tor_malloc_ and copy an existing string into the +/// allocated buffer, returning a pointer that can later be called in C. +/// +/// # Inputs +/// +/// * `src`, a reference to a String. +/// +/// # Returns +/// +/// A `*mut c_char` that should be freed by tor_free in C +/// +pub fn allocate_and_copy_string(src: &String) -> *mut c_char { + let bytes: &[u8] = src.as_bytes(); + + let size = mem::size_of_val::<[u8]>(bytes); + let size_one_byte = mem::size_of::<u8>(); + + // handle integer overflow when adding one to the calculated length + let size_with_null_byte = match size.checked_add(size_one_byte) { + Some(n) => n, + None => return ptr::null_mut(), + }; + + let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; + + if dest.is_null() { + return ptr::null_mut(); + } + + unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) }; + + // set the last byte as null, using the ability to index into a slice + // rather than doing pointer arithmatic + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; + slice[size] = 0; // add a null terminator + + dest as *mut c_char +} + +#[cfg(test)] +mod test { + + #[test] + fn test_allocate_and_copy_string_with_empty() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::new(); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; + + assert_eq!("", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } + + #[test] + fn test_allocate_and_copy_string_with_not_empty_string() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::from("foo bar biz"); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; + + assert_eq!("foo bar biz", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } +} diff --git a/src/rust/tor_rust/Cargo.toml b/src/rust/tor_rust/Cargo.toml new file mode 100644 index 0000000000..86fad3ee76 --- /dev/null +++ b/src/rust/tor_rust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Tor Project"] +name = "tor_rust" +version = "0.1.0" + +[lib] +name = "tor_rust" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies.tor_util] +path = "../tor_util" + +[dependencies.protover] +path = "../protover" + diff --git a/src/rust/tor_rust/include.am b/src/rust/tor_rust/include.am new file mode 100644 index 0000000000..c02324cb77 --- /dev/null +++ b/src/rust/tor_rust/include.am @@ -0,0 +1,30 @@ +EXTRA_DIST +=\ + src/rust/tor_rust/Cargo.toml \ + src/rust/tor_rust/lib.rs + +EXTRA_CARGO_OPTIONS= + +src/rust/target/release/@TOR_RUST_STATIC_NAME@: FORCE + ( cd "$(abs_top_builddir)/src/rust" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release $(EXTRA_CARGO_OPTIONS) \ + $(CARGO_ONLINE) \ + --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" ) + +distclean-rust: + ( cd "$(abs_top_builddir)/src/rust" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) clean $(EXTRA_CARGO_OPTIONS) \ + $(CARGO_ONLINE) \ + --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" ) + rm -rf "$(abs_top_builddir)/src/rust/registry" + +if USE_RUST +build-rust: src/rust/target/release/@TOR_RUST_STATIC_NAME@ +else +build-rust: +endif + +FORCE: diff --git a/src/rust/tor_rust/lib.rs b/src/rust/tor_rust/lib.rs new file mode 100644 index 0000000000..c1585c0480 --- /dev/null +++ b/src/rust/tor_rust/lib.rs @@ -0,0 +1,5 @@ +extern crate tor_util; +extern crate protover; + +pub use tor_util::*; +pub use protover::*; diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml index f175fbdfb0..d7379a5988 100644 --- a/src/rust/tor_util/Cargo.toml +++ b/src/rust/tor_util/Cargo.toml @@ -8,6 +8,9 @@ name = "tor_util" path = "lib.rs" crate_type = ["rlib", "staticlib"] +[dependencies.tor_allocate] +path = "../tor_allocate" + [dependencies] -libc = "*" +libc = "0.2.22" diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs index af4bfc41af..5c3cdba4be 100644 --- a/src/rust/tor_util/ffi.rs +++ b/src/rust/tor_util/ffi.rs @@ -1,56 +1,26 @@ -//! FFI functions, only to be called from C. -//! -//! Equivalent C versions of these live in `src/common/compat_rust.c` - -use std::mem::forget; -use std::ffi::CString; +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ -use libc; -use rust_string::RustString; - -/// Free the passed `RustString` (`rust_str_t` in C), to be used in place of -/// `tor_free`(). -/// -/// # Examples -/// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// rust_str_free(r_s); -/// ``` -#[no_mangle] -#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] -pub unsafe extern "C" fn rust_str_free(_str: RustString) { - // Empty body: Just drop _str and we're done (Drop takes care of it). -} +//! FFI functions to announce Rust support during tor startup, only to be +//! called from C. +//! -/// Lends an immutable, NUL-terminated C String. -/// -/// # Examples -/// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// const char *s = rust_str_get(r_s); -/// printf("%s", s); -/// rust_str_free(r_s); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn rust_str_get(str: RustString) -> *const libc::c_char { - let res = str.as_ptr(); - forget(str); - res -} +use libc::c_char; +use tor_allocate::allocate_and_copy_string; /// Returns a short string to announce Rust support during startup. /// /// # Examples /// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// const char *s = rust_str_get(r_s); -/// printf("%s", s); -/// rust_str_free(r_s); +/// char *rust_str = rust_welcome_string(); +/// printf("%s", rust_str); +/// tor_free(rust_str); /// ``` #[no_mangle] -pub extern "C" fn rust_welcome_string() -> RustString { - let s = CString::new("Tor is running with Rust integration. Please report \ - any bugs you encouter.") - .unwrap(); - RustString::from(s) +pub extern "C" fn rust_welcome_string() -> *mut c_char { + let rust_welcome = String::from( + "Tor is running with Rust integration. Please report \ + any bugs you encounter.", + ); + allocate_and_copy_string(&rust_welcome) } diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am deleted file mode 100644 index ec3898577b..0000000000 --- a/src/rust/tor_util/include.am +++ /dev/null @@ -1,13 +0,0 @@ -EXTRA_DIST +=\ - src/rust/tor_util/Cargo.toml \ - src/rust/tor_util/lib.rs \ - src/rust/tor_util/ffi.rs \ - src/rust/tor_util/rust_string.rs - -src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs index 79d583d1ae..12cb3896b6 100644 --- a/src/rust/tor_util/lib.rs +++ b/src/rust/tor_util/lib.rs @@ -1,13 +1,11 @@ -//! C <-> Rust compatibility helpers and types. -//! -//! Generically useful, small scale helpers should go here. This goes for both -//! the C side (in the form of the ffi module) as well as the Rust side -//! (individual modules per functionality). The corresponding C stuff lives in -//! `src/common/compat_rust.{c,h}`. +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! Small module to announce Rust support during startup for demonstration +//! purposes. extern crate libc; +extern crate tor_allocate; -mod rust_string; pub mod ffi; - -pub use rust_string::*; +pub mod strings; diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs deleted file mode 100644 index 46ec3fd7a8..0000000000 --- a/src/rust/tor_util/rust_string.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::ffi::CString; -use std::mem::forget; -use libc; - -/// Compatibility wrapper for strings allocated in Rust and passed to C. -/// -/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so -/// we need to take special care to ensure we're not accidentally calling -/// `tor_free`() on any string allocated in Rust. To more easily differentiate -/// between strings that possibly (if Rust support is enabled) were allocated -/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust -/// side is `RustString`. -/// -/// Note: This type must not be used for strings allocated in C. -#[repr(C)] -#[derive(Debug)] -pub struct RustString(*mut libc::c_char); - -impl RustString { - /// Returns a pointer to the underlying NUL-terminated byte array. - /// - /// Note that this function is not typically useful for Rust callers, - /// except in a direct FFI context. - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let r = RustString::from(CString::new("asdf").unwrap()); - /// let c_str = r.as_ptr(); - /// assert_eq!(b'a', unsafe { *c_str as u8}); - /// ``` - pub fn as_ptr(&self) -> *const libc::c_char { - self.0 as *const libc::c_char - } -} - -impl From<CString> for RustString { - /// Constructs a new `RustString` - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let r = RustString::from(CString::new("asdf").unwrap()); - /// ``` - fn from(str: CString) -> RustString { - RustString(str.into_raw()) - } -} - -impl Into<CString> for RustString { - /// Reconstructs a `CString` from this `RustString`. - /// - /// Useful to take ownership back from a `RustString` that was given to C - /// code. - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let cs = CString::new("asdf").unwrap(); - /// let r = RustString::from(cs.clone()); - /// let cs2 = r.into(); - /// assert_eq!(cs, cs2); - /// ``` - fn into(self) -> CString { - // Calling from_raw is always OK here: We only construct self using - // valid CStrings and don't expose anything that could mutate it - let ret = unsafe { CString::from_raw(self.0) }; - forget(self); - ret - } -} - -impl Drop for RustString { - fn drop(&mut self) { - // Don't use into() here, because we would need to move out of - // self. Same safety consideration. Immediately drop the created - // CString, which takes care of freeing the wrapped string. - unsafe { CString::from_raw(self.0) }; - } -} - -#[cfg(test)] -mod test { - use std::mem; - use super::*; - - use libc; - - /// Ensures we're not adding overhead by using RustString. - #[test] - fn size_of() { - assert_eq!(mem::size_of::<*mut libc::c_char>(), - mem::size_of::<RustString>()) - } -} diff --git a/src/rust/tor_util/strings.rs b/src/rust/tor_util/strings.rs new file mode 100644 index 0000000000..9321ce4f85 --- /dev/null +++ b/src/rust/tor_util/strings.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! Utilities for working with static strings. + +use std::ffi::CStr; + +/// A byte-array containing a single NUL byte (`b"\0"`). +pub const NUL_BYTE: &'static [u8] = b"\0"; + +/// Determine if a byte slice is a C-like string. +/// +/// These checks guarantee that: +/// +/// 1. there are no intermediate NUL bytes +/// 2. the last byte *is* a NUL byte +/// +/// # Warning +/// +/// This function does _not_ guarantee that the bytes represent any valid +/// encoding such as ASCII or UTF-8. +/// +/// # Examples +/// +/// ``` +/// # use tor_util::strings::byte_slice_is_c_like; +/// # +/// let bytes: &[u8] = b"foo bar baz"; +/// +/// assert!(byte_slice_is_c_like(&bytes) == false); +/// +/// let bytes: &[u8] = b"foo\0bar baz"; +/// +/// assert!(byte_slice_is_c_like(&bytes) == false); +/// +/// let bytes: &[u8] = b"foo bar baz\0"; +/// +/// assert!(byte_slice_is_c_like(&bytes) == true); +/// ``` +pub fn byte_slice_is_c_like(bytes: &[u8]) -> bool { + if !bytes[..bytes.len() - 1].contains(&0x00) && bytes[bytes.len() - 1] == 0x00 { + return true; + } + false +} + +/// Get a static `CStr` containing a single `NUL_BYTE`. +/// +/// # Examples +/// +/// When used as follows in a Rust FFI function, which could be called +/// from C: +/// +/// ``` +/// # extern crate libc; +/// # extern crate tor_util; +/// # +/// # use tor_util::strings::empty_static_cstr; +/// use libc::c_char; +/// use std::ffi::CStr; +/// +/// pub extern "C" fn give_c_code_an_empty_static_string() -> *const c_char { +/// let empty: &'static CStr = empty_static_cstr(); +/// +/// empty.as_ptr() +/// } +/// +/// # fn main() { +/// # give_c_code_an_empty_static_string(); +/// # } +/// ``` +/// +/// This equates to an "empty" `const char*` static string in C. +pub fn empty_static_cstr() -> &'static CStr { + let empty: &'static CStr; + + unsafe { + empty = CStr::from_bytes_with_nul_unchecked(NUL_BYTE); + } + + empty +} diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs deleted file mode 100644 index 1ff605a43c..0000000000 --- a/src/rust/tor_util/tests/rust_string.rs +++ /dev/null @@ -1,37 +0,0 @@ -extern crate tor_util; -extern crate libc; - -use std::ffi::CString; -use tor_util::RustString; - -#[test] -fn rust_string_conversions_preserve_c_string() { - let s = CString::new("asdf foo").unwrap(); - let r = RustString::from(s.clone()); - let r2 = RustString::from(s.clone()); - let c = r2.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 8); - let c_str = r.into(); - assert_eq!(s, c_str); -} - -#[test] -fn empty_string() { - let s = CString::new("").unwrap(); - let r = RustString::from(s.clone()); - let c = r.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 0); - let c_str = r.into(); - assert_eq!(s, c_str); -} - -#[test] -fn c_string_with_unicode() { - // The euro sign is three bytes - let s = CString::new("asd€asd").unwrap(); - let r = RustString::from(s.clone()); - let c = r.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 9); - let c_str = r.into(); - assert_eq!(s, c_str); -} diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 605f1a92c3..cfbe281b94 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -18,6 +18,7 @@ TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ test_config.obj test_connection.obj \ test_cell_formats.obj test_relay.obj test_replay.obj \ test_channelpadding.obj \ + test_circuitstats.obj \ test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj tinytest.obj: ..\ext\tinytest.c diff --git a/src/test/bench.c b/src/test/bench.c index b7b123eee2..24ff8b255c 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -3,11 +3,6 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; -/* Ordinarily defined in tor_main.c; this bit is just here to provide one - * since we're not linking to tor_main.c */ -const char tor_git_revision[] = ""; - /** * \file bench.c * \brief Benchmarks for lower level Tor modules. @@ -723,6 +718,8 @@ main(int argc, const char **argv) init_logging(1); options->command = CMD_RUN_UNITTESTS; options->DataDirectory = tor_strdup(""); + options->KeyDirectory = tor_strdup(""); + options->CacheDirectory = tor_strdup(""); options_init(options); if (set_options(options, &errmsg) < 0) { printf("Failed to set initial options: %s\n", errmsg); diff --git a/src/test/fakechans.h b/src/test/fakechans.h index c0de430e3d..ab5d8461b6 100644 --- a/src/test/fakechans.h +++ b/src/test/fakechans.h @@ -20,7 +20,6 @@ void scheduler_release_channel_mock(channel_t *ch); /* Query some counters used by the exposed mocks */ int get_mock_scheduler_has_waiting_cells_count(void); -int get_mock_scheduler_release_channel_count(void); #endif /* !defined(TOR_FAKECHANS_H) */ diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 1e98eb6c85..a96552f0fc 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -9,9 +9,6 @@ #include "crypto.h" #include "crypto_ed25519.h" -extern const char tor_git_revision[]; -const char tor_git_revision[] = ""; - static or_options_t *mock_options = NULL; static const or_options_t * mock_get_options(void) @@ -113,6 +110,9 @@ global_init(void) /* Make BUG() and nonfatal asserts crash */ tor_set_failed_assertion_callback(abort); + + /* Make protocol warnings handled correctly. */ + init_protocol_warning_severity_level(); } #ifdef LLVM_FUZZ diff --git a/src/test/include.am b/src/test/include.am index 230845a1ed..1a49367c6b 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -99,6 +99,7 @@ src_test_test_SOURCES = \ src/test/test_circuitmux.c \ src/test/test_circuitbuild.c \ src/test/test_circuituse.c \ + src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ @@ -110,11 +111,11 @@ src_test_test_SOURCES = \ src/test/test_controller_events.c \ src/test/test_crypto.c \ src/test/test_crypto_openssl.c \ - src/test/test_dos.c \ src/test/test_data.c \ src/test/test_dir.c \ src/test/test_dir_common.c \ src/test/test_dir_handle_get.c \ + src/test/test_dos.c \ src/test/test_entryconn.c \ src/test/test_entrynodes.c \ src/test/test_guardfraction.c \ @@ -127,6 +128,7 @@ src_test_test_SOURCES = \ src/test/test_hs_service.c \ src/test/test_hs_client.c \ src/test/test_hs_intropoint.c \ + src/test/test_hs_control.c \ src/test/test_handles.c \ src/test/test_hs_cache.c \ src/test/test_hs_descriptor.c \ @@ -154,7 +156,6 @@ src_test_test_SOURCES = \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ - src/test/test_rust.c \ src/test/test_scheduler.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ @@ -358,3 +359,5 @@ EXTRA_DIST += \ src/test/test_workqueue_pipe2.sh \ src/test/test_workqueue_socketpair.sh +test-rust: + $(TESTS_ENVIRONMENT) $(abs_top_srcdir)/src/test/test_rust.sh diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 484f13dd05..89d946d506 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "orconfig.h" #include <string.h> #include <stdio.h> diff --git a/src/test/test-timers.c b/src/test/test-timers.c index 99715f4333..a0b5b535c2 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -133,7 +133,7 @@ main(int argc, char **argv) ret = 0; } - timer_free(NULL); + timer_free_(NULL); for (i = 0; i < N_TIMERS; ++i) { timer_free(timers[i]); diff --git a/src/test/test.c b/src/test/test.c index 383bc00002..2712de4ed1 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -62,7 +62,6 @@ double fabs(double x); #include "routerparse.h" #include "statefile.h" #include "crypto_curve25519.h" -#include "onion_ntor.h" /** Run unit tests for the onion handshake code. */ static void @@ -174,7 +173,7 @@ test_bad_onion_handshake(void *arg) s_buf, s_keys, 40)); c_buf[64] ^= 33; - /* (Let the server procede) */ + /* (Let the server proceed) */ tt_int_op(0, OP_EQ, onion_skin_TAP_server_handshake(c_buf, pk, NULL, s_buf, s_keys, 40)); @@ -345,8 +344,8 @@ test_onion_queues(void *arg) tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); done: - circuit_free(TO_CIRCUIT(circ1)); - circuit_free(TO_CIRCUIT(circ2)); + circuit_free_(TO_CIRCUIT(circ1)); + circuit_free_(TO_CIRCUIT(circ2)); tor_free(create1); tor_free(create2); tor_free(onionskin); @@ -616,7 +615,7 @@ test_rend_fns(void *arg) done: if (descs) { for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_free(descs); } if (parsed) @@ -1197,6 +1196,7 @@ struct testgroup_t testgroups[] = { { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, { "circuituse/", circuituse_tests }, + { "circuitstats/", circuitstats_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, @@ -1208,10 +1208,10 @@ struct testgroup_t testgroups[] = { { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, { "crypto/openssl/", crypto_openssl_tests }, - { "dos/", dos_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, { "dir/md/", microdesc_tests }, + { "dos/", dos_tests }, { "entryconn/", entryconn_tests }, { "entrynodes/", entrynodes_tests }, { "guardfraction/", guardfraction_tests }, @@ -1221,6 +1221,7 @@ struct testgroup_t testgroups[] = { { "hs_cell/", hs_cell_tests }, { "hs_common/", hs_common_tests }, { "hs_config/", hs_config_tests }, + { "hs_control/", hs_control_tests }, { "hs_descriptor/", hs_descriptor }, { "hs_ntor/", hs_ntor_tests }, { "hs_service/", hs_service_tests }, @@ -1247,7 +1248,6 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, - { "rust/", rust_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, { "shared-random/", sr_tests }, diff --git a/src/test/test.h b/src/test/test.h index 3c12d2b673..26139fc5fe 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -189,6 +189,7 @@ extern struct testcase_t circuitbuild_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; extern struct testcase_t circuituse_tests[]; +extern struct testcase_t circuitstats_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; @@ -200,9 +201,9 @@ extern struct testcase_t controller_tests[]; extern struct testcase_t controller_event_tests[]; extern struct testcase_t crypto_tests[]; extern struct testcase_t crypto_openssl_tests[]; -extern struct testcase_t dos_tests[]; extern struct testcase_t dir_tests[]; extern struct testcase_t dir_handle_get_tests[]; +extern struct testcase_t dos_tests[]; extern struct testcase_t entryconn_tests[]; extern struct testcase_t entrynodes_tests[]; extern struct testcase_t guardfraction_tests[]; @@ -212,6 +213,7 @@ extern struct testcase_t hs_cache[]; extern struct testcase_t hs_cell_tests[]; extern struct testcase_t hs_common_tests[]; extern struct testcase_t hs_config_tests[]; +extern struct testcase_t hs_control_tests[]; extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_ntor_tests[]; extern struct testcase_t hs_service_tests[]; @@ -241,7 +243,6 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; -extern struct testcase_t rust_tests[]; extern struct testcase_t scheduler_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t socks_tests[]; diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c index 7edba988a6..b0d37b2989 100644 --- a/src/test/test_accounting.c +++ b/src/test/test_accounting.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "or.h" #include "test.h" #define HIBERNATE_PRIVATE diff --git a/src/test/test_address.c b/src/test/test_address.c index f36ff6998b..9c88d37a41 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -763,7 +763,7 @@ test_address_get_if_addrs_list_internal(void *arg) tt_assert(!smartlist_contains_ipv6_tor_addr(results)); done: - free_interface_address_list(results); + interface_address_list_free(results); return; } @@ -792,7 +792,7 @@ test_address_get_if_addrs_list_no_internal(void *arg) tt_assert(!smartlist_contains_ipv6_tor_addr(results)); done: - free_interface_address_list(results); + interface_address_list_free(results); return; } @@ -834,7 +834,7 @@ test_address_get_if_addrs6_list_internal(void *arg) } done: - free_interface_address6_list(results); + interface_address6_list_free(results); teardown_capture_of_logs(); return; } @@ -878,7 +878,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg) done: teardown_capture_of_logs(); - free_interface_address6_list(results); + interface_address6_list_free(results); return; } @@ -943,8 +943,8 @@ test_address_get_if_addrs_internal_fail(void *arg) done: UNMOCK(get_interface_addresses_raw); UNMOCK(get_interface_address6_via_udp_socket_hack); - free_interface_address6_list(results1); - free_interface_address6_list(results2); + interface_address6_list_free(results1); + interface_address6_list_free(results2); return; } @@ -971,8 +971,8 @@ test_address_get_if_addrs_no_internal_fail(void *arg) done: UNMOCK(get_interface_addresses_raw); UNMOCK(get_interface_address6_via_udp_socket_hack); - free_interface_address6_list(results1); - free_interface_address6_list(results2); + interface_address6_list_free(results1); + interface_address6_list_free(results2); return; } diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 9f4e19bc5f..057d9fa2dc 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -450,52 +450,54 @@ test_buffer_time_tracking(void *arg) tt_assert(buf); monotime_coarse_set_mock_time_nsec(START_NSEC); - const uint32_t START_MSEC = (uint32_t)monotime_coarse_absolute_msec(); + const uint32_t START_TS = monotime_coarse_get_stamp(); /* Empty buffer means the timestamp is 0. */ - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC)); - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000)); buf_add(buf, "ABCDEFG", 7); - tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000)); buf2 = buf_copy(buf); tt_assert(buf2); tt_int_op(1234, OP_EQ, - buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234)); + buf_get_oldest_chunk_timestamp(buf2, START_TS+1234)); /* Now add more bytes; enough to overflow the first chunk. */ monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000); + const uint32_t TS2 = monotime_coarse_get_stamp(); for (i = 0; i < 600; ++i) buf_add(buf, "ABCDEFG", 7); tt_int_op(4207, OP_EQ, buf_datalen(buf)); /* The oldest bytes are still in the front. */ - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000)); /* Once those bytes are dropped, the chunk is still on the first * timestamp. */ buf_get_bytes(buf, tmp, 100); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000)); /* But once we discard the whole first chunk, we get the data in the second * chunk. */ buf_get_bytes(buf, tmp, 4000); tt_int_op(107, OP_EQ, buf_datalen(buf)); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000)); /* This time we'll be grabbing a chunk from the freelist, and making sure its time gets updated */ monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000); + const uint32_t TS3 = monotime_coarse_get_stamp(); for (i = 0; i < 600; ++i) buf_add(buf, "ABCDEFG", 7); tt_int_op(4307, OP_EQ, buf_datalen(buf)); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000)); buf_get_bytes(buf, tmp, 4000); buf_get_bytes(buf, tmp, 306); - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617)); - tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3)); + tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3+383)); done: buf_free(buf); diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 007f7e3d3e..88cdef383f 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -477,7 +477,7 @@ test_cfmt_create_cells(void *arg) cell.command = CELL_CREATED; tt_int_op(-1, OP_EQ, create_cell_parse(&cc, &cell)); - /* You can't acutally make an unparseable CREATE or CREATE_FAST cell. */ + /* You can't actually make an unparseable CREATE or CREATE_FAST cell. */ /* Try some CREATE2 cells. First with a bad type. */ cell.command = CELL_CREATE2; diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c index 69e89b69b0..df987f82ce 100644 --- a/src/test/test_cell_queue.c +++ b/src/test/test_cell_queue.c @@ -130,8 +130,8 @@ test_circuit_n_cells(void *arg) tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), OP_EQ, 2); done: - circuit_free(TO_CIRCUIT(or_c)); - circuit_free(TO_CIRCUIT(origin_c)); + circuit_free_(TO_CIRCUIT(or_c)); + circuit_free_(TO_CIRCUIT(origin_c)); } struct testcase_t cell_queue_tests[] = { diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 023c2950c9..bdc9d32f78 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -6,8 +6,10 @@ #include "or.h" #include "channel.h" /* For channel_note_destroy_not_pending */ +#define CIRCUITLIST_PRIVATE #include "circuitlist.h" #include "circuitmux.h" +#include "circuitmux_ewma.h" /* For var_cell_free */ #include "connection_or.h" /* For packed_cell stuff */ @@ -15,8 +17,10 @@ #include "relay.h" /* For init/free stuff */ #include "scheduler.h" +#include "networkstatus.h" /* Test suite stuff */ +#include "log_test_helpers.h" #include "test.h" #include "fakechans.h" @@ -26,62 +30,18 @@ static cell_t * test_chan_last_seen_fixed_cell_ptr = NULL; static int test_chan_var_cells_recved = 0; static var_cell_t * test_chan_last_seen_var_cell_ptr = NULL; static int test_cells_written = 0; -static int test_destroy_not_pending_calls = 0; static int test_doesnt_want_writes_count = 0; static int test_dumpstats_calls = 0; static int test_has_waiting_cells_count = 0; -static double test_overhead_estimate = 1.0; static int test_releases_count = 0; -static circuitmux_t *test_target_cmux = NULL; -static unsigned int test_cmux_cells = 0; static channel_t *dump_statistics_mock_target = NULL; static int dump_statistics_mock_matches = 0; - -static void chan_test_channel_dump_statistics_mock( - channel_t *chan, int severity); -static int chan_test_channel_flush_from_first_active_circuit_mock( - channel_t *chan, int max); -static unsigned int chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux); -static void channel_note_destroy_not_pending_mock(channel_t *ch, - circid_t circid); -static void chan_test_cell_handler(channel_t *ch, - cell_t *cell); -static const char * chan_test_describe_transport(channel_t *ch); -static void chan_test_dumpstats(channel_t *ch, int severity); -static void chan_test_var_cell_handler(channel_t *ch, - var_cell_t *var_cell); -static void chan_test_close(channel_t *ch); -static void chan_test_error(channel_t *ch); -static void chan_test_finish_close(channel_t *ch); -static const char * chan_test_get_remote_descr(channel_t *ch, int flags); -static int chan_test_is_canonical(channel_t *ch, int req); -static size_t chan_test_num_bytes_queued(channel_t *ch); -static int chan_test_num_cells_writeable(channel_t *ch); -static int chan_test_write_cell(channel_t *ch, cell_t *cell); -static int chan_test_write_packed_cell(channel_t *ch, - packed_cell_t *packed_cell); -static int chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell); -static void scheduler_channel_doesnt_want_writes_mock(channel_t *ch); - -static void test_channel_dumpstats(void *arg); -static void test_channel_flush(void *arg); -static void test_channel_flushmux(void *arg); -static void test_channel_incoming(void *arg); -static void test_channel_lifecycle(void *arg); -static void test_channel_multi(void *arg); -static void test_channel_queue_incoming(void *arg); -static void test_channel_queue_size(void *arg); -static void test_channel_write(void *arg); - -static void -channel_note_destroy_not_pending_mock(channel_t *ch, - circid_t circid) -{ - (void)ch; - (void)circid; - - ++test_destroy_not_pending_calls; -} +static int test_close_called = 0; +static int test_chan_should_be_canonical = 0; +static int test_chan_should_match_target = 0; +static int test_chan_canonical_should_be_reliable = 0; +static int test_chan_listener_close_fn_called = 0; +static int test_chan_listener_fn_called = 0; static const char * chan_test_describe_transport(channel_t *ch) @@ -112,71 +72,14 @@ chan_test_channel_dump_statistics_mock(channel_t *chan, int severity) return; } -/** - * If the target cmux is the cmux for chan, make fake cells up to the - * target number of cells and write them to chan. Otherwise, invoke - * the real channel_flush_from_first_active_circuit(). - */ - -static int -chan_test_channel_flush_from_first_active_circuit_mock(channel_t *chan, - int max) -{ - int result = 0, c = 0; - packed_cell_t *cell = NULL; - - tt_ptr_op(chan, OP_NE, NULL); - if (test_target_cmux != NULL && - test_target_cmux == chan->cmux) { - while (c <= max && test_cmux_cells > 0) { - cell = packed_cell_new(); - channel_write_packed_cell(chan, cell); - ++c; - --test_cmux_cells; - } - result = c; - } else { - result = channel_flush_from_first_active_circuit__real(chan, max); - } - - done: - return result; -} - -/** - * If we have a target cmux set and this matches it, lie about how - * many cells we have according to the number indicated; otherwise - * pass to the real circuitmux_num_cells(). - */ - -static unsigned int -chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux) -{ - unsigned int result = 0; - - tt_ptr_op(cmux, OP_NE, NULL); - if (cmux != NULL) { - if (cmux == test_target_cmux) { - result = test_cmux_cells; - } else { - result = circuitmux_num_cells__real(cmux); - } - } - - done: - - return result; -} - /* * Handle an incoming fixed-size cell for unit tests */ static void -chan_test_cell_handler(channel_t *ch, - cell_t *cell) +chan_test_cell_handler(channel_t *chan, cell_t *cell) { - tt_assert(ch); + tt_assert(chan); tt_assert(cell); test_chan_last_seen_fixed_cell_ptr = cell; @@ -226,6 +129,8 @@ chan_test_close(channel_t *ch) { tt_assert(ch); + ++test_close_called; + done: return; } @@ -274,35 +179,6 @@ chan_test_get_remote_descr(channel_t *ch, int flags) return "Fake channel for unit tests; no real endpoint"; } -static double -chan_test_get_overhead_estimate(channel_t *ch) -{ - tt_assert(ch); - - done: - return test_overhead_estimate; -} - -static int -chan_test_is_canonical(channel_t *ch, int req) -{ - tt_ptr_op(ch, OP_NE, NULL); - tt_assert(req == 0 || req == 1); - - done: - /* Fake channels are always canonical */ - return 1; -} - -static size_t -chan_test_num_bytes_queued(channel_t *ch) -{ - tt_assert(ch); - - done: - return 0; -} - static int chan_test_num_cells_writeable(channel_t *ch) { @@ -313,26 +189,6 @@ chan_test_num_cells_writeable(channel_t *ch) } static int -chan_test_write_cell(channel_t *ch, cell_t *cell) -{ - int rv = 0; - - tt_assert(ch); - tt_assert(cell); - - if (test_chan_accept_cells) { - /* Free the cell and bump the counter */ - tor_free(cell); - ++test_cells_written; - rv = 1; - } - /* else return 0, we didn't accept it */ - - done: - return rv; -} - -static int chan_test_write_packed_cell(channel_t *ch, packed_cell_t *packed_cell) { @@ -343,7 +199,6 @@ chan_test_write_packed_cell(channel_t *ch, if (test_chan_accept_cells) { /* Free the cell and bump the counter */ - packed_cell_free(packed_cell); ++test_cells_written; rv = 1; } @@ -419,36 +274,26 @@ new_fake_channel(void) channel_init(chan); chan->close = chan_test_close; - chan->get_overhead_estimate = chan_test_get_overhead_estimate; - chan->get_remote_descr = chan_test_get_remote_descr; - chan->num_bytes_queued = chan_test_num_bytes_queued; chan->num_cells_writeable = chan_test_num_cells_writeable; - chan->write_cell = chan_test_write_cell; + chan->get_remote_descr = chan_test_get_remote_descr; chan->write_packed_cell = chan_test_write_packed_cell; chan->write_var_cell = chan_test_write_var_cell; chan->state = CHANNEL_STATE_OPEN; + chan->cmux = circuitmux_alloc(); + return chan; } void free_fake_channel(channel_t *chan) { - cell_queue_entry_t *cell, *cell_tmp; - if (! chan) return; if (chan->cmux) circuitmux_free(chan->cmux); - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - tor_free(chan); } @@ -489,16 +334,6 @@ scheduler_channel_doesnt_want_writes_mock(channel_t *ch) } /** - * Counter query for scheduler_release_channel_mock() - */ - -int -get_mock_scheduler_release_channel_count(void) -{ - return test_releases_count; -} - -/** * Mock for scheduler_release_channel() */ @@ -513,6 +348,58 @@ scheduler_release_channel_mock(channel_t *ch) return; } +static int +test_chan_is_canonical(channel_t *chan, int req) +{ + tor_assert(chan); + + if (req && test_chan_canonical_should_be_reliable) { + return 1; + } + + if (test_chan_should_be_canonical) { + return 1; + } + return 0; +} + +static int +test_chan_matches_target(channel_t *chan, const tor_addr_t *target) +{ + (void) chan; + (void) target; + + if (test_chan_should_match_target) { + return 1; + } + return 0; +} + +static void +test_chan_listener_close(channel_listener_t *chan) +{ + (void) chan; + ++test_chan_listener_close_fn_called; + return; +} + +static void +test_chan_listener_fn(channel_listener_t *listener, channel_t *chan) +{ + (void) listener; + (void) chan; + + ++test_chan_listener_fn_called; + return; +} + +static const char * +test_chan_listener_describe_transport(channel_listener_t *chan) +{ + (void) chan; + return "Fake listener channel."; +} + /** * Test for channel_dumpstats() and limited test for * channel_dump_statistics() @@ -523,6 +410,7 @@ test_channel_dumpstats(void *arg) { channel_t *ch = NULL; cell_t *cell = NULL; + packed_cell_t *p_cell = NULL; int old_count; (void)arg; @@ -536,7 +424,6 @@ test_channel_dumpstats(void *arg) /* Set up a new fake channel */ ch = new_fake_channel(); tt_assert(ch); - ch->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch); @@ -579,24 +466,21 @@ test_channel_dumpstats(void *arg) /* Now make another channel */ ch = new_fake_channel(); tt_assert(ch); - ch->cmux = circuitmux_alloc(); channel_register(ch); - tt_assert(ch->registered); + tt_int_op(ch->registered, OP_EQ, 1); /* Lie about its age so dumpstats gets coverage for rate calculations */ ch->timestamp_created = time(NULL) - 30; - tt_assert(ch->timestamp_created > 0); - tt_assert(time(NULL) > ch->timestamp_created); + tt_int_op(ch->timestamp_created, OP_GT, 0); + tt_int_op(time(NULL), OP_GT, ch->timestamp_created); /* Put cells through it both ways to make the counters non-zero */ - cell = tor_malloc_zero(sizeof(*cell)); - make_fake_cell(cell); + p_cell = packed_cell_new(); test_chan_accept_cells = 1; old_count = test_cells_written; - channel_write_cell(ch, cell); - cell = NULL; + channel_write_packed_cell(ch, p_cell); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - tt_assert(ch->n_bytes_xmitted > 0); - tt_assert(ch->n_cells_xmitted > 0); + tt_u64_op(ch->n_bytes_xmitted, OP_GT, 0); + tt_u64_op(ch->n_cells_xmitted, OP_GT, 0); /* Receive path */ channel_set_cell_handlers(ch, @@ -605,19 +489,18 @@ test_channel_dumpstats(void *arg) tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, chan_test_var_cell_handler); - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); + cell = tor_malloc_zero(sizeof(*cell)); old_count = test_chan_fixed_cells_recved; - channel_queue_cell(ch, cell); - tor_free(cell); + channel_process_cell(ch, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); - tt_assert(ch->n_bytes_recved > 0); - tt_assert(ch->n_cells_recved > 0); + tt_u64_op(ch->n_bytes_recved, OP_GT, 0); + tt_u64_op(ch->n_cells_recved, OP_GT, 0); /* Test channel_dump_statistics */ ch->describe_transport = chan_test_describe_transport; ch->dumpstats = chan_test_dumpstats; - ch->is_canonical = chan_test_is_canonical; + test_chan_should_be_canonical = 1; + ch->is_canonical = test_chan_is_canonical; old_count = test_dumpstats_calls; channel_dump_statistics(ch, LOG_DEBUG); tt_int_op(test_dumpstats_calls, OP_EQ, old_count + 1); @@ -631,8 +514,8 @@ test_channel_dumpstats(void *arg) ch = NULL; done: - tor_free(cell); free_fake_channel(ch); + tor_free(cell); UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); @@ -640,215 +523,229 @@ test_channel_dumpstats(void *arg) return; } +/* Test outbound cell. The callstack is: + * channel_flush_some_cells() + * -> channel_flush_from_first_active_circuit() + * -> channel_write_packed_cell() + * -> write_packed_cell() + * -> chan->write_packed_cell() fct ptr. + * + * This test goes from a cell in a circuit up to the channel write handler + * that should put them on the connection outbuf. */ static void -test_channel_flush(void *arg) +test_channel_outbound_cell(void *arg) { - channel_t *ch = NULL; - cell_t *cell = NULL; - packed_cell_t *p_cell = NULL; - var_cell_t *v_cell = NULL; - int init_count; - - (void)arg; + int old_count; + channel_t *chan = NULL; + packed_cell_t *p_cell = NULL, *p_cell2 = NULL; + origin_circuit_t *circ = NULL; + cell_queue_t *queue; - ch = new_fake_channel(); - tt_assert(ch); + (void) arg; - /* Cache the original count */ - init_count = test_cells_written; + /* The channel will be freed so we need to hijack this so the scheduler + * doesn't get confused. */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); - /* Stop accepting so we can queue some */ - test_chan_accept_cells = 0; + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; - /* Queue a regular cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Queue a var cell */ - v_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(v_cell); - channel_write_var_cell(ch, v_cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Try a packed cell now */ + /* Setup a valid circuit to queue a cell. */ + circ = origin_circuit_new(); + tt_assert(circ); + /* Circuit needs an origin purpose to be considered origin. */ + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + TO_CIRCUIT(circ)->n_circ_id = 42; + /* This is the outbound test so use the next channel queue. */ + queue = &TO_CIRCUIT(circ)->n_chan_cells; + /* Setup packed cell to queue on the circuit. */ p_cell = packed_cell_new(); tt_assert(p_cell); - channel_write_packed_cell(ch, p_cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Now allow writes through again */ - test_chan_accept_cells = 1; - - /* ...and flush */ - channel_flush_cells(ch); - - /* All three should have gone through */ - tt_int_op(test_cells_written, OP_EQ, init_count + 3); - - done: - tor_free(ch); - - return; -} - -/** - * Channel flush tests that require cmux mocking - */ - -static void -test_channel_flushmux(void *arg) -{ - channel_t *ch = NULL; - int old_count, q_len_before, q_len_after; - ssize_t result; - - (void)arg; - - /* Install mocks we need for this test */ - MOCK(channel_flush_from_first_active_circuit, - chan_test_channel_flush_from_first_active_circuit_mock); - MOCK(circuitmux_num_cells, - chan_test_circuitmux_num_cells_mock); - - ch = new_fake_channel(); - tt_assert(ch); - ch->cmux = circuitmux_alloc(); - + p_cell2 = packed_cell_new(); + tt_assert(p_cell2); + /* Setup a channel to put the circuit on. */ + chan = new_fake_channel(); + tt_assert(chan); + chan->state = CHANNEL_STATE_OPENING; + channel_change_state_open(chan); + /* Outbound channel. */ + channel_mark_outgoing(chan); + /* Try to register it so we can clean it through the channel cleanup + * process. */ + channel_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + /* Set EWMA policy so we can pick it when flushing. */ + channel_set_cmux_policy_everywhere(&ewma_policy); + tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy); + + /* Register circuit to the channel circid map which will attach the circuit + * to the channel's cmux as well. */ + circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan); + tt_int_op(channel_num_circuits(chan), OP_EQ, 1); + tt_assert(!TO_CIRCUIT(circ)->next_active_on_n_chan); + tt_assert(!TO_CIRCUIT(circ)->prev_active_on_n_chan); + /* Test the cmux state. */ + tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux); + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + + /* Flush the channel without any cell on it. */ old_count = test_cells_written; - - test_target_cmux = ch->cmux; - test_cmux_cells = 1; - - /* Enable cell acceptance */ - test_chan_accept_cells = 1; - - result = channel_flush_some_cells(ch, 1); - - tt_int_op(result, OP_EQ, 1); + ssize_t flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 0); + tt_int_op(test_cells_written, OP_EQ, old_count); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); + tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 0); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0); + + /* Queue cell onto the next queue that is the outbound direction. Than + * update its cmux so the circuit can be picked when flushing cells. */ + cell_queue_append(queue, p_cell); + p_cell = NULL; + tt_int_op(queue->n, OP_EQ, 1); + cell_queue_append(queue, p_cell2); + p_cell2 = NULL; + tt_int_op(queue->n, OP_EQ, 2); + + update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT); + tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2); + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + + /* From this point on, we have a queued cell on an active circuit attached + * to the channel's cmux. */ + + /* Flush the first cell. This is going to go down the call stack. */ + old_count = test_cells_written; + flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - tt_int_op(test_cmux_cells, OP_EQ, 0); - - /* Now try it without accepting to force them into the queue */ - test_chan_accept_cells = 0; - test_cmux_cells = 1; - q_len_before = chan_cell_queue_len(&(ch->outgoing_queue)); - - result = channel_flush_some_cells(ch, 1); - - /* We should not have actually flushed any */ - tt_int_op(result, OP_EQ, 0); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 1); + /* Circuit should remain active because there is a second cell queued. */ + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + /* Should still be attached. */ + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0)); + + /* Flush second cell. This is going to go down the call stack. */ + old_count = test_cells_written; + flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - /* But we should have gotten to the fake cellgen loop */ - tt_int_op(test_cmux_cells, OP_EQ, 0); - /* ...and we should have a queued cell */ - q_len_after = chan_cell_queue_len(&(ch->outgoing_queue)); - tt_int_op(q_len_after, OP_EQ, q_len_before + 1); - - /* Now accept cells again and drain the queue */ - test_chan_accept_cells = 1; - channel_flush_cells(ch); - tt_int_op(test_cells_written, OP_EQ, old_count + 2); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - test_target_cmux = NULL; - test_cmux_cells = 0; + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); + /* No more cells should make the circuit inactive. */ + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 0); + /* Should still be attached. */ + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2); done: - if (ch) - circuitmux_free(ch->cmux); - tor_free(ch); - - UNMOCK(channel_flush_from_first_active_circuit); - UNMOCK(circuitmux_num_cells); - - test_chan_accept_cells = 0; - - return; + if (circ) { + circuit_free_(TO_CIRCUIT(circ)); + } + tor_free(p_cell); + channel_free_all(); + UNMOCK(scheduler_release_channel); } +/* Test inbound cell. The callstack is: + * channel_process_cell() + * -> chan->cell_handler() + * + * This test is about checking if we can process an inbound cell down to the + * channel handler. */ static void -test_channel_incoming(void *arg) +test_channel_inbound_cell(void *arg) { - channel_t *ch = NULL; + channel_t *chan = NULL; cell_t *cell = NULL; - var_cell_t *var_cell = NULL; int old_count; - (void)arg; + (void) arg; - /* Mock these for duration of the test */ - MOCK(scheduler_channel_doesnt_want_writes, - scheduler_channel_doesnt_want_writes_mock); - MOCK(scheduler_release_channel, - scheduler_release_channel_mock); + /* The channel will be freed so we need to hijack this so the scheduler + * doesn't get confused. */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - ch = new_fake_channel(); - tt_assert(ch); + chan = new_fake_channel(); + tt_assert(chan); /* Start it off in OPENING */ - ch->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch->cmux = circuitmux_alloc(); - - /* Install incoming cell handlers */ - channel_set_cell_handlers(ch, - chan_test_cell_handler, - chan_test_var_cell_handler); - /* Test cell handler getters */ - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, - chan_test_var_cell_handler); + chan->state = CHANNEL_STATE_OPENING; /* Try to register it */ - channel_register(ch); - tt_assert(ch->registered); + channel_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); /* Open it */ - channel_change_state_open(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); + channel_change_state_open(chan); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_OPEN); + tt_int_op(chan->has_been_open, OP_EQ, 1); - /* Receive a fixed cell */ - cell = tor_malloc_zero(sizeof(cell_t)); + /* Receive a cell now. */ + cell = tor_malloc_zero(sizeof(*cell)); make_fake_cell(cell); old_count = test_chan_fixed_cells_recved; - channel_queue_cell(ch, cell); - tor_free(cell); + channel_process_cell(chan, cell); + tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count); + tt_assert(monotime_coarse_is_zero(&chan->timestamp_xfer)); + tt_u64_op(chan->timestamp_active, OP_EQ, 0); + tt_u64_op(chan->timestamp_recv, OP_EQ, 0); + + /* Setup incoming cell handlers. We don't care about var cell, the channel + * layers is not handling those. */ + channel_set_cell_handlers(chan, chan_test_cell_handler, NULL); + tt_ptr_op(chan->cell_handler, OP_EQ, chan_test_cell_handler); + /* Now process the cell, we should see it. */ + old_count = test_chan_fixed_cells_recved; + channel_process_cell(chan, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); - - /* Receive a variable-size cell */ - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - old_count = test_chan_var_cells_recved; - channel_queue_var_cell(ch, var_cell); - tor_free(cell); - tt_int_op(test_chan_var_cells_recved, OP_EQ, old_count + 1); + /* We should have a series of timestamp set. */ + tt_assert(!monotime_coarse_is_zero(&chan->timestamp_xfer)); + tt_u64_op(chan->timestamp_active, OP_NE, 0); + tt_u64_op(chan->timestamp_recv, OP_NE, 0); + tt_assert(monotime_coarse_is_zero(&chan->next_padding_time)); + tt_u64_op(chan->n_cells_recved, OP_EQ, 1); + tt_u64_op(chan->n_bytes_recved, OP_EQ, get_cell_network_size(0)); /* Close it */ - channel_mark_for_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); - chan_test_finish_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); + old_count = test_close_called; + channel_mark_for_close(chan); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSING); + tt_int_op(chan->reason_for_closing, OP_EQ, CHANNEL_CLOSE_REQUESTED); + tt_int_op(test_close_called, OP_EQ, old_count + 1); + + /* This closes the channe so it calls in the scheduler, make sure of it. */ + old_count = test_releases_count; + chan_test_finish_close(chan); + tt_int_op(test_releases_count, OP_EQ, old_count + 1); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSED); + + /* The channel will be free, lets make sure it is not accessible. */ + uint64_t chan_id = chan->global_identifier; + tt_ptr_op(channel_find_by_global_id(chan_id), OP_EQ, chan); channel_run_cleanup(); - ch = NULL; + chan = channel_find_by_global_id(chan_id); + tt_assert(chan == NULL); done: - free_fake_channel(ch); tor_free(cell); - tor_free(var_cell); - - UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); - - return; } /** @@ -861,7 +758,7 @@ static void test_channel_lifecycle(void *arg) { channel_t *ch1 = NULL, *ch2 = NULL; - cell_t *cell = NULL; + packed_cell_t *p_cell = NULL; int old_count, init_doesnt_want_writes_count; int init_releases_count; @@ -879,38 +776,29 @@ test_channel_lifecycle(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; ch1 = new_fake_channel(); tt_assert(ch1); /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); /* Try to write a cell through (should queue) */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); + p_cell = packed_cell_new(); old_count = test_cells_written; - channel_write_cell(ch1, cell); + channel_write_packed_cell(ch1, p_cell); tt_int_op(old_count, OP_EQ, test_cells_written); /* Move it to OPEN and flush */ channel_change_state_open(ch1); - /* Queue should drain */ - tt_int_op(old_count + 1, OP_EQ, test_cells_written); - - /* Get another one */ +/* Get another one */ ch2 = new_fake_channel(); tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); /* Register */ channel_register(ch2); @@ -960,8 +848,6 @@ test_channel_lifecycle(void *arg) UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); - - return; } /** @@ -988,15 +874,11 @@ test_channel_lifecycle_2(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; ch = new_fake_channel(); tt_assert(ch); /* Start it off in OPENING */ ch->state = CHANNEL_STATE_OPENING; - /* The full lifecycle test needs a cmux */ - ch->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch); @@ -1016,7 +898,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1035,7 +916,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1064,7 +944,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1090,7 +969,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1125,655 +1003,6 @@ test_channel_lifecycle_2(void *arg) } static void -test_channel_multi(void *arg) -{ - channel_t *ch1 = NULL, *ch2 = NULL; - uint64_t global_queue_estimate; - cell_t *cell = NULL; - - (void)arg; - - /* Accept cells to lower layer */ - test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - - ch1 = new_fake_channel(); - tt_assert(ch1); - ch2 = new_fake_channel(); - tt_assert(ch2); - - /* Initial queue size update */ - channel_update_xmit_queue_size(ch1); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Queue some cells, check queue estimates */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Stop accepting cells at lower layer */ - test_chan_accept_cells = 0; - - /* Queue some cells and check queue estimates */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - channel_update_xmit_queue_size(ch1); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 1024); - - /* Allow cells through again */ - test_chan_accept_cells = 1; - - /* Flush chan 2 */ - channel_flush_cells(ch2); - - /* Update and check queue sizes */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* Flush chan 1 */ - channel_flush_cells(ch1); - - /* Update and check queue sizes */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Now block again */ - test_chan_accept_cells = 0; - - /* Queue some cells */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - cell = NULL; - - /* Check the estimates */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 1024); - - /* Now close channel 2; it should be subtracted from the global queue */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch2); - UNMOCK(scheduler_release_channel); - - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* - * Since the fake channels aren't registered, channel_free_all() can't - * see them properly. - */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch1); - UNMOCK(scheduler_release_channel); - - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Now free everything */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_free_all(); - UNMOCK(scheduler_release_channel); - - done: - free_fake_channel(ch1); - free_fake_channel(ch2); - - return; -} - -/** - * Check some hopefully-impossible edge cases in the channel queue we - * can only trigger by doing evil things to the queue directly. - */ - -static void -test_channel_queue_impossible(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - packed_cell_t *packed_cell = NULL; - var_cell_t *var_cell = NULL; - int old_count; - cell_queue_entry_t *q = NULL; - uint64_t global_queue_estimate; - uintptr_t cellintptr; - - /* Cache the global queue size (see below) */ - global_queue_estimate = channel_get_global_queue_estimate(); - - (void)arg; - - ch = new_fake_channel(); - tt_assert(ch); - - /* We test queueing here; tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Cache the cell written count */ - old_count = test_cells_written; - - /* Assert that the queue is initially empty */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Get a fresh cell and write it to the channel*/ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cellintptr = (uintptr_t)(void*)cell; - channel_write_cell(ch, cell); - - /* Now it should be queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED); - tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); - } - /* Do perverse things to it */ - tor_free(q->u.fixed.cell); - q->u.fixed.cell = NULL; - - /* - * Now change back to open with channel_change_state() and assert that it - * gets thrown away properly. - */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Same thing but for a var_cell */ - - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - cellintptr = (uintptr_t)(void*)var_cell; - channel_write_var_cell(ch, var_cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_VAR); - tt_assert((uintptr_t)q->u.var.var_cell == cellintptr); - } - - /* Remove the cell from the queue entry */ - tor_free(q->u.var.var_cell); - q->u.var.var_cell = NULL; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Same thing with a packed_cell */ - - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - packed_cell = packed_cell_new(); - tt_assert(packed_cell); - cellintptr = (uintptr_t)(void*)packed_cell; - channel_write_packed_cell(ch, packed_cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_PACKED); - tt_assert((uintptr_t)q->u.packed.packed_cell == cellintptr); - } - - /* Remove the cell from the queue entry */ - packed_cell_free(q->u.packed.packed_cell); - q->u.packed.packed_cell = NULL; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Unknown cell type case */ - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cellintptr = (uintptr_t)(void*)cell; - channel_write_cell(ch, cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED); - tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); - } - /* Clobber it, including the queue entry type */ - tor_free(q->u.fixed.cell); - q->u.fixed.cell = NULL; - q->type = CELL_QUEUE_PACKED + 1; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - tor_capture_bugs_(1); - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); - tor_end_capture_bugs_(); - - done: - free_fake_channel(ch); - - /* - * Doing that meant that we couldn't correctly adjust the queue size - * for the var cell, so manually reset the global queue size estimate - * so the next test doesn't break if we run with --no-fork. - */ - estimated_total_queue_size = global_queue_estimate; - - return; -} - -static void -test_channel_queue_incoming(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - var_cell_t *var_cell = NULL; - int old_fixed_count, old_var_count; - - (void)arg; - - /* Mock these for duration of the test */ - MOCK(scheduler_channel_doesnt_want_writes, - scheduler_channel_doesnt_want_writes_mock); - MOCK(scheduler_release_channel, - scheduler_release_channel_mock); - - /* Accept cells to lower layer */ - test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - - ch = new_fake_channel(); - tt_assert(ch); - /* Start it off in OPENING */ - ch->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch->cmux = circuitmux_alloc(); - - /* Test cell handler getters */ - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, NULL); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, NULL); - - /* Try to register it */ - channel_register(ch); - tt_assert(ch->registered); - - /* Open it */ - channel_change_state_open(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); - - /* Assert that the incoming queue is empty */ - tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue))); - - /* Queue an incoming fixed-length cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_queue_cell(ch, cell); - - /* Assert that the incoming queue has one entry */ - tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 1); - - /* Queue an incoming var cell */ - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - channel_queue_var_cell(ch, var_cell); - - /* Assert that the incoming queue has two entries */ - tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 2); - - /* - * Install cell handlers; this will drain the queue, so save the old - * cell counters first - */ - old_fixed_count = test_chan_fixed_cells_recved; - old_var_count = test_chan_var_cells_recved; - channel_set_cell_handlers(ch, - chan_test_cell_handler, - chan_test_var_cell_handler); - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, - chan_test_var_cell_handler); - - /* Assert cells were received */ - tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_fixed_count + 1); - tt_int_op(test_chan_var_cells_recved, OP_EQ, old_var_count + 1); - - /* - * Assert that the pointers are different from the cells we allocated; - * when queueing cells with no incoming cell handlers installed, the - * channel layer should copy them to a new buffer, and free them after - * delivery. These pointers will have already been freed by the time - * we get here, so don't dereference them. - */ - tt_ptr_op(test_chan_last_seen_fixed_cell_ptr, OP_NE, cell); - tt_ptr_op(test_chan_last_seen_var_cell_ptr, OP_NE, var_cell); - - /* Assert queue is now empty */ - tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue))); - - /* Close it; this contains an assertion that the incoming queue is empty */ - channel_mark_for_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); - chan_test_finish_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); - channel_run_cleanup(); - ch = NULL; - - done: - free_fake_channel(ch); - tor_free(cell); - tor_free(var_cell); - - UNMOCK(scheduler_channel_doesnt_want_writes); - UNMOCK(scheduler_release_channel); - - return; -} - -static void -test_channel_queue_size(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - int n, old_count; - uint64_t global_queue_estimate; - - (void)arg; - - ch = new_fake_channel(); - tt_assert(ch); - - /* Initial queue size update */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Test the call-through to our fake lower layer */ - n = channel_num_cells_writeable(ch); - /* chan_test_num_cells_writeable() always returns 32 */ - tt_int_op(n, OP_EQ, 32); - - /* - * Now we queue some cells and check that channel_num_cells_writeable() - * adjusts properly - */ - - /* tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Get a fresh cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - - old_count = test_cells_written; - channel_write_cell(ch, cell); - /* Assert that it got queued, not written through, correctly */ - tt_int_op(test_cells_written, OP_EQ, old_count); - - /* Now check chan_test_num_cells_writeable() again */ - n = channel_num_cells_writeable(ch); - /* Should return 0 since we're in CHANNEL_STATE_MAINT */ - tt_int_op(n, OP_EQ, 0); - - /* Update queue size estimates */ - channel_update_xmit_queue_size(ch); - /* One cell, times an overhead factor of 1.0 */ - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Try a different overhead factor */ - test_overhead_estimate = 0.5; - /* This one should be ignored since it's below 1.0 */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Now try a larger one */ - test_overhead_estimate = 2.0; - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 1024); - /* Go back to 1.0 */ - test_overhead_estimate = 1.0; - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Check the global estimate too */ - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* Go to open */ - old_count = test_cells_written; - channel_change_state_open(ch); - - /* - * It should try to write, but we aren't accepting cells right now, so - * it'll requeue - */ - tt_int_op(test_cells_written, OP_EQ, old_count); - - /* Check the queue size again */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* - * Now the cell is in the queue, and we're open, so we should get 31 - * writeable cells. - */ - n = channel_num_cells_writeable(ch); - tt_int_op(n, OP_EQ, 31); - - /* Accept cells again */ - test_chan_accept_cells = 1; - /* ...and re-process the queue */ - old_count = test_cells_written; - channel_flush_cells(ch); - tt_int_op(test_cells_written, OP_EQ, old_count + 1); - - /* Should have 32 writeable now */ - n = channel_num_cells_writeable(ch); - tt_int_op(n, OP_EQ, 32); - - /* Should have queue size estimate of zero */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Okay, now we're done with this one */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch); - UNMOCK(scheduler_release_channel); - - done: - free_fake_channel(ch); - - return; -} - -static void -test_channel_write(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = tor_malloc_zero(sizeof(cell_t)); - packed_cell_t *packed_cell = NULL; - var_cell_t *var_cell = - tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - int old_count; - - (void)arg; - - packed_cell = packed_cell_new(); - tt_assert(packed_cell); - - ch = new_fake_channel(); - tt_assert(ch); - make_fake_cell(cell); - make_fake_var_cell(var_cell); - - /* Tell it to accept cells */ - test_chan_accept_cells = 1; - - old_count = test_cells_written; - channel_write_cell(ch, cell); - cell = NULL; - tt_assert(test_cells_written == old_count + 1); - - channel_write_var_cell(ch, var_cell); - var_cell = NULL; - tt_assert(test_cells_written == old_count + 2); - - channel_write_packed_cell(ch, packed_cell); - packed_cell = NULL; - tt_assert(test_cells_written == old_count + 3); - - /* Now we test queueing; tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Get a fresh cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - - old_count = test_cells_written; - channel_write_cell(ch, cell); - tt_assert(test_cells_written == old_count); - - /* - * Now change back to open with channel_change_state() and assert that it - * gets drained from the queue. - */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count + 1); - - /* - * Check the note destroy case - */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cell->command = CELL_DESTROY; - - /* Set up the mock */ - MOCK(channel_note_destroy_not_pending, - channel_note_destroy_not_pending_mock); - - old_count = test_destroy_not_pending_calls; - channel_write_cell(ch, cell); - tt_assert(test_destroy_not_pending_calls == old_count + 1); - - /* Now send a non-destroy and check we don't call it */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - tt_assert(test_destroy_not_pending_calls == old_count + 1); - - UNMOCK(channel_note_destroy_not_pending); - - /* - * Now switch it to CLOSING so we can test the discard-cells case - * in the channel_write_*() functions. - */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch); - UNMOCK(scheduler_release_channel); - - /* Send cells that will drop in the closing state */ - old_count = test_cells_written; - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - cell = NULL; - tt_assert(test_cells_written == old_count); - - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - channel_write_var_cell(ch, var_cell); - var_cell = NULL; - tt_assert(test_cells_written == old_count); - - packed_cell = packed_cell_new(); - channel_write_packed_cell(ch, packed_cell); - packed_cell = NULL; - tt_assert(test_cells_written == old_count); - - done: - free_fake_channel(ch); - tor_free(var_cell); - tor_free(cell); - packed_cell_free(packed_cell); - return; -} - -static void test_channel_id_map(void *arg) { (void)arg; @@ -1879,19 +1108,449 @@ test_channel_id_map(void *arg) #undef N_CHAN } +static void +test_channel_state(void *arg) +{ + (void) arg; + + /* Test state validity. */ + tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSED), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPENING), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_MAINT), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_LAST), OP_EQ, 0); + tt_int_op(channel_state_is_valid(INT_MAX), OP_EQ, 0); + + /* Test listener state validity. */ + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSED), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LAST), + OP_EQ, 0); + tt_int_op(channel_listener_state_is_valid(INT_MAX), OP_EQ, 0); + + /* Test state transition. */ + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED, + CHANNEL_STATE_OPENING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED, + CHANNEL_STATE_ERROR), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_CLOSED), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_OPEN), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_OPENING), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_MAINT), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST, + CHANNEL_STATE_MAINT), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST, INT_MAX), + OP_EQ, 0); + + /* Test listener state transition. */ + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSED, + CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSED, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 0); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSING, + CHANNEL_LISTENER_STATE_CLOSED), + OP_EQ, 1); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSING, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_ERROR, + CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 0); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LISTENING, + CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LISTENING, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LAST, + INT_MAX), + OP_EQ, 0); + + /* Test state string. */ + tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSING), OP_EQ, + "closing"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_ERROR), OP_EQ, + "channel error"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSED), OP_EQ, + "closed"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_OPEN), OP_EQ, + "open"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_OPENING), OP_EQ, + "opening"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_MAINT), OP_EQ, + "temporarily suspended for maintenance"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_LAST), OP_EQ, + "unknown or invalid channel state"); + tt_str_op(channel_state_to_string(INT_MAX), OP_EQ, + "unknown or invalid channel state"); + + /* Test listener state string. */ + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, "closing"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, "channel listener error"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, "listening"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LAST), + OP_EQ, "unknown or invalid channel listener state"); + tt_str_op(channel_listener_state_to_string(INT_MAX), + OP_EQ, "unknown or invalid channel listener state"); + + done: + ; +} + +static networkstatus_t *mock_ns = NULL; + +static networkstatus_t * +mock_networkstatus_get_latest_consensus(void) +{ + return mock_ns; +} + +static void +test_channel_duplicates(void *arg) +{ + channel_t *chan = NULL; + routerstatus_t rs; + + (void) arg; + + setup_full_capture_of_logs(LOG_INFO); + /* Try a flat call with channel nor connections. */ + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + mock_ns = tor_malloc_zero(sizeof(*mock_ns)); + mock_ns->routerstatus_list = smartlist_new(); + MOCK(networkstatus_get_latest_consensus, + mock_networkstatus_get_latest_consensus); + + chan = new_fake_channel(); + tt_assert(chan); + chan->is_canonical = test_chan_is_canonical; + memset(chan->identity_digest, 'A', sizeof(chan->identity_digest)); + channel_add_to_digest_map(chan); + tt_ptr_op(channel_find_by_remote_identity(chan->identity_digest, NULL), + OP_EQ, chan); + + /* No relay has been associated with this channel. */ + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + /* Associate relay to this connection in the consensus. */ + memset(&rs, 0, sizeof(rs)); + memset(rs.identity_digest, 'A', sizeof(rs.identity_digest)); + smartlist_add(mock_ns->routerstatus_list, &rs); + + /* Non opened channel. */ + chan->state = CHANNEL_STATE_CLOSING; + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + chan->state = CHANNEL_STATE_OPEN; + + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 1 connections to 1 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + test_chan_should_be_canonical = 1; + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 1 connections to 1 relays. Found 1 current canonical " + "connections, in 1 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + teardown_capture_of_logs(); + + done: + free_fake_channel(chan); + smartlist_clear(mock_ns->routerstatus_list); + networkstatus_vote_free(mock_ns); + UNMOCK(networkstatus_get_latest_consensus); +} + +static void +test_channel_for_extend(void *arg) +{ + channel_t *chan1 = NULL, *chan2 = NULL; + channel_t *ret_chan = NULL; + char digest[DIGEST_LEN]; + ed25519_public_key_t ed_id; + tor_addr_t addr; + const char *msg; + int launch; + time_t now = time(NULL); + + (void) arg; + + memset(digest, 'A', sizeof(digest)); + memset(&ed_id, 'B', sizeof(ed_id)); + + chan1 = new_fake_channel(); + tt_assert(chan1); + /* Need to be registered to get added to the id map. */ + channel_register(chan1); + tt_int_op(chan1->registered, OP_EQ, 1); + /* We need those for the test. */ + chan1->is_canonical = test_chan_is_canonical; + chan1->matches_target = test_chan_matches_target; + chan1->timestamp_created = now - 9; + + chan2 = new_fake_channel(); + tt_assert(chan2); + /* Need to be registered to get added to the id map. */ + channel_register(chan2); + tt_int_op(chan2->registered, OP_EQ, 1); + /* We need those for the test. */ + chan2->is_canonical = test_chan_is_canonical; + chan2->matches_target = test_chan_matches_target; + /* Make it older than chan1. */ + chan2->timestamp_created = chan1->timestamp_created - 1; + + /* Set channel identities and add it to the channel map. The last one to be + * added is made the first one in the list so the lookup will always return + * that one first. */ + channel_set_identity_digest(chan2, digest, &ed_id); + channel_set_identity_digest(chan1, digest, &ed_id); + tt_ptr_op(channel_find_by_remote_identity(digest, NULL), OP_EQ, chan1); + tt_ptr_op(channel_find_by_remote_identity(digest, &ed_id), OP_EQ, chan1); + + /* The expected result is chan2 because it is older than chan1. */ + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* Switch that around from previous test. */ + chan2->timestamp_created = chan1->timestamp_created + 1; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan1); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* Same creation time, num circuits will be used and they both have 0 so the + * channel 2 should be picked due to how channel_is_better() work. */ + chan2->timestamp_created = chan1->timestamp_created; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan1); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* For the rest of the tests, we need channel 1 to be the older. */ + chan2->timestamp_created = chan1->timestamp_created + 1; + + /* Condemned the older channel. */ + chan1->state = CHANNEL_STATE_CLOSING; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + chan1->state = CHANNEL_STATE_OPEN; + + /* Make the older channel a client one. */ + channel_mark_client(chan1); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + channel_clear_client(chan1); + + /* Non matching ed identity with valid digest. */ + ed25519_public_key_t dumb_ed_id; + memset(&dumb_ed_id, 0, sizeof(dumb_ed_id)); + ret_chan = channel_get_for_extend(digest, &dumb_ed_id, &addr, &msg, + &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Not connected. Connecting."); + tt_int_op(launch, OP_EQ, 1); + + /* Opening channel, we'll check if the target address matches. */ + test_chan_should_match_target = 1; + chan1->state = CHANNEL_STATE_OPENING; + chan2->state = CHANNEL_STATE_OPENING; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connection in progress; waiting."); + tt_int_op(launch, OP_EQ, 0); + chan1->state = CHANNEL_STATE_OPEN; + chan2->state = CHANNEL_STATE_OPEN; + + /* Mark channel 1 as bad for circuits. */ + channel_mark_bad_for_new_circs(chan1); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + chan1->is_bad_for_new_circs = 0; + + /* Mark both channels as unusable. */ + channel_mark_bad_for_new_circs(chan1); + channel_mark_bad_for_new_circs(chan2); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " + " Launching a new one."); + tt_int_op(launch, OP_EQ, 1); + chan1->is_bad_for_new_circs = 0; + chan2->is_bad_for_new_circs = 0; + + /* Non canonical channels. */ + test_chan_should_match_target = 0; + test_chan_canonical_should_be_reliable = 1; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " + " Launching a new one."); + tt_int_op(launch, OP_EQ, 1); + + done: + free_fake_channel(chan1); + free_fake_channel(chan2); +} + +static void +test_channel_listener(void *arg) +{ + int old_count; + time_t now = time(NULL); + channel_listener_t *chan = NULL; + + (void) arg; + + chan = tor_malloc_zero(sizeof(*chan)); + tt_assert(chan); + channel_init_listener(chan); + tt_u64_op(chan->global_identifier, OP_EQ, 1); + tt_int_op(chan->timestamp_created, OP_GE, now); + chan->close = test_chan_listener_close; + + /* Register it. At this point, it is not open so it will be put in the + * finished list. */ + channel_listener_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + channel_listener_unregister(chan); + + /* Register it as listening now thus active. */ + chan->state = CHANNEL_LISTENER_STATE_LISTENING; + channel_listener_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + + /* Set the listener function. */ + channel_listener_set_listener_fn(chan, test_chan_listener_fn); + tt_ptr_op(chan->listener, OP_EQ, test_chan_listener_fn); + + /* Put a channel in the listener incoming list and queue it. + * function. By doing this, the listener() handler will be called. */ + channel_t *in_chan = new_fake_channel(); + old_count = test_chan_listener_fn_called; + channel_listener_queue_incoming(chan, in_chan); + free_fake_channel(in_chan); + tt_int_op(test_chan_listener_fn_called, OP_EQ, old_count + 1); + + /* Put listener channel in CLOSING state. */ + old_count = test_chan_listener_close_fn_called; + channel_listener_mark_for_close(chan); + tt_int_op(test_chan_listener_close_fn_called, OP_EQ, old_count + 1); + channel_listener_change_state(chan, CHANNEL_LISTENER_STATE_CLOSED); + + /* Dump stats so we at least hit the code path. */ + chan->describe_transport = test_chan_listener_describe_transport; + /* There is a check for "now > timestamp_created" when dumping the stats so + * make sure we go in. */ + chan->timestamp_created = now - 10; + channel_listener_dump_statistics(chan, LOG_INFO); + + done: + channel_free_all(); +} + struct testcase_t channel_tests[] = { - { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL }, - { "flush", test_channel_flush, TT_FORK, NULL, NULL }, - { "flushmux", test_channel_flushmux, TT_FORK, NULL, NULL }, - { "incoming", test_channel_incoming, TT_FORK, NULL, NULL }, - { "lifecycle", test_channel_lifecycle, TT_FORK, NULL, NULL }, - { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, NULL, NULL }, - { "multi", test_channel_multi, TT_FORK, NULL, NULL }, - { "queue_impossible", test_channel_queue_impossible, TT_FORK, NULL, NULL }, - { "queue_incoming", test_channel_queue_incoming, TT_FORK, NULL, NULL }, - { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL }, - { "write", test_channel_write, TT_FORK, NULL, NULL }, - { "id_map", test_channel_id_map, TT_FORK, NULL, NULL }, + { "inbound_cell", test_channel_inbound_cell, TT_FORK, + NULL, NULL }, + { "outbound_cell", test_channel_outbound_cell, TT_FORK, + NULL, NULL }, + { "id_map", test_channel_id_map, TT_FORK, + NULL, NULL }, + { "lifecycle", test_channel_lifecycle, TT_FORK, + NULL, NULL }, + { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, + NULL, NULL }, + { "dumpstats", test_channel_dumpstats, TT_FORK, + NULL, NULL }, + { "state", test_channel_state, TT_FORK, + NULL, NULL }, + { "duplicates", test_channel_duplicates, TT_FORK, + NULL, NULL }, + { "get_channel_for_extend", test_channel_for_extend, TT_FORK, + NULL, NULL }, + { "listener", test_channel_listener, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index d5713688a0..9e570b81a7 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define TOR_CHANNEL_INTERNAL_ #define MAIN_PRIVATE #define NETWORKSTATUS_PRIVATE @@ -276,7 +279,6 @@ test_channelpadding_timers(void *arg) { channelpadding_decision_t decision; channel_t *chans[CHANNELS_TO_TEST]; - int64_t new_time; (void)arg; tor_libevent_postfork(); @@ -286,8 +288,9 @@ test_channelpadding_timers(void *arg) monotime_init(); monotime_enable_test_mocking(); - monotime_set_mock_time_nsec(1); - monotime_coarse_set_mock_time_nsec(1); + uint64_t nsec_mock = 1; + monotime_set_mock_time_nsec(nsec_mock); + monotime_coarse_set_mock_time_nsec(nsec_mock); timers_initialize(); channelpadding_new_consensus_params(NULL); @@ -301,11 +304,14 @@ test_channelpadding_timers(void *arg) tried_to_write_cell = 0; int i = 0; + monotime_coarse_t now; + monotime_coarse_get(&now); + /* This loop fills our timerslot array with timers of increasing time * until they fire */ for (; i < CHANNELPADDING_MAX_TIMERS; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() - + 10 + i*4; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 10 + i*4); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -315,7 +321,8 @@ test_channelpadding_timers(void *arg) /* This loop should add timers to the first position in the timerslot * array, since its timeout is before all other timers. */ for (; i < CHANNELS_TO_TEST/3; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 1; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 1); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -326,8 +333,8 @@ test_channelpadding_timers(void *arg) * pseudorandom pattern. It ensures that the lists can grow with multiple * timers in them. */ for (; i < CHANNELS_TO_TEST/2; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 10 + - i*3 % CHANNELPADDING_MAX_TIMERS; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 10 + i*3 % CHANNELPADDING_MAX_TIMERS); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -337,8 +344,8 @@ test_channelpadding_timers(void *arg) /* This loop should add timers to the last position in the timerslot * array, since its timeout is after all other timers. */ for (; i < CHANNELS_TO_TEST; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 500 + - i % CHANNELPADDING_MAX_TIMERS; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 500 + i % CHANNELPADDING_MAX_TIMERS); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -346,9 +353,9 @@ test_channelpadding_timers(void *arg) } // Wait for the timers and then kill the event loop. - new_time = (monotime_coarse_absolute_msec()+1001)*NSEC_PER_MSEC; - monotime_coarse_set_mock_time_nsec(new_time); - monotime_set_mock_time_nsec(new_time); + nsec_mock += 1001 * NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(nsec_mock); + monotime_set_mock_time_nsec(nsec_mock); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, CHANNELS_TO_TEST); @@ -385,6 +392,7 @@ test_channelpadding_killonehop(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; timers_initialize(); setup_mock_consensus(); @@ -398,9 +406,12 @@ test_channelpadding_killonehop(void *arg) smartlist_clear(current_md_consensus->net_params); channelpadding_new_consensus_params(current_md_consensus); + monotime_coarse_t now; + monotime_coarse_get(&now); + tried_to_write_cell = 0; get_options_mutable()->Tor2webMode = 1; - client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(client_relay3->pending_padding_callback); @@ -410,9 +421,10 @@ test_channelpadding_killonehop(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -424,7 +436,7 @@ test_channelpadding_killonehop(void *arg) // Before the client tries to pad, the relay will still pad: tried_to_write_cell = 0; - relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100); get_options_mutable()->ORPort_set = 1; get_options_mutable()->Tor2webMode = 0; decision = channelpadding_decide_to_pad_channel(relay3_client); @@ -432,9 +444,10 @@ test_channelpadding_killonehop(void *arg) tt_assert(relay3_client->pending_padding_callback); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -444,7 +457,7 @@ test_channelpadding_killonehop(void *arg) tt_assert(relay3_client->padding_enabled); tt_assert(client_relay3->padding_enabled); get_options_mutable()->Tor2webMode = 1; - /* For the relay to recieve the negotiate: */ + /* For the relay to receive the negotiate: */ get_options_mutable()->ORPort_set = 1; decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); @@ -471,7 +484,8 @@ test_channelpadding_killonehop(void *arg) get_options_mutable()->ORPort_set = 0; get_options_mutable()->HiddenServiceSingleHopMode = 1; get_options_mutable()->HiddenServiceNonAnonymousMode = 1; - client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + + monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(client_relay3->pending_padding_callback); @@ -481,9 +495,10 @@ test_channelpadding_killonehop(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101 * NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -495,7 +510,7 @@ test_channelpadding_killonehop(void *arg) // Before the client tries to pad, the relay will still pad: tried_to_write_cell = 0; - relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100); get_options_mutable()->ORPort_set = 1; get_options_mutable()->HiddenServiceSingleHopMode = 0; get_options_mutable()->HiddenServiceNonAnonymousMode = 0; @@ -504,9 +519,10 @@ test_channelpadding_killonehop(void *arg) tt_assert(relay3_client->pending_padding_callback); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101 * NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -514,7 +530,7 @@ test_channelpadding_killonehop(void *arg) // Test client side (it should stop immediately) get_options_mutable()->HiddenServiceSingleHopMode = 1; get_options_mutable()->HiddenServiceNonAnonymousMode = 1; - /* For the relay to recieve the negotiate: */ + /* For the relay to receive the negotiate: */ get_options_mutable()->ORPort_set = 1; decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD); @@ -570,6 +586,9 @@ test_channelpadding_consensus(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; + monotime_coarse_t now; + monotime_coarse_get(&now); timers_initialize(); if (!connection_array) @@ -583,7 +602,7 @@ test_channelpadding_consensus(void *arg) /* Test 1: Padding can be completely disabled via consensus */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -593,9 +612,10 @@ test_channelpadding_consensus(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); @@ -625,7 +645,7 @@ test_channelpadding_consensus(void *arg) tt_i64_op(val, OP_EQ, 0); val = channelpadding_compute_time_until_pad_for_netflow(chan); tt_i64_op(val, OP_EQ, -2); - tt_assert(!chan->next_padding_time_ms); + tt_assert(monotime_coarse_is_zero(&chan->next_padding_time)); smartlist_clear(current_md_consensus->net_params); @@ -638,7 +658,7 @@ test_channelpadding_consensus(void *arg) channelpadding_new_consensus_params(current_md_consensus); tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -650,9 +670,10 @@ test_channelpadding_consensus(void *arg) tt_i64_op(val, OP_LE, 200); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+201)*NSEC_PER_MSEC; + new_time += 201*NSEC_PER_MSEC; monotime_set_mock_time_nsec(new_time); monotime_coarse_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); @@ -814,7 +835,7 @@ test_channelpadding_negotiation(void *arg) get_options_mutable()->ORPort_set = 0; /* Test case #2: Torrc options */ - /* ConnectionPadding auto; Relay doesn't suport us */ + /* ConnectionPadding auto; Relay doesn't support us */ ((channel_tls_t*)relay3_client)->conn->link_proto = 4; relay3_client->padding_enabled = 0; tried_to_write_cell = 0; @@ -825,7 +846,7 @@ test_channelpadding_negotiation(void *arg) ((channel_tls_t*)relay3_client)->conn->link_proto = 5; relay3_client->padding_enabled = 1; - /* ConnectionPadding 1; Relay doesn't suport us */ + /* ConnectionPadding 1; Relay doesn't support us */ get_options_mutable()->ConnectionPadding = 1; tried_to_write_cell = 0; decision = channelpadding_decide_to_pad_channel(client_relay3); @@ -941,6 +962,9 @@ test_channelpadding_decide_to_pad_channel(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; + monotime_coarse_t now; + monotime_coarse_get(&now); timers_initialize(); setup_full_capture_of_logs(LOG_WARN); channelpadding_new_consensus_params(NULL); @@ -957,7 +981,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2a: > 1.1s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1200; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 1200); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); tt_assert(!chan->pending_padding_callback); @@ -965,23 +989,27 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2b: >= 1.0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1000; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 1000); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); tt_int_op(tried_to_write_cell, OP_EQ, 0); + // Set up a timer for the <0 case below. + monotime_coarse_t now_minus_100s; + monotime_coarse_add_msec(&now_minus_100s, &now, 900); // Wait for the timer from case #2b - new_time = (monotime_coarse_absolute_msec() + 1000)*NSEC_PER_MSEC; + new_time += 1000*NSEC_PER_MSEC; monotime_set_mock_time_nsec(new_time); monotime_coarse_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); /* Test case #2c: > 0.1s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -992,16 +1020,17 @@ test_channelpadding_decide_to_pad_channel(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); /* Test case #2e: 0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec(); + monotime_coarse_add_msec(&chan->next_padding_time, &now, 0); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1009,7 +1038,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2f: <0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() - 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now_minus_100s, 0); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1017,7 +1046,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #3: Channel that sends a packet while timeout is scheduled */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1028,9 +1057,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) // We don't expect any timer callbacks here. Make a dummy one to be sure. // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1038,7 +1068,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #4: Channel that closes while a timeout is scheduled */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1048,9 +1078,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) chan->state = CHANNEL_STATE_MAINT; // We don't expect any timer callbacks here. Make a dummy one to be sure. - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1059,16 +1090,17 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #5: Make sure previous test case didn't break everything */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); tt_int_op(tried_to_write_cell, OP_EQ, 0); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1087,7 +1119,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) * It must be last. */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1097,9 +1129,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) free_fake_channeltls((channel_tls_t*)chan); // We don't expect any timer callbacks here. Make a dummy one to be sure. - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time = 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index f622704ec5..d170009a9c 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -141,7 +141,7 @@ test_clist_maps(void *arg) /* Okay, now free ch2 and make sure that the circuit ID is STILL not * usable, because we haven't declared the destroy to be nonpending */ tt_int_op(cdm.ncalls, OP_EQ, 0); - circuit_free(TO_CIRCUIT(or_c2)); + circuit_free_(TO_CIRCUIT(or_c2)); or_c2 = NULL; /* prevent free */ tt_int_op(cdm.ncalls, OP_EQ, 2); memset(&cdm, 0, sizeof(cdm)); @@ -160,9 +160,9 @@ test_clist_maps(void *arg) done: if (or_c1) - circuit_free(TO_CIRCUIT(or_c1)); + circuit_free_(TO_CIRCUIT(or_c1)); if (or_c2) - circuit_free(TO_CIRCUIT(or_c2)); + circuit_free_(TO_CIRCUIT(or_c2)); if (ch1) tor_free(ch1->cmux); if (ch2) @@ -234,11 +234,11 @@ test_rend_token_maps(void *arg) /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); - circuit_free(TO_CIRCUIT(c1)); + circuit_free_(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ - circuit_free(TO_CIRCUIT(c2)); + circuit_free_(TO_CIRCUIT(c2)); c2 = NULL; tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); @@ -275,15 +275,15 @@ test_rend_token_maps(void *arg) done: if (c1) - circuit_free(TO_CIRCUIT(c1)); + circuit_free_(TO_CIRCUIT(c1)); if (c2) - circuit_free(TO_CIRCUIT(c2)); + circuit_free_(TO_CIRCUIT(c2)); if (c3) - circuit_free(TO_CIRCUIT(c3)); + circuit_free_(TO_CIRCUIT(c3)); if (c4) - circuit_free(TO_CIRCUIT(c4)); + circuit_free_(TO_CIRCUIT(c4)); if (c5) - circuit_free(TO_CIRCUIT(c5)); + circuit_free_(TO_CIRCUIT(c5)); } static void @@ -452,10 +452,10 @@ test_hs_circuitmap_isolation(void *arg) } done: - circuit_free(TO_CIRCUIT(circ1)); - circuit_free(TO_CIRCUIT(circ2)); - circuit_free(TO_CIRCUIT(circ3)); - circuit_free(TO_CIRCUIT(circ4)); + circuit_free_(TO_CIRCUIT(circ1)); + circuit_free_(TO_CIRCUIT(circ2)); + circuit_free_(TO_CIRCUIT(circ3)); + circuit_free_(TO_CIRCUIT(circ4)); } struct testcase_t circuitlist_tests[] = { diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c new file mode 100644 index 0000000000..8ebef659ca --- /dev/null +++ b/src/test/test_circuitstats.c @@ -0,0 +1,201 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITBUILD_PRIVATE +#define CIRCUITSTATS_PRIVATE +#define CIRCUITLIST_PRIVATE +#define CHANNEL_PRIVATE_ + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuitbuild.h" +#include "circuitstats.h" +#include "circuituse.h" +#include "channel.h" + +void test_circuitstats_timeout(void *arg); +void test_circuitstats_hoplen(void *arg); +origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); +origin_circuit_t *add_opened_threehop(void); +origin_circuit_t *build_unopened_fourhop(struct timeval); + +int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); + +static int marked_for_close; +/* Mock function because we are not trying to test the close circuit that does + * an awful lot of checks on the circuit object. */ +static void +mock_circuit_mark_for_close(circuit_t *circ, int reason, int line, + const char *file) +{ + (void) circ; + (void) reason; + (void) line; + (void) file; + marked_for_close = 1; + return; +} + +origin_circuit_t * +add_opened_threehop(void) +{ + origin_circuit_t *or_circ = origin_circuit_new(); + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN; + + onion_append_hop(&or_circ->cpath, &fakehop); + onion_append_hop(&or_circ->cpath, &fakehop); + onion_append_hop(&or_circ->cpath, &fakehop); + + or_circ->has_opened = 1; + TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + return or_circ; +} + +origin_circuit_t * +build_unopened_fourhop(struct timeval circ_start_time) +{ + origin_circuit_t *or_circ = origin_circuit_new(); + extend_info_t *fakehop = tor_malloc_zero(sizeof(extend_info_t)); + memset(fakehop, 0, sizeof(extend_info_t)); + + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + TO_CIRCUIT(or_circ)->timestamp_began = circ_start_time; + TO_CIRCUIT(or_circ)->timestamp_created = circ_start_time; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->desired_path_len = 4; + + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + + tor_free(fakehop); + + return or_circ; +} + +origin_circuit_t * +subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout) +{ + origin_circuit_t *or_circ = build_unopened_fourhop(circ_start_time); + + // Now make them open one at a time and call + // circuit_build_times_handle_completed_hop(); + or_circ->cpath->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); + + or_circ->cpath->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); + + // Third hop: We should count it now. + or_circ->cpath->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, + !should_timeout); // 1 if counted, 0 otherwise + + // Fourth hop: Don't double count + or_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, + !should_timeout); + + done: + return or_circ; +} + +void +test_circuitstats_hoplen(void *arg) +{ + /* Plan: + * 0. Test no other opened circs (relaxed timeout) + * 1. Check >3 hop circ building w/o timeout + * 2. Check >3 hop circs w/ timeouts.. + */ + struct timeval circ_start_time; + origin_circuit_t *threehop = NULL; + origin_circuit_t *fourhop = NULL; + (void)arg; + MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close); + + circuit_build_times_init(get_circuit_build_times_mutable()); + + // Let's set a close_ms to 2X the initial timeout, so we can + // test relaxed functionality (which uses the close_ms timeout) + get_circuit_build_times_mutable()->close_ms *= 2; + + tor_gettimeofday(&circ_start_time); + circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff + + // Test 1: Build a fourhop circuit that should get marked + // as relaxed and eventually counted by circuit_expire_building + // (but not before) + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0); + tt_int_op(marked_for_close, OP_EQ, 0); + circuit_expire_building(); + tt_int_op(marked_for_close, OP_EQ, 0); + tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1); + TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119; + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + tt_int_op(marked_for_close, OP_EQ, 1); + + circuit_free_(TO_CIRCUIT(fourhop)); + circuit_build_times_reset(get_circuit_build_times_mutable()); + + // Test 2: Add a threehop circuit for non-relaxed timeouts + threehop = add_opened_threehop(); + + /* This circuit should not timeout */ + tor_gettimeofday(&circ_start_time); + circ_start_time.tv_sec -= 59; + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE, + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + + circuit_free_((circuit_t *)fourhop); + circuit_build_times_reset(get_circuit_build_times_mutable()); + + /* Test 3: This circuit should now time out and get marked as a + * measurement circuit, but still get counted (and counted only once) + */ + circ_start_time.tv_sec -= 2; + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ, + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + + done: + UNMOCK(circuit_mark_for_close_); + circuit_free_(TO_CIRCUIT(threehop)); + circuit_free_(TO_CIRCUIT(fourhop)); + circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); +} + +#define TEST_CIRCUITSTATS(name, flags) \ + { #name, test_##name, (flags), NULL, NULL } + +struct testcase_t circuitstats_tests[] = { + TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK), + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index e7380c1d14..d42335de38 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -323,7 +323,7 @@ test_config_write_to_data_subdir(void *arg) tt_int_op(mkdir(options->DataDirectory, 0700), OP_EQ, 0); #endif - // Write attempt shoudl fail, if subdirectory doesn't exist. + // Write attempt should fail, if subdirectory doesn't exist. tt_assert(write_to_data_subdir(subdir, fname, str, NULL)); tt_assert(! check_or_create_data_subdir(subdir)); @@ -1391,7 +1391,7 @@ test_config_resolve_my_address(void *arg) * if running on. * 3. Hostname from previous step cannot be converted to * address by using tor_inet_aton() function. - * 4. However, tor_lookup_hostname() succeds in resolving the + * 4. However, tor_lookup_hostname() succeeds in resolving the * hostname from step 2. * 5. Unfortunately, tor_addr_is_internal() deems this address * to be internal. @@ -4833,7 +4833,7 @@ test_config_include_limit(void *data) torrc_path); tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4863,7 +4863,7 @@ test_config_include_does_not_exist(void *data) tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", missing_path); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4895,7 +4895,7 @@ test_config_include_error_in_included_file(void *data) tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", invalid_path); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4937,8 +4937,8 @@ test_config_include_empty_file_folder(void *data) folder_path, file_path); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_EQ, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -4975,7 +4975,8 @@ test_config_include_no_permission(void *data) folder_path); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, + &include_used, NULL), OP_EQ, -1); tt_ptr_op(result, OP_EQ, NULL); @@ -5031,8 +5032,8 @@ test_config_include_recursion_before_after(void *data) } int include_used; - tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5096,8 +5097,8 @@ test_config_include_recursion_after_only(void *data) } int include_used; - tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5185,8 +5186,8 @@ test_config_include_folder_order(void *data) torrcd); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5239,8 +5240,8 @@ test_config_include_path_syntax(void *data) esc_dir_with_pathsep); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_EQ, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5294,14 +5295,14 @@ test_config_include_has_include(void *data) char torrc_contents[1000] = "Test 1\n"; int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_int_op(include_used, OP_EQ, 0); config_free_lines(result); tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_int_op(include_used, OP_EQ, 1); done: @@ -5516,6 +5517,85 @@ test_config_check_bridge_distribution_setting_unrecognised(void *arg) return; } +static void +test_config_include_opened_file_list(void *data) +{ + (void)data; + + config_line_t *result = NULL; + smartlist_t *opened_files = smartlist_new(); + char *dir = tor_strdup(get_fname("test_include_opened_file_list")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrcd[PATH_MAX+1]; + tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d"); + +#ifdef _WIN32 + tt_int_op(mkdir(torrcd), OP_EQ, 0); +#else + tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0); +#endif + + char subfolder[PATH_MAX+1]; + tor_snprintf(subfolder, sizeof(subfolder), "%s"PATH_SEPARATOR"%s", torrcd, + "subfolder"); + +#ifdef _WIN32 + tt_int_op(mkdir(subfolder), OP_EQ, 0); +#else + tt_int_op(mkdir(subfolder, 0700), OP_EQ, 0); +#endif + + char path[PATH_MAX+1]; + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", subfolder, + "01_file_in_subfolder"); + tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0); + + char empty[PATH_MAX+1]; + tor_snprintf(empty, sizeof(empty), "%s"PATH_SEPARATOR"%s", torrcd, "empty"); + tt_int_op(write_str_to_file(empty, "", 0), OP_EQ, 0); + + char file[PATH_MAX+1]; + tor_snprintf(file, sizeof(file), "%s"PATH_SEPARATOR"%s", torrcd, "file"); + tt_int_op(write_str_to_file(file, "Test 2\n", 0), OP_EQ, 0); + + char dot[PATH_MAX+1]; + tor_snprintf(dot, sizeof(dot), "%s"PATH_SEPARATOR"%s", torrcd, ".dot"); + tt_int_op(write_str_to_file(dot, "Test 3\n", 0), OP_EQ, 0); + + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), + "%%include %s\n", + torrcd); + + int include_used; + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used, + opened_files), OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + tt_int_op(include_used, OP_EQ, 1); + + tt_int_op(smartlist_len(opened_files), OP_EQ, 4); + tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1); + tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1); + // files inside subfolders are not opended, only the subfolder is opened + tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1); + tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1); + // dot files are not opened as we ignore them when we get their name from + // their parent folder + + done: + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + config_free_lines(result); + tor_free(dir); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -5563,6 +5643,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(check_bridge_distribution_setting_valid, 0), CONFIG_TEST(check_bridge_distribution_setting_invalid, 0), CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0), + CONFIG_TEST(include_opened_file_list, 0), END_OF_TESTCASES }; diff --git a/src/test/test_connection.c b/src/test/test_connection.c index 314b90cfda..33f453b8b2 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -779,7 +779,7 @@ test_conn_download_status(void *arg) #define CONNECTION_TESTCASE(name, fork, setup) \ { #name, test_conn_##name, fork, &setup, NULL } -/* where arg is an expression (constant, varaible, compound expression) */ +/* where arg is an expression (constant, variable, compound expression) */ #define CONNECTION_TESTCASE_ARG(name, fork, setup, arg) \ { #name "_" #arg, test_conn_##name, fork, &setup, (void *)arg } diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c index ddb1bc53c1..ffec3149b0 100644 --- a/src/test/test_conscache.c +++ b/src/test/test_conscache.c @@ -31,8 +31,8 @@ test_conscache_simple_usage(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); @@ -124,8 +124,8 @@ test_conscache_cleanup(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); @@ -267,8 +267,8 @@ test_conscache_filter(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c index 80d3f943ab..a9a4b6a98e 100644 --- a/src/test/test_consdiffmgr.c +++ b/src/test/test_consdiffmgr.c @@ -24,8 +24,8 @@ consdiffmgr_test_setup(const struct testcase_t *arg) { (void)arg; char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer. check_private_dir(ddir_fname, CPD_CREATE, NULL); consdiff_cfg_t consdiff_cfg = { 300 }; @@ -215,8 +215,8 @@ test_consdiffmgr_init_failure(void *arg) /* As in ...test_setup, but do not create the datadir. The missing directory * will cause a failure. */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer. consdiff_cfg_t consdiff_cfg = { 7200, 300 }; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 472fcb8c53..af19f63f6c 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -6,6 +6,7 @@ #include "bridges.h" #include "control.h" #include "entrynodes.h" +#include "hs_common.h" #include "networkstatus.h" #include "rendservice.h" #include "routerlist.h" @@ -13,10 +14,87 @@ #include "test_helpers.h" static void -test_add_onion_helper_keyarg(void *arg) +test_add_onion_helper_keyarg_v3(void *arg) { - crypto_pk_t *pk = NULL; - crypto_pk_t *pk2 = NULL; + int ret, hs_version; + add_onion_secret_key_t pk; + char *key_new_blob = NULL; + char *err_msg = NULL; + const char *key_new_alg = NULL; + + (void) arg; + + memset(&pk, 0, sizeof(pk)); + + /* Test explicit ED25519-V3 key generation. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); + tt_assert(key_new_blob); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + + /* Test discarding the private key. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + + /* Test passing a key blob. */ + { + /* The base64 key and hex key are the same. Hex key is 64 bytes long. The + * sk has been generated randomly using python3. */ + const char *base64_sk = + "a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW" + "6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw=="; + const char *hex_sk = + "\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc" + "\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44" + "\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82" + "\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b"; + char *key_blob = NULL; + + tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); + tt_assert(key_blob); + ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tor_free(key_blob); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_mem_op(pk.v3, OP_EQ, hex_sk, 64); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + } + + done: + tor_free(pk.v3); + tor_free(key_new_blob); + tor_free(err_msg); +} + +static void +test_add_onion_helper_keyarg_v2(void *arg) +{ + int ret, hs_version; + add_onion_secret_key_t pk; + crypto_pk_t *pk1 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; @@ -25,83 +103,100 @@ test_add_onion_helper_keyarg(void *arg) (void) arg; + memset(&pk, 0, sizeof(pk)); + /* Test explicit RSA1024 key generation. */ - pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test "BEST" key generation (Assumes BEST = RSA1024). */ - crypto_pk_free(pk); + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test discarding the private key. */ - crypto_pk_free(pk); + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test generating a invalid key type. */ - crypto_pk_free(pk); - pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + crypto_pk_free(pk.v2); pk.v2 = NULL; + ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); /* Test loading a RSA1024 key. */ tor_free(err_msg); - pk = pk_generate(0); - tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded)); + pk1 = pk_generate(0); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk2); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); - tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0); + tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0); /* Test loading a invalid key type. */ tor_free(arg_str); - crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk1); pk1 = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); /* Test loading a invalid key. */ tor_free(arg_str); - crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(err_msg); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); done: - crypto_pk_free(pk); - crypto_pk_free(pk2); + crypto_pk_free(pk1); + crypto_pk_free(pk.v2); tor_free(key_new_blob); tor_free(err_msg); tor_free(encoded); @@ -1370,7 +1465,10 @@ test_download_status_bridge(void *arg) } struct testcase_t controller_tests[] = { - { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0, + NULL, NULL }, + { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0, + NULL, NULL }, { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL }, { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, NULL, NULL }, diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 997b110d62..0c7c571b2f 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -567,7 +567,7 @@ test_dir_routerinfo_parsing(void *arg) static void routerinfo_free_wrapper_(void *arg) { - routerinfo_free(arg); + routerinfo_free_(arg); } static void @@ -664,7 +664,7 @@ test_dir_extrainfo_parsing(void *arg) escaped(NULL); extrainfo_free(ei); routerinfo_free(ri); - digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); + digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_); } static void @@ -760,7 +760,7 @@ test_dir_parse_router_list(void *arg) smartlist_free(chunks); routerinfo_free(ri); if (map) { - digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); + digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_); router_get_routerlist()->identity_map = (struct digest_ri_map_t*)digestmap_new(); } @@ -1270,7 +1270,7 @@ test_dir_versions(void *arg) tt_int_op(0,OP_EQ, tor_version_as_new_as( "Tor 0.2.9.9 (git-00)", "Tor 0.2.9.9 (git-01)")); - /* In #21278, we comapre without integer overflows. + /* In #21278, we compare without integer overflows. * But since #21450 limits version components to [0, INT32_MAX], it is no * longer possible to cause an integer overflow in tor_version_compare() */ tt_int_op(0,OP_EQ, tor_version_as_new_as( @@ -4874,9 +4874,11 @@ mock_check_private_dir(const char *dirname, cpd_check_t check, static char * mock_get_datadir_fname(const or_options_t *options, + directory_root_t roottype, const char *sub1, const char *sub2, const char *suffix) { + (void) roottype; char *rv = NULL; /* @@ -5033,7 +5035,7 @@ test_dir_dump_unparseable_descriptors(void *data) mock_options->MaxUnparseableDescSizeToLog = 1536; MOCK(get_options, mock_get_options); MOCK(check_private_dir, mock_check_private_dir); - MOCK(options_get_datadir_fname2_suffix, + MOCK(options_get_dir_fname2_suffix, mock_get_datadir_fname); /* @@ -5551,7 +5553,7 @@ test_dir_dump_unparseable_descriptors(void *data) mock_unlink_reset(); UNMOCK(write_str_to_file); mock_write_str_to_file_reset(); - UNMOCK(options_get_datadir_fname2_suffix); + UNMOCK(options_get_dir_fname2_suffix); UNMOCK(check_private_dir); UNMOCK(get_options); tor_free(mock_options); @@ -6174,6 +6176,106 @@ test_dir_platform_str(void *arg) ; } +static networkstatus_t *mock_networkstatus; + +static networkstatus_t * +mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) +{ + (void)f; + return mock_networkstatus; +} + +static void +test_dir_networkstatus_consensus_has_ipv6(void *arg) +{ + (void)arg; + + int has_ipv6 = 0; + + /* Init options and networkstatus */ + or_options_t our_options; + mock_options = &our_options; + reset_options(mock_options, &mock_get_options_calls); + MOCK(get_options, mock_get_options); + + networkstatus_t our_networkstatus; + mock_networkstatus = &our_networkstatus; + memset(mock_networkstatus, 0, sizeof(*mock_networkstatus)); + MOCK(networkstatus_get_latest_consensus_by_flavor, + mock_networkstatus_get_latest_consensus_by_flavor); + + /* A live consensus */ + mock_networkstatus->valid_after = time(NULL) - 3600; + mock_networkstatus->valid_until = time(NULL) + 3600; + + /* Test the bounds for A lines in the NS consensus */ + mock_options->UseMicrodescriptors = 0; + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 20; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES - 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* Test the bounds for A lines in the microdesc consensus */ + mock_options->UseMicrodescriptors = 1; + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 20; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS - 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* Test the edge cases */ + mock_options->UseMicrodescriptors = 1; + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + + /* Reasonably live */ + mock_networkstatus->valid_until = approx_time() - 60; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + /* Not reasonably live */ + mock_networkstatus->valid_after = approx_time() - 24*60*60 - 3600; + mock_networkstatus->valid_until = approx_time() - 24*60*60 - 60; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* NULL consensus */ + mock_networkstatus = NULL; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + done: + UNMOCK(get_options); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -6241,6 +6343,7 @@ struct testcase_t dir_tests[] = { DIR(assumed_flags, 0), DIR(networkstatus_compute_bw_weights_v10, 0), DIR(platform_str, 0), + DIR(networkstatus_consensus_has_ipv6, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index fe26657ad8..ca64dce5fe 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -88,7 +88,7 @@ test_dir_handle_get_bad_request(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -117,7 +117,7 @@ test_dir_handle_get_v1_command_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -164,7 +164,7 @@ test_dir_handle_get_v1_command(void *data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_dirportfrontpage); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); } @@ -190,7 +190,7 @@ test_dir_handle_get_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -225,7 +225,7 @@ test_dir_handle_get_robots_txt(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); } @@ -254,7 +254,7 @@ test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -282,7 +282,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id( done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -315,7 +315,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -344,7 +344,7 @@ test_dir_handle_get_rendezvous2_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); rend_cache_free_all(); } @@ -424,7 +424,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data) UNMOCK(connection_write_to_buf_impl_); NS_UNMOCK(router_get_my_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); rend_encoded_v2_service_descriptor_free(desc_holder); @@ -457,7 +457,7 @@ test_dir_handle_get_micro_d_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -469,6 +469,7 @@ init_mock_options(void) memset(mock_options, 0, sizeof(or_options_t)); mock_options->TestingTorNetwork = 1; mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp")); + mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory); check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL); } @@ -541,7 +542,7 @@ test_dir_handle_get_micro_d(void *data) UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); smartlist_free(list); @@ -595,7 +596,7 @@ test_dir_handle_get_micro_d_server_busy(void *data) UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); smartlist_free(list); microdesc_free_all(); @@ -632,7 +633,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -672,7 +673,7 @@ test_dir_handle_get_networkstatus_bridges(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -709,7 +710,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -737,7 +738,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -798,7 +799,7 @@ test_dir_handle_get_server_descriptors_all(void* data) done: NS_UNMOCK(router_get_my_routerinfo); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -902,7 +903,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo->cache_info.signed_descriptor_body); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -974,7 +975,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo->cache_info.signed_descriptor_body); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -1038,7 +1039,7 @@ test_dir_handle_get_server_descriptors_d(void* data) done: UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -1094,7 +1095,7 @@ test_dir_handle_get_server_descriptors_busy(void* data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); crypto_pk_free(identity_pkey); @@ -1125,7 +1126,7 @@ test_dir_handle_get_server_keys_bad_req(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1151,7 +1152,7 @@ test_dir_handle_get_server_keys_all_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1210,7 +1211,7 @@ test_dir_handle_get_server_keys_all(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -1240,7 +1241,7 @@ test_dir_handle_get_server_keys_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1288,7 +1289,7 @@ test_dir_handle_get_server_keys_authority(void* data) done: UNMOCK(get_my_v3_authority_cert); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; @@ -1316,7 +1317,7 @@ test_dir_handle_get_server_keys_fp_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1370,7 +1371,7 @@ test_dir_handle_get_server_keys_fp(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); clear_dir_servers(); @@ -1399,7 +1400,7 @@ test_dir_handle_get_server_keys_sk_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1444,7 +1445,7 @@ test_dir_handle_get_server_keys_sk(void* data) done: UNMOCK(get_my_v3_authority_cert); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); authority_cert_free(mock_cert); mock_cert = NULL; tor_free(header); tor_free(body); @@ -1472,7 +1473,7 @@ test_dir_handle_get_server_keys_fpsk_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1529,7 +1530,7 @@ test_dir_handle_get_server_keys_fpsk(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -1583,7 +1584,7 @@ test_dir_handle_get_server_keys_busy(void* data) done: UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); or_options_free(mock_options); mock_options = NULL; @@ -1649,7 +1650,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d) UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(stats); smartlist_free(mock_ns_val->voters); @@ -1690,7 +1691,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(stats); or_options_free(mock_options); mock_options = NULL; @@ -1764,7 +1765,7 @@ test_dir_handle_get_status_vote_current_consensus_too_old(void *data) UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(mock_ns_val); or_options_free(mock_options); mock_options = NULL; @@ -1825,7 +1826,7 @@ status_vote_current_consensus_ns_test(char **header, char **body, done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -1948,7 +1949,7 @@ test_dir_handle_get_status_vote_current_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1971,7 +1972,7 @@ status_vote_current_d_test(char **header, char **body, size_t *body_l) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -1991,7 +1992,7 @@ status_vote_next_d_test(char **header, char **body, size_t *body_l) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -2116,7 +2117,7 @@ test_dir_handle_get_status_vote_next_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2135,7 +2136,7 @@ status_vote_next_consensus_test(char **header, char **body, size_t *body_used) body, body_used, 18, 0); done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -2175,7 +2176,7 @@ test_dir_handle_get_status_vote_current_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2199,7 +2200,7 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2281,7 +2282,7 @@ status_vote_next_consensus_signatures_test(char **header, char **body, body, body_used, 22, 0); done: - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); UNMOCK(connection_write_to_buf_impl_); } @@ -2429,7 +2430,7 @@ test_dir_handle_get_status_vote_next_authority(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_my_v3_authority_cert); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; @@ -2511,7 +2512,7 @@ test_dir_handle_get_status_vote_current_authority(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_my_v3_authority_cert); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; diff --git a/src/test/test_dns.c b/src/test/test_dns.c index 19dcb02931..1fee01d2c0 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "or.h" #include "test.h" @@ -121,7 +124,7 @@ static int n_connection_free = 0; static connection_t *last_freed_conn = NULL; static void -NS(connection_free)(connection_t *conn) +NS(connection_free_)(connection_t *conn) { n_connection_free++; @@ -264,7 +267,7 @@ NS(test_main)(void *arg) */ NS_MOCK(dns_cancel_pending_resolve); - NS_MOCK(connection_free); + NS_MOCK(connection_free_); exitconn->on_circuit = &(on_circuit->base_); exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE; @@ -291,7 +294,7 @@ NS(test_main)(void *arg) NS_UNMOCK(send_resolved_cell); NS_UNMOCK(send_resolved_hostname_cell); NS_UNMOCK(dns_cancel_pending_resolve); - NS_UNMOCK(connection_free); + NS_UNMOCK(connection_free_); tor_free(on_circuit); tor_free(exitconn); tor_free(nextconn); diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index c29b1a7126..9d8a072c77 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -34,7 +34,7 @@ entryconn_rewrite_teardown(const struct testcase_t *tc, void *arg) (void)tc; entry_connection_t *ec = arg; if (ec) - connection_free_(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec)); addressmap_free_all(); return 1; } @@ -156,8 +156,8 @@ test_entryconn_rewrite_automap_ipv4(void *arg) ec->socks_request->address); done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } /* Automap on resolve, connect to automapped address, resolve again and get @@ -230,9 +230,9 @@ test_entryconn_rewrite_automap_ipv6(void *arg) ec->socks_request->address); done: - connection_free_(ENTRY_TO_CONN(ec)); - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } #if 0 @@ -283,7 +283,7 @@ test_entryconn_rewrite_automap_reverse(void *arg) tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } #endif /* 0 */ @@ -333,7 +333,7 @@ test_entryconn_rewrite_cached_dns_ipv4(void *arg) tt_str_op(ec2->socks_request->address, OP_EQ, "240.240.241.241"); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite because of cached DNS entry. */ @@ -385,8 +385,8 @@ test_entryconn_rewrite_cached_dns_ipv6(void *arg) tt_str_op(ec2->socks_request->address, OP_EQ, "[::f00f]"); done: - connection_free_(ENTRY_TO_CONN(ec)); - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Fail to connect to unmapped address in virtual range. */ @@ -426,7 +426,7 @@ test_entryconn_rewrite_unmapped_virtual(void *arg) tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite because of mapaddress option */ @@ -507,7 +507,7 @@ test_entryconn_rewrite_automap_exit(void *arg) tt_int_op(rr.end_reason, OP_EQ, END_STREAM_REASON_TORPROTOCOL); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite into .exit because of mapaddress */ @@ -618,9 +618,9 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg) */ done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); - connection_free_(ENTRY_TO_CONN(ec4)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec4)); } static void @@ -678,8 +678,8 @@ test_entryconn_rewrite_mapaddress_automap_onion_common(entry_connection_t *ec, "abcdefghijklmnop.onion")); done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } /* This time is the same, but we start with a mapping from a non-onion diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 80ebebe3f8..92a860360d 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -2024,7 +2024,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg) tt_mem_op(g->identity, OP_NE, g_prev->identity, DIGEST_LEN); tt_int_op(g->is_primary, OP_EQ, 1); tt_i64_op(g->last_tried_to_connect, OP_EQ, start+60); - tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmd now. + tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmed now. /* Call this one up; watch it get confirmed. */ update_approx_time(start+90); @@ -2372,8 +2372,8 @@ upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr) // circuit_guard_state_free(data->guard2_state); // held in circ2 guard_selection_free(data->gs); smartlist_free(data->all_origin_circuits); - circuit_free(TO_CIRCUIT(data->circ1)); - circuit_free(TO_CIRCUIT(data->circ2)); + circuit_free_(TO_CIRCUIT(data->circ1)); + circuit_free_(TO_CIRCUIT(data->circ2)); tor_free(data); return big_fake_network_cleanup(testcase, NULL); } diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index e18deb2700..cadef257f1 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -58,11 +58,11 @@ test_ext_or_id_map(void *arg) done: if (c1) - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); if (c2) - connection_free_(TO_CONN(c2)); + connection_free_minimal(TO_CONN(c2)); if (c3) - connection_free_(TO_CONN(c3)); + connection_free_minimal(TO_CONN(c3)); tor_free(idp); tor_free(idp2); connection_or_clear_ext_or_id_map(); @@ -145,7 +145,7 @@ test_ext_or_write_command(void *arg) done: if (c1) - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); tor_free(cp); tor_free(buf); UNMOCK(connection_write_to_buf_impl_); @@ -591,7 +591,7 @@ test_ext_or_handshake(void *arg) UNMOCK(connection_write_to_buf_impl_); UNMOCK(crypto_rand); if (conn) - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); #undef CONTAINS #undef WRITE } diff --git a/src/test/test_handles.c b/src/test/test_handles.c index 7ddee6e376..eb1e1f1bbe 100644 --- a/src/test/test_handles.c +++ b/src/test/test_handles.c @@ -13,6 +13,8 @@ typedef struct demo_t { } demo_t; HANDLE_DECL(demo, demo_t, static) +#define demo_handle_free(h) \ + FREE_AND_NULL(demo_handle_t, demo_handle_free_, (h)) HANDLE_IMPL(demo, demo_t, static) static demo_t * diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 7737499f50..9189bb65be 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -258,8 +258,9 @@ test_hs_desc_event(void *arg) sizeof(desc_id_base32)); /* test request event */ - control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID, - STR_DESC_ID_BASE32); + control_event_hs_descriptor_requested(rend_query.onion_address, + rend_query.auth_type, HSDIR_EXIST_ID, + STR_DESC_ID_BASE32, NULL); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; tt_assert(received_msg); @@ -268,8 +269,8 @@ test_hs_desc_event(void *arg) /* test received event */ rend_query.auth_type = REND_BASIC_AUTH; - control_event_hs_descriptor_received(rend_query.onion_address, - &rend_query.base_, HSDIR_EXIST_ID); + control_event_hsv2_descriptor_received(rend_query.onion_address, + &rend_query.base_, HSDIR_EXIST_ID); expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; tt_assert(received_msg); @@ -278,7 +279,7 @@ test_hs_desc_event(void *arg) /* test failed event */ rend_query.auth_type = REND_STEALTH_AUTH; - control_event_hs_descriptor_failed(&rend_query.base_, + control_event_hsv2_descriptor_failed(&rend_query.base_, HSDIR_NONE_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ @@ -289,7 +290,7 @@ test_hs_desc_event(void *arg) /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query.base_, + control_event_hsv2_descriptor_failed(&rend_query.base_, HSDIR_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ @@ -301,7 +302,7 @@ test_hs_desc_event(void *arg) /* test no HSDir fingerprint type */ rend_query.auth_type = REND_NO_AUTH; - control_event_hs_descriptor_failed(&rend_query.base_, NULL, + control_event_hsv2_descriptor_failed(&rend_query.base_, NULL, "QUERY_NO_HSDIR"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \ "UNKNOWN REASON=QUERY_NO_HSDIR\r\n"; @@ -1009,7 +1010,7 @@ test_prune_services_on_reload(void *arg) set_rend_service_list(old); set_rend_rend_service_staging_list(new); rend_service_prune_list_impl_(); - /* Check if they've all been transfered. */ + /* Check if they've all been transferred. */ tt_int_op(smartlist_len(old), OP_EQ, 0); tt_int_op(smartlist_len(new), OP_EQ, 2); } diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index 91b13be862..458ce1a92e 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -259,7 +259,7 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) done: tor_free(hsdir_query_str); if (conn) - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); return received_desc; } diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 750920fac0..7ee7210bc9 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -227,10 +227,10 @@ test_e2e_rend_circuit_setup_legacy(void *arg) tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); done: - connection_free_(conn); + connection_free_minimal(conn); if (or_circ) tor_free(TO_CIRCUIT(or_circ)->n_chan); - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /* Test: Ensure that setting up v3 rendezvous circuits works correctly. */ @@ -297,10 +297,10 @@ test_e2e_rend_circuit_setup(void *arg) tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); done: - connection_free_(conn); + connection_free_minimal(conn); if (or_circ) tor_free(TO_CIRCUIT(or_circ)->n_chan); - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /** Test client logic for picking intro points from a descriptor. Also test how @@ -560,7 +560,7 @@ test_descriptor_fetch(void *arg) smartlist_add(get_connection_array(), TO_CONN(dir_conn)); ret = hs_client_refetch_hsdesc(&service_pk); smartlist_remove(get_connection_array(), TO_CONN(dir_conn)); - connection_free_(TO_CONN(dir_conn)); + connection_free_minimal(TO_CONN(dir_conn)); tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING); } @@ -579,7 +579,7 @@ test_descriptor_fetch(void *arg) tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED); done: - connection_free_(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec)); UNMOCK(networkstatus_get_live_consensus); UNMOCK(router_have_minimum_dir_info); hs_free_all(); diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 21daa58abd..8c273c9639 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -289,7 +289,7 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns, memcpy(rs->identity_digest, identity, DIGEST_LEN); rs->is_hs_dir = is_hsdir; - rs->supports_v3_hsdir = 1; + rs->pv.supports_v3_hsdir = 1; strlcpy(rs->nickname, nickname, sizeof(rs->nickname)); tor_addr_parse(&ipv4_addr, "1.2.3.4"); ri->addr = tor_addr_to_ipv4h(&ipv4_addr); diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c new file mode 100644 index 0000000000..207a55de6d --- /dev/null +++ b/src/test/test_hs_control.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_control.c + * \brief Unit tests for hidden service control port event and command. + **/ + +#define CONTROL_PRIVATE +#define CIRCUITBUILD_PRIVATE +#define RENDCOMMON_PRIVATE +#define RENDSERVICE_PRIVATE +#define HS_SERVICE_PRIVATE + +#include "or.h" +#include "test.h" +#include "control.h" +#include "config.h" +#include "hs_common.h" +#include "hs_control.h" +#include "nodelist.h" +//#include "rendcommon.h" +//#include "rendservice.h" +//#include "routerset.h" +//#include "circuitbuild.h" +#include "test_helpers.h" + +/* mock ID digest and longname for node that's in nodelist */ +#define HSDIR_EXIST_ID \ + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \ + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" +#define STR_HSDIR_EXIST_LONGNAME \ + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir" +#define STR_HSDIR_NONE_EXIST_LONGNAME \ + "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + +/* Helper global variable for hidden service descriptor event test. + * It's used as a pointer to dynamically created message buffer in + * send_control_event_string_replacement function, which mocks + * send_control_event_string function. + * + * Always free it after use! */ +static char *received_msg = NULL; + +/** Mock function for send_control_event_string + */ +static void +queue_control_event_string_replacement(uint16_t event, char *msg) +{ + (void) event; + tor_free(received_msg); + received_msg = msg; +} + +/** Mock function for node_describe_longname_by_id, it returns either + * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME + */ +static const char * +node_describe_longname_by_id_replacement(const char *id_digest) +{ + if (!strcmp(id_digest, HSDIR_EXIST_ID)) { + return STR_HSDIR_EXIST_LONGNAME; + } else { + return STR_HSDIR_NONE_EXIST_LONGNAME; + } +} + +/* HSDir fetch index is a series of 'D' */ +#define HSDIR_INDEX_FETCH_HEX \ + "4343434343434343434343434343434343434343434343434343434343434343" +#define HSDIR_INDEX_STORE_HEX \ + "4444444444444444444444444444444444444444444444444444444444444444" + +static const node_t * +mock_node_get_by_id(const char *digest) +{ + static node_t node; + memcpy(node.identity, digest, DIGEST_LEN); + node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t)); + memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN); + memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN); + return &node; +} + +static void +test_hs_desc_event(void *arg) +{ + int ret; + char *expected_msg = NULL; + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + ed25519_keypair_t identity_kp; + ed25519_public_key_t blinded_pk; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + routerstatus_t hsdir_rs; + hs_ident_dir_conn_t ident; + + (void) arg; + MOCK(queue_control_event_string, + queue_control_event_string_replacement); + MOCK(node_describe_longname_by_id, + node_describe_longname_by_id_replacement); + MOCK(node_get_by_id, mock_node_get_by_id); + + /* Setup what we need for this test. */ + ed25519_keypair_generate(&identity_kp, 0); + hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address); + ret = hs_address_is_valid(onion_address); + tt_int_op(ret, OP_EQ, 1); + memset(&blinded_pk, 'B', sizeof(blinded_pk)); + memset(&hsdir_rs, 0, sizeof(hsdir_rs)); + memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN); + ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk); + tt_int_op(ret, OP_EQ, 0); + memcpy(&ident.identity_pk, &identity_kp.pubkey, + sizeof(ed25519_public_key_t)); + memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk)); + + /* HS_DESC REQUESTED ... */ + hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk, + &hsdir_rs); + tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX=" + HSDIR_INDEX_FETCH_HEX "\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC CREATED... */ + hs_control_desc_event_created(onion_address, &blinded_pk); + tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN " + "UNKNOWN %s\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC UPLOAD... */ + uint8_t hsdir_index_store[DIGEST256_LEN]; + memset(hsdir_index_store, 'D', sizeof(hsdir_index_store)); + hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID, + &blinded_pk, hsdir_index_store); + tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN " + STR_HSDIR_EXIST_LONGNAME " %s " + "HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC FAILED... */ + hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC"); + tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s " + "REASON=BAD_DESC\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC RECEIVED... */ + hs_control_desc_event_received(&ident, HSDIR_EXIST_ID); + tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC UPLOADED... */ + hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID); + tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN " + STR_HSDIR_EXIST_LONGNAME "\r\n", + onion_address); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + done: + UNMOCK(queue_control_event_string); + UNMOCK(node_describe_longname_by_id); + UNMOCK(node_get_by_id); + tor_free(received_msg); + tor_free(expected_msg); +} + +struct testcase_t hs_control_tests[] = { + { "hs_desc_event", test_hs_desc_event, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 9ec183db06..8e9d461c40 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -413,7 +413,7 @@ test_decode_invalid_intro_point(void *arg) (void) arg; - /* Seperate pieces of a valid encoded introduction point. */ + /* Separate pieces of a valid encoded introduction point. */ const char *intro_point = "introduction-point AQIUMDI5OUYyNjhGQ0E5RDU1Q0QxNTc="; const char *auth_key = diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 0cae2de7e1..55dfafbeac 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -139,7 +139,7 @@ helper_create_introduce1_cell(void) memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len); } - /* Set the cell extentions to none. */ + /* Set the cell extensions to none. */ { trn_cell_extension_t *ext = trn_cell_extension_new(); trn_cell_extension_set_num(ext, 0); @@ -194,7 +194,7 @@ test_establish_intro_wrong_purpose(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Prepare a circuit for accepting an ESTABLISH_INTRO cell */ @@ -228,7 +228,7 @@ test_establish_intro_wrong_keytype(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */ @@ -263,7 +263,7 @@ test_establish_intro_wrong_keytype2(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ @@ -333,7 +333,7 @@ test_establish_intro_wrong_mac(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong auth key length. Should @@ -378,7 +378,7 @@ test_establish_intro_wrong_auth_key_len(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should @@ -423,7 +423,7 @@ test_establish_intro_wrong_sig_len(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should @@ -460,7 +460,7 @@ test_establish_intro_wrong_sig(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to @@ -552,7 +552,7 @@ test_circuitmap_free_all(void) ; } -/** Successfuly register a v2 intro point and a v3 intro point. Ensure that HS +/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS * circuitmap is maintained properly. */ static void test_intro_point_registration(void *arg) @@ -629,8 +629,8 @@ test_intro_point_registration(void *arg) done: crypto_pk_free(legacy_auth_key); - circuit_free(TO_CIRCUIT(intro_circ)); - circuit_free(TO_CIRCUIT(legacy_intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(legacy_intro_circ)); trn_cell_establish_intro_free(establish_intro_cell); test_circuitmap_free_all(); @@ -650,7 +650,7 @@ test_introduce1_suitable_circuit(void *arg) circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 1); } @@ -659,7 +659,7 @@ test_introduce1_suitable_circuit(void *arg) circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -670,7 +670,7 @@ test_introduce1_suitable_circuit(void *arg) /* Bogus pointer, the check is against NULL on n_chan. */ circ->base_.n_chan = (channel_t *) circ; ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -681,7 +681,7 @@ test_introduce1_suitable_circuit(void *arg) circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); circ->already_received_introduce1 = 1; ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -725,6 +725,7 @@ test_introduce1_validation(void *arg) /* Create our decoy cell that we'll modify as we go to test the validation * function of that parsed cell. */ cell = helper_create_introduce1_cell(); + tt_assert(cell); /* It should NOT be a legacy cell which will trigger a BUG(). */ memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id)); @@ -800,7 +801,7 @@ test_received_introduce1_handling(void *arg) circ = helper_create_intro_circuit(); ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); tt_int_op(ret, OP_EQ, -1); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); } /* We have a unit test only for the suitability of a circuit to receive an @@ -813,7 +814,7 @@ test_received_introduce1_handling(void *arg) memset(test, 0, sizeof(test)); ret = handle_introduce1(circ, test, sizeof(test)); tor_free(circ->p_chan); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, -1); } @@ -838,8 +839,8 @@ test_received_introduce1_handling(void *arg) memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); ret = hs_intro_received_introduce1(circ, request, request_len); - circuit_free(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } @@ -867,8 +868,8 @@ test_received_introduce1_handling(void *arg) memcpy(token, legacy_key_id, sizeof(token)); hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); ret = hs_intro_received_introduce1(circ, request, request_len); - circuit_free(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 8407eccfa8..7fade6379d 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -20,6 +20,7 @@ #define STATEFILE_PRIVATE #define TOR_CHANNEL_INTERNAL_ #define HS_CLIENT_PRIVATE +#define ROUTERPARSE_PRIVATE #include "test.h" #include "test_helpers.h" @@ -37,6 +38,7 @@ #include "networkstatus.h" #include "nodelist.h" #include "relay.h" +#include "routerparse.h" #include "hs_common.h" #include "hs_config.h" @@ -182,7 +184,7 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); done: - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /* Helper: Return a newly allocated and initialized origin circuit with @@ -194,7 +196,7 @@ helper_create_origin_circuit(int purpose, int flags) origin_circuit_t *circ = NULL; circ = origin_circuit_init(purpose, flags); - tt_assert(circ); + tor_assert(circ); circ->cpath = tor_malloc_zero(sizeof(crypt_path_t)); circ->cpath->magic = CRYPT_PATH_MAGIC; circ->cpath->state = CPATH_STATE_OPEN; @@ -206,7 +208,6 @@ helper_create_origin_circuit(int purpose, int flags) /* Create a default HS identifier. */ circ->hs_ident = tor_malloc_zero(sizeof(hs_ident_circuit_t)); - done: return circ; } @@ -219,7 +220,7 @@ helper_create_service(void) { /* Set a service for this circuit. */ hs_service_t *service = hs_service_new(get_options()); - tt_assert(service); + tor_assert(service); service->config.version = HS_VERSION_THREE; ed25519_secret_key_generate(&service->keys.identity_sk, 0); ed25519_public_key_generate(&service->keys.identity_pk, @@ -241,7 +242,7 @@ helper_create_service_ip(void) { hs_desc_link_specifier_t *ls; hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0); - tt_assert(ip); + tor_assert(ip); /* Add a first unused link specifier. */ ls = tor_malloc_zero(sizeof(*ls)); ls->type = LS_IPV4; @@ -252,7 +253,6 @@ helper_create_service_ip(void) memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id)); smartlist_add(ip->base.link_specifiers, ls); - done: return ip; } @@ -655,13 +655,13 @@ test_intro_circuit_opened(void *arg) teardown_capture_of_logs(); done: - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); UNMOCK(relay_send_command_from_edge_); } -/** Test the operations we do on a circuit after we learn that we successfuly +/** Test the operations we do on a circuit after we learn that we successfully * established an intro point on it */ static void test_intro_established(void *arg) @@ -730,7 +730,7 @@ test_intro_established(void *arg) done: if (circ) - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -772,7 +772,7 @@ test_rdv_circuit_opened(void *arg) tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); done: - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); UNMOCK(relay_send_command_from_edge_); @@ -825,7 +825,7 @@ test_closing_intro_circs(void *arg) /* Now pretend we are freeing this intro circuit. We want to see that our * destructor is not gonna kill our intro point structure since that's the * job of the cleanup routine. */ - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); intro_circ = NULL; entry = service_intro_point_find(service, &ip->auth_key_kp.pubkey); tt_assert(entry); @@ -857,7 +857,7 @@ test_closing_intro_circs(void *arg) done: if (intro_circ) { - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Frees the service object. */ hs_free_all(); @@ -938,7 +938,7 @@ test_introduce2(void *arg) or_state_free(dummy_state); dummy_state = NULL; if (circ) - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -1022,7 +1022,7 @@ test_service_event(void *arg) done: hs_circuitmap_remove_circuit(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -1212,6 +1212,7 @@ test_build_update_descriptors(void *arg) /* Ugly yes but we never free the "ri" object so this just makes things * easier. */ ri.protocol_list = (char *) "HSDir=1-2 LinkAuth=3"; + summarize_protover_flags(&ri.pv, ri.protocol_list, NULL); ret = curve25519_secret_key_generate(&curve25519_secret_key, 0); tt_int_op(ret, OP_EQ, 0); ri.onion_curve25519_pkey = @@ -1576,8 +1577,8 @@ test_rendezvous1_parsing(void *arg) * would need an extra circuit and some more stuff but it's doable. */ done: - circuit_free(TO_CIRCUIT(service_circ)); - circuit_free(TO_CIRCUIT(client_circ)); + circuit_free_(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(client_circ)); hs_service_free(service); hs_free_all(); UNMOCK(relay_send_command_from_edge_); diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 87012cd283..b3d4d8e39a 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -40,6 +40,12 @@ fi CASE8=$dflt CASE9=$dflt CASE10=$dflt + CASE11A=$dflt + CASE11B=$dflt + CASE11C=$dflt + CASE11D=$dflt + CASE11E=$dflt + CASE11F=$dflt if [ $# -ge 1 ]; then eval "CASE${1}"=1 @@ -363,6 +369,109 @@ echo "==== Case 10 ok" fi +# Case 11a: -passphrase-fd without --keygen + +if [ "$CASE11A" = 1 ]; then + +ME="${DATA_DIR}/case11a" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true + +grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11A ok" + +fi + +# Case 11b: --no-passphrase without --keygen + +if [ "$CASE11B" = 1 ]; then + +ME="${DATA_DIR}/case11b" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true + +grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11B ok" + +fi + +# Case 11c: --newpass without --keygen + +if [ "$CASE11C" = 1 ]; then + +ME="${DATA_DIR}/case11C" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true + +grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11C ok" + +fi + +######## --master-key does not work yet, but this will test the error case +######## when it does. +# +# Case 11d: --master-key without --keygen +# +if [ "$CASE11D" = 1 ]; then +# +# ME="${DATA_DIR}/case11d" +# +# mkdir -p "${ME}/keys" +# +# ${TOR} --DataDirectory "${ME}" --master-key "${ME}/foobar" > "${ME}/stdout" && die "Successfully started with master-key but no keygen?" || true +# +# cat "${ME}/stdout" +# +# grep "master-key without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + + echo "==== Case 11D skipped" + +fi + + +# Case 11E: Silly passphrase-fd + +if [ "$CASE11E" = 1 ]; then + +ME="${DATA_DIR}/case11E" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true + +grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11E ok" + +fi + + +# Case 11F: --no-passphrase with --passphrase-fd + +if [ "$CASE11F" = 1 ]; then + +ME="${DATA_DIR}/case11F" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true + +grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11F ok" + +fi + # Check cert-only. diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 422d419078..6840072d76 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -302,8 +302,8 @@ test_link_handshake_certs_ok(void *arg) mock_own_cert = mock_peer_cert = NULL; memset(c1->identity_digest, 0, sizeof(c1->identity_digest)); memset(c2->identity_digest, 0, sizeof(c2->identity_digest)); - connection_free_(TO_CONN(c1)); - connection_free_(TO_CONN(c2)); + connection_free_minimal(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c2)); tor_free(cell1); tor_free(cell2); certs_cell_free(cc1); @@ -343,7 +343,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) tor_free(d->cell); certs_cell_free(d->ccell); connection_or_clear_identity(d->c); - connection_free_(TO_CONN(d->c)); + connection_free_minimal(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); crypto_pk_free(d->key1); @@ -930,7 +930,7 @@ test_link_handshake_send_authchallenge(void *arg) done: UNMOCK(connection_or_write_var_cell_to_buf); - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); tor_free(cell1); tor_free(cell2); crypto_pk_free(rsa0); @@ -955,7 +955,7 @@ recv_authchallenge_cleanup(const struct testcase_t *test, void *obj) if (d) { tor_free(d->cell); - connection_free_(TO_CONN(d->c)); + connection_free_minimal(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); tor_free(d); @@ -1158,8 +1158,8 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) tor_free(d->cell); connection_or_clear_identity(d->c1); connection_or_clear_identity(d->c2); - connection_free_(TO_CONN(d->c1)); - connection_free_(TO_CONN(d->c2)); + connection_free_minimal(TO_CONN(d->c1)); + connection_free_minimal(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); tor_free(d->chan2); crypto_pk_free(d->key1); diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 4f0ecd778b..59b28f7580 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -69,12 +69,12 @@ test_md_cache(void *data) time3 = time(NULL) - 15*24*60*60; /* Possibly, turn this into a test setup/cleanup pair */ - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test")); #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif tt_assert(!strcmpstart(test_md3_noannotation, "onion-key")); @@ -152,7 +152,7 @@ test_md_cache(void *data) strlen(test_md3_noannotation)); tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new", - options->DataDirectory); + options->CacheDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_assert(s); tt_mem_op(md1->body, OP_EQ, s + md1->off, md1->bodylen); @@ -180,7 +180,7 @@ test_md_cache(void *data) /* read the cache. */ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", - options->DataDirectory); + options->CacheDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_mem_op(md1->body, OP_EQ, s + md1->off, strlen(test_md1)); tt_mem_op(md2->body, OP_EQ, s + md2->off, strlen(test_md2)); @@ -234,7 +234,7 @@ test_md_cache(void *data) done: if (options) - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); microdesc_free_all(); smartlist_free(added); @@ -266,17 +266,17 @@ test_md_cache_broken(void *data) options = get_options_mutable(); tt_assert(options); - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test2")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test2")); #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", - options->DataDirectory); + options->CacheDirectory); write_str_to_file(fn, truncated_md, 1); @@ -285,7 +285,7 @@ test_md_cache_broken(void *data) done: if (options) - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); tor_free(fn); microdesc_free_all(); } @@ -754,8 +754,8 @@ test_md_reject_cache(void *arg) or_options_t *options = get_options_mutable(); char buf[DIGEST256_LEN]; - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test_rej")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test_rej")); mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t)); mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t)); mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); @@ -765,9 +765,9 @@ test_md_reject_cache(void *arg) mock_ns_val->flavor = FLAV_MICRODESC; #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif MOCK(router_get_mutable_consensus_status_by_descriptor_digest, @@ -802,7 +802,7 @@ test_md_reject_cache(void *arg) done: UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest); - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); microdesc_free_all(); smartlist_free(added); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); diff --git a/src/test/test_oom.c b/src/test/test_oom.c index cf28690a28..c172fe60c7 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -202,7 +202,7 @@ test_oom_streambuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL; - uint32_t tvms; + uint32_t tvts; int i; smartlist_t *edgeconns = smartlist_new(); const uint64_t start_ns = 1389641159 * (uint64_t)1000000000; @@ -270,22 +270,28 @@ test_oom_streambuf(void *arg) now_ns -= now_ns % 1000000000; now_ns += 1000000000; monotime_coarse_set_mock_time_nsec(now_ns); - tvms = (uint32_t) monotime_coarse_absolute_msec(); + tvts = monotime_coarse_get_stamp(); - tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500); - tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490); - tt_int_op(circuit_max_queued_cell_age(c3, tvms), OP_EQ, 480); - tt_int_op(circuit_max_queued_cell_age(c4, tvms), OP_EQ, 0); +#define ts_is_approx(ts, val) do { \ + uint32_t x_ = (uint32_t) monotime_coarse_stamp_units_to_approx_msec(ts); \ + tt_int_op(x_, OP_GE, val - 5); \ + tt_int_op(x_, OP_LE, val + 5); \ + } while (0) - tt_int_op(circuit_max_queued_data_age(c1, tvms), OP_EQ, 390); - tt_int_op(circuit_max_queued_data_age(c2, tvms), OP_EQ, 380); - tt_int_op(circuit_max_queued_data_age(c3, tvms), OP_EQ, 0); - tt_int_op(circuit_max_queued_data_age(c4, tvms), OP_EQ, 370); + ts_is_approx(circuit_max_queued_cell_age(c1, tvts), 500); + ts_is_approx(circuit_max_queued_cell_age(c2, tvts), 490); + ts_is_approx(circuit_max_queued_cell_age(c3, tvts), 480); + ts_is_approx(circuit_max_queued_cell_age(c4, tvts), 0); - tt_int_op(circuit_max_queued_item_age(c1, tvms), OP_EQ, 500); - tt_int_op(circuit_max_queued_item_age(c2, tvms), OP_EQ, 490); - tt_int_op(circuit_max_queued_item_age(c3, tvms), OP_EQ, 480); - tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 370); + ts_is_approx(circuit_max_queued_data_age(c1, tvts), 390); + ts_is_approx(circuit_max_queued_data_age(c2, tvts), 380); + ts_is_approx(circuit_max_queued_data_age(c3, tvts), 0); + ts_is_approx(circuit_max_queued_data_age(c4, tvts), 370); + + ts_is_approx(circuit_max_queued_item_age(c1, tvts), 500); + ts_is_approx(circuit_max_queued_item_age(c2, tvts), 490); + ts_is_approx(circuit_max_queued_item_age(c3, tvts), 480); + ts_is_approx(circuit_max_queued_item_age(c4, tvts), 370); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 80); @@ -301,7 +307,7 @@ test_oom_streambuf(void *arg) smartlist_add(edgeconns, ec); } tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2); - tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 1000); + ts_is_approx(circuit_max_queued_item_age(c4, tvts), 1000); tt_int_op(cell_queues_check_size(), OP_EQ, 0); @@ -335,7 +341,7 @@ test_oom_streambuf(void *arg) circuit_free(c5); SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec, - connection_free_(TO_CONN(ec))); + connection_free_minimal(TO_CONN(ec))); smartlist_free(edgeconns); UNMOCK(circuit_mark_for_close_); diff --git a/src/test/test_options.c b/src/test/test_options.c index 62732cabf7..b8898766e1 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -906,7 +906,7 @@ test_options_validate__authdir(void *ignored) "Address 100.200.10.1\n" "BridgeAuthoritativeDir 1\n" "ContactInfo hello@hello.com\n" - "V3BandwidthsFile non-existant-file\n"); + "V3BandwidthsFile non-existent-file\n"); mock_clean_saved_logs(); options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_str_op(msg, OP_EQ, @@ -918,7 +918,7 @@ test_options_validate__authdir(void *ignored) "Address 100.200.10.1\n" "BridgeAuthoritativeDir 1\n" "ContactInfo hello@hello.com\n" - "V3BandwidthsFile non-existant-file\n"); + "V3BandwidthsFile non-existent-file\n"); mock_clean_saved_logs(); options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg); tt_str_op(msg, OP_EQ, @@ -930,7 +930,7 @@ test_options_validate__authdir(void *ignored) "Address 100.200.10.1\n" "BridgeAuthoritativeDir 1\n" "ContactInfo hello@hello.com\n" - "GuardfractionFile non-existant-file\n"); + "GuardfractionFile non-existent-file\n"); mock_clean_saved_logs(); options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_str_op(msg, OP_EQ, @@ -942,7 +942,7 @@ test_options_validate__authdir(void *ignored) "Address 100.200.10.1\n" "BridgeAuthoritativeDir 1\n" "ContactInfo hello@hello.com\n" - "GuardfractionFile non-existant-file\n"); + "GuardfractionFile non-existent-file\n"); mock_clean_saved_logs(); options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg); tt_str_op(msg, OP_EQ, @@ -1112,7 +1112,7 @@ test_options_validate__transproxy(void *ignored) // Test unknown trans proxy free_options_test_data(tdata); - tdata = get_options_test_data("TransProxyType non-existant\n"); + tdata = get_options_test_data("TransProxyType non-existent\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "Unrecognized value for TransProxyType"); diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 83dca2d431..f8aa8ac40b 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1318,7 +1318,7 @@ mock_get_interface_address6_list(int severity, return clone_list; done: - free_interface_address6_list(clone_list); + interface_address6_list_free(clone_list); return NULL; } @@ -1393,11 +1393,11 @@ test_policies_reject_interface_address(void *arg) done: addr_policy_list_free(policy); - free_interface_address6_list(public_ipv4_addrs); - free_interface_address6_list(public_ipv6_addrs); + interface_address6_list_free(public_ipv4_addrs); + interface_address6_list_free(public_ipv6_addrs); UNMOCK(get_interface_address6_list); - /* we don't use free_interface_address6_list on these lists because their + /* we don't use interface_address6_list_free on these lists because their * address pointers are stack-based */ smartlist_free(mock_ipv4_addrs); smartlist_free(mock_ipv6_addrs); diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 9b94044b91..c343e9957d 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -8,10 +8,20 @@ #include "protover.h" +#include "or.h" +#include "connection_or.h" + static void test_protover_parse(void *arg) { (void) arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); + done: + ; +#else char *re_encoded = NULL; const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; @@ -78,12 +88,18 @@ test_protover_parse(void *arg) SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent)); smartlist_free(elts); tor_free(re_encoded); +#endif } static void test_protover_parse_fail(void *arg) { (void)arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); +#else smartlist_t *elts; /* random junk */ @@ -109,7 +125,7 @@ test_protover_parse_fail(void *arg) /* Broken range */ elts = parse_protocol_list("Link=1,9-8,3"); tt_ptr_op(elts, OP_EQ, NULL); - +#endif done: ; } @@ -276,6 +292,209 @@ test_protover_all_supported(void *arg) } static void +test_protover_list_supports_protocol_returns_true(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 1); + tt_int_op(is_supported, OP_EQ, 1); + + done: + ; +} + +static void +test_protover_list_supports_protocol_for_unsupported_returns_false(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 10); + tt_int_op(is_supported, OP_EQ, 0); + + done: + ; +} + +static void +test_protover_supports_version(void *arg) +{ + (void)arg; + + tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 3)); + tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 6)); + tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINK, 7)); + tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINKAUTH, 3)); + + tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 2)); + tt_assert(protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 3)); + tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 4)); + tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 4)); + tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 3)); + tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 2)); + + tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_DESC, 2)); + done: + ; +} + +/* This could be MAX_PROTOCOLS_TO_EXPAND, but that's not exposed by protover */ +#define MAX_PROTOCOLS_TO_TEST 1024 + +/* LinkAuth and Relay protocol versions. + * Hard-coded here, because they are not in the code, or not exposed in the + * headers. */ +#define PROTOVER_LINKAUTH_V1 1 +#define PROTOVER_LINKAUTH_V3 3 + +#define PROTOVER_RELAY_V1 1 +#define PROTOVER_RELAY_V2 2 + +/* Highest supported HSv2 introduce protocol version. + * Hard-coded here, because it does not appear anywhere in the code. + * It's not clear if we actually support version 2, see #25068. */ +#define PROTOVER_HSINTRO_V2 3 + +/* HSv2 Rend and HSDir protocol versions. + * Hard-coded here, because they do not appear anywhere in the code. */ +#define PROTOVER_HS_RENDEZVOUS_POINT_V2 1 +#define PROTOVER_HSDIR_V2 1 + +/* DirCache, Desc, Microdesc, and Cons protocol versions. + * Hard-coded here, because they do not appear anywhere in the code. */ +#define PROTOVER_DIRCACHE_V1 1 +#define PROTOVER_DIRCACHE_V2 2 + +#define PROTOVER_DESC_V1 1 +#define PROTOVER_DESC_V2 2 + +#define PROTOVER_MICRODESC_V1 1 +#define PROTOVER_MICRODESC_V2 2 + +#define PROTOVER_CONS_V1 1 +#define PROTOVER_CONS_V2 2 + +/* Make sure we haven't forgotten any supported protocols */ +static void +test_protover_supported_protocols(void *arg) +{ + (void)arg; + + const char *supported_protocols = protover_get_supported_protocols(); + + /* Test for new Link in the code, that hasn't been added to supported + * protocols */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_LINK, + MAX_LINK_PROTO)); + for (uint16_t i = 0; i < MAX_PROTOCOLS_TO_TEST; i++) { + if (is_or_protocol_version_known(i)) { + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_LINK, + i)); + } + } + + /* Legacy LinkAuth does not appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_V1)); + /* Latest LinkAuth is not exposed in the headers. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_V3)); + /* Is there any way to test for new LinkAuth? */ + + /* Relay protovers do not appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_V2)); + /* Is there any way to test for new Relay? */ + + /* We could test legacy HSIntro by calling rend_service_update_descriptor(), + * and checking the protocols field. But that's unlikely to change, so + * we just use a hard-coded value. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HSINTRO_V2)); + /* Test for HSv3 HSIntro */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_V3)); + /* Is there any way to test for new HSIntro? */ + + /* Legacy HSRend does not appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V2)); + /* Test for HSv3 HSRend */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V3)); + /* Is there any way to test for new HSRend? */ + + /* Legacy HSDir does not appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSDIR, + PROTOVER_HSDIR_V2)); + /* Test for HSv3 HSDir */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSDIR, + PROTOVER_HSDIR_V3)); + /* Is there any way to test for new HSDir? */ + + /* No DirCache versions appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_DIRCACHE, + PROTOVER_DIRCACHE_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_DIRCACHE, + PROTOVER_DIRCACHE_V2)); + /* Is there any way to test for new DirCache? */ + + /* No Desc versions appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_DESC, + PROTOVER_DESC_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_DESC, + PROTOVER_DESC_V2)); + /* Is there any way to test for new Desc? */ + + /* No Microdesc versions appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_MICRODESC, + PROTOVER_MICRODESC_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_MICRODESC, + PROTOVER_MICRODESC_V2)); + /* Is there any way to test for new Microdesc? */ + + /* No Cons versions appear anywhere in the code. */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_CONS, + PROTOVER_CONS_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_CONS, + PROTOVER_CONS_V2)); + /* Is there any way to test for new Cons? */ + + done: + ; +} + +static void test_protover_vote_roundtrip(void *args) { (void) args; @@ -360,6 +579,10 @@ struct testcase_t protover_tests[] = { PV_TEST(parse_fail, 0), PV_TEST(vote, 0), PV_TEST(all_supported, 0), + PV_TEST(list_supports_protocol_for_unsupported_returns_false, 0), + PV_TEST(list_supports_protocol_returns_true, 0), + PV_TEST(supports_version, 0), + PV_TEST(supported_protocols, 0), PV_TEST(vote_roundtrip, 0), END_OF_TESTCASES }; diff --git a/src/test/test_relay.c b/src/test/test_relay.c index e3489627a0..73c0ed5586 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -67,10 +67,6 @@ test_relay_append_cell_to_circuit_queue(void *arg) pchan = new_fake_channel(); tt_assert(pchan); - /* We'll need chans with working cmuxes */ - nchan->cmux = circuitmux_alloc(); - pchan->cmux = circuitmux_alloc(); - /* Make a fake orcirc */ orcirc = new_fake_orcirc(nchan, pchan); tt_assert(orcirc); diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index 9354dd0480..9f6cfc4a22 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -834,7 +834,7 @@ test_rend_cache_failure_entry_free(void *data) (void)data; // Test that it can deal with a NULL argument - rend_cache_failure_entry_free(NULL); + rend_cache_failure_entry_free_(NULL); /* done: */ /* (void)0; */ @@ -963,7 +963,7 @@ test_rend_cache_entry_free(void *data) rend_cache_entry_t *e; // Handles NULL correctly - rend_cache_entry_free(NULL); + rend_cache_entry_free_(NULL); // Handles NULL descriptor correctly e = tor_malloc_zero(sizeof(rend_cache_entry_t)); @@ -1135,7 +1135,7 @@ test_rend_cache_failure_intro_entry_free(void *data) rend_cache_failure_intro_t *entry; // Handles a null argument - rend_cache_failure_intro_entry_free(NULL); + rend_cache_failure_intro_entry_free_(NULL); // Handles a non-null argument entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT); @@ -1148,7 +1148,7 @@ test_rend_cache_failure_purge(void *data) (void)data; // Handles a null failure cache - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); rend_cache_failure = NULL; rend_cache_failure_purge(); diff --git a/src/test/test_replay.c b/src/test/test_replay.c index c379cafa7c..d8dcc7370c 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -74,7 +74,7 @@ static void test_replaycache_free_null(void *arg) { (void)arg; - replaycache_free(NULL); + replaycache_free_(NULL); /* Assert that we're here without horrible death */ tt_assert(1); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index d8b72651a0..e4abcdb92d 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -421,6 +421,7 @@ test_routerkeys_ed_keys_init_all(void *arg) { (void)arg; char *dir = tor_strdup(get_fname("test_ed_keys_init_all")); + char *keydir = tor_strdup(get_fname("test_ed_keys_init_all/KEYS")); or_options_t *options = tor_malloc_zero(sizeof(or_options_t)); time_t now = time(NULL); ed25519_public_key_t id; @@ -445,13 +446,14 @@ test_routerkeys_ed_keys_init_all(void *arg) #ifdef _WIN32 mkdir(dir); - mkdir(get_fname("test_ed_keys_init_all/keys")); + mkdir(keydir); #else mkdir(dir, 0700); - mkdir(get_fname("test_ed_keys_init_all/keys"), 0700); + mkdir(keydir, 0700); #endif /* defined(_WIN32) */ options->DataDirectory = dir; + options->KeyDirectory = keydir; tt_int_op(1, OP_EQ, load_ed_keys(options, now)); tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0)); @@ -521,7 +523,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Demonstrate that we can start up with no secret identity key */ routerkeys_free_all(); - unlink(get_fname("test_ed_keys_init_all/keys/" + unlink(get_fname("test_ed_keys_init_all/KEYS/" "ed25519_master_id_secret_key")); tt_int_op(1, OP_EQ, load_ed_keys(options, now)); tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0)); @@ -542,6 +544,7 @@ test_routerkeys_ed_keys_init_all(void *arg) done: tor_free(dir); + tor_free(keydir); tor_free(options); tor_cert_free(link_cert); routerkeys_free_all(); diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 3b0e943ce5..c19d66ef9d 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -5,7 +5,11 @@ #include <math.h> #include <time.h> +#define CONNECTION_PRIVATE +#define DIRECTORY_PRIVATE #define DIRVOTE_PRIVATE +#define ENTRYNODES_PRIVATE +#define HIBERNATE_PRIVATE #define NETWORKSTATUS_PRIVATE #define ROUTERLIST_PRIVATE #define TOR_UNIT_TESTING @@ -13,19 +17,24 @@ #include "config.h" #include "connection.h" #include "container.h" +#include "control.h" #include "directory.h" #include "dirvote.h" #include "entrynodes.h" +#include "hibernate.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" #include "policies.h" #include "router.h" #include "routerlist.h" +#include "routerset.h" #include "routerparse.h" #include "shared_random.h" +#include "statefile.h" #include "test.h" #include "test_dir_common.h" +#include "log_test_helpers.h" void construct_consensus(char **consensus_text_md); @@ -411,6 +420,111 @@ test_router_pick_directory_server_impl(void *arg) networkstatus_vote_free(con_md); } +static or_state_t *dummy_state = NULL; +static or_state_t * +get_or_state_replacement(void) +{ + return dummy_state; +} + +static void +mock_directory_initiate_request(directory_request_t *req) +{ + (void)req; + return; +} + +static circuit_guard_state_t * +mock_circuit_guard_state_new(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst) +{ + (void) guard; + (void) state; + (void) rst; + return NULL; +} + +/** Test that we will use our directory guards to fetch mds even if we don't + * have any dirinfo (tests bug #23862). */ +static void +test_directory_guard_fetch_with_no_dirinfo(void *arg) +{ + int retval; + char *consensus_text_md = NULL; + or_options_t *options = get_options_mutable(); + + (void) arg; + + hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE); + + /* Initialize the SRV subsystem */ + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + sr_init(0); + UNMOCK(get_my_v3_authority_cert); + + /* Initialize the entry node configuration from the ticket */ + options->UseEntryGuards = 1; + options->StrictNodes = 1; + get_options_mutable()->EntryNodes = routerset_new(); + routerset_parse(get_options_mutable()->EntryNodes, + "2121212121212121212121212121212121212121", "foo"); + + /* Mock some functions */ + dummy_state = tor_malloc_zero(sizeof(or_state_t)); + MOCK(get_or_state, get_or_state_replacement); + MOCK(directory_initiate_request, mock_directory_initiate_request); + /* we need to mock this one to avoid memleaks */ + MOCK(circuit_guard_state_new, mock_circuit_guard_state_new); + + /* Call guards_update_all() to simulate loading our state file (see + * entry_guards_load_guards_from_state() and ticket #23989). */ + guards_update_all(); + + /* Test logic: Simulate the arrival of a new consensus when we have no + * dirinfo at all. Tor will need to fetch the mds from the consensus. Make + * sure that Tor will use the specified entry guard instead of relying on the + * fallback directories. */ + + /* Fixup the dirconn that will deliver the consensus */ + dir_connection_t *conn = dir_connection_new(AF_INET); + tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + conn->base_.port = 8800; + TO_CONN(conn)->address = tor_strdup("127.0.0.1"); + conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS; + conn->requested_resource = tor_strdup("ns"); + + /* Construct a consensus */ + construct_consensus(&consensus_text_md); + tt_assert(consensus_text_md); + + /* Place the consensus in the dirconn */ + response_handler_args_t args; + memset(&args, 0, sizeof(response_handler_args_t)); + args.status_code = 200; + args.body = consensus_text_md; + args.body_len = strlen(consensus_text_md); + + /* Update approx time so that the consensus is considered live */ + update_approx_time(time(NULL)+1010); + + setup_capture_of_logs(LOG_DEBUG); + + /* Now handle the consensus */ + retval = handle_response_fetch_consensus(conn, &args); + tt_int_op(retval, OP_EQ, 0); + + /* Make sure that our primary guard was chosen */ + expect_log_msg_containing("Selected primary guard router3"); + + done: + tor_free(consensus_text_md); + tor_free(dummy_state); + connection_free_minimal(TO_CONN(conn)); + entry_guards_free_all(); + teardown_capture_of_logs(); +} + static connection_t *mocked_connection = NULL; /* Mock connection_get_by_type_addr_port_purpose by returning @@ -494,6 +608,8 @@ struct testcase_t routerlist_tests[] = { NODE(launch_descriptor_downloads, 0), NODE(router_is_already_dir_fetching, TT_FORK), ROUTER(pick_directory_server_impl, TT_FORK), + { "directory_guard_fetch_with_no_dirinfo", + test_directory_guard_fetch_with_no_dirinfo, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index c9c69911da..c541324674 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define ROUTERSET_PRIVATE #include "or.h" @@ -696,7 +699,7 @@ NS(test_main)(void *arg) static void NS(test_main)(void *arg) { - const routerset_t *set; + routerset_t *set; int needs_geoip; (void)arg; @@ -706,14 +709,14 @@ NS(test_main)(void *arg) set = routerset_new(); needs_geoip = routerset_needs_geoip(set); - routerset_free((routerset_t *)set); + routerset_free(set); tt_int_op(needs_geoip, OP_EQ, 0); set = NULL; set = routerset_new(); smartlist_add(set->country_names, tor_strndup("xx", 2)); needs_geoip = routerset_needs_geoip(set); - routerset_free((routerset_t *)set); + routerset_free(set); set = NULL; tt_int_op(needs_geoip, OP_NE, 0); @@ -1944,7 +1947,7 @@ NS(test_main)(void *arg) done: tor_free(s); - routerset_free((routerset_t *)set); + routerset_free(set); } #undef NS_SUBMODULE @@ -2081,28 +2084,28 @@ NS(test_main)(void *arg) * Structural test for routerset_free, where the routerset is NULL. */ -NS_DECL(void, smartlist_free, (smartlist_t *sl)); +NS_DECL(void, smartlist_free_, (smartlist_t *sl)); static void NS(test_main)(void *arg) { (void)arg; - NS_MOCK(smartlist_free); + NS_MOCK(smartlist_free_); - routerset_free(NULL); + routerset_free_(NULL); - tt_int_op(CALLED(smartlist_free), OP_EQ, 0); + tt_int_op(CALLED(smartlist_free_), OP_EQ, 0); done: ; } void -NS(smartlist_free)(smartlist_t *s) +NS(smartlist_free_)(smartlist_t *s) { (void)s; - CALLED(smartlist_free)++; + CALLED(smartlist_free_)++; } #undef NS_SUBMODULE @@ -2112,9 +2115,9 @@ NS(smartlist_free)(smartlist_t *s) * Structural test for routerset_free. */ -NS_DECL(void, smartlist_free, (smartlist_t *sl)); -NS_DECL(void, strmap_free,(strmap_t *map, void (*free_val)(void*))); -NS_DECL(void, digestmap_free, (digestmap_t *map, void (*free_val)(void*))); +NS_DECL(void, smartlist_free_, (smartlist_t *sl)); +NS_DECL(void, strmap_free_,(strmap_t *map, void (*free_val)(void*))); +NS_DECL(void, digestmap_free_, (digestmap_t *map, void (*free_val)(void*))); static void NS(test_main)(void *arg) @@ -2122,39 +2125,39 @@ NS(test_main)(void *arg) routerset_t *routerset = routerset_new(); (void)arg; - NS_MOCK(smartlist_free); - NS_MOCK(strmap_free); - NS_MOCK(digestmap_free); + NS_MOCK(smartlist_free_); + NS_MOCK(strmap_free_); + NS_MOCK(digestmap_free_); routerset_free(routerset); - tt_int_op(CALLED(smartlist_free), OP_NE, 0); - tt_int_op(CALLED(strmap_free), OP_NE, 0); - tt_int_op(CALLED(digestmap_free), OP_NE, 0); + tt_int_op(CALLED(smartlist_free_), OP_NE, 0); + tt_int_op(CALLED(strmap_free_), OP_NE, 0); + tt_int_op(CALLED(digestmap_free_), OP_NE, 0); done: ; } void -NS(smartlist_free)(smartlist_t *s) +NS(smartlist_free_)(smartlist_t *s) { - CALLED(smartlist_free)++; - smartlist_free__real(s); + CALLED(smartlist_free_)++; + smartlist_free___real(s); } void -NS(strmap_free)(strmap_t *map, void (*free_val)(void*)) +NS(strmap_free_)(strmap_t *map, void (*free_val)(void*)) { - CALLED(strmap_free)++; - strmap_free__real(map, free_val); + CALLED(strmap_free_)++; + strmap_free___real(map, free_val); } void -NS(digestmap_free)(digestmap_t *map, void (*free_val)(void*)) +NS(digestmap_free_)(digestmap_t *map, void (*free_val)(void*)) { - CALLED(digestmap_free)++; - digestmap_free__real(map, free_val); + CALLED(digestmap_free_)++; + digestmap_free___real(map, free_val); } #undef NS_SUBMODULE diff --git a/src/test/test_rust.c b/src/test/test_rust.c deleted file mode 100644 index 6ad57d6fcb..0000000000 --- a/src/test/test_rust.c +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "orconfig.h" -#include "compat_rust.h" -#include "test.h" -#include "util.h" - -static void -test_welcome_string(void *arg) -{ - (void)arg; - rust_str_t s = rust_welcome_string(); - const char *c_str = rust_str_get(s); - tt_assert(c_str); - size_t len = strlen(c_str); -#ifdef HAVE_RUST - tt_assert(len > 0); -#else - tt_assert(len == 0); -#endif - - done: - rust_str_free(s); -} - -struct testcase_t rust_tests[] = { - { "welcome_string", test_welcome_string, 0, NULL, NULL }, - END_OF_TESTCASES -}; - diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh index d559f94ce0..133f2bb940 100755 --- a/src/test/test_rust.sh +++ b/src/test/test_rust.sh @@ -1,13 +1,20 @@ #!/bin/sh -# Test all the Rust crates we're using +# Test all Rust crates -crates=tor_util +crates="protover tor_util smartlist tor_allocate" exitcode=0 +set -e + for crate in $crates; do - cd "${abs_top_srcdir:-.}/src/rust/${crate}" - CARGO_TARGET_DIR="${abs_top_builddir}/src/rust/target" CARGO_HOME="${abs_top_builddir}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 + cd "${abs_top_builddir:-../../..}/src/rust" + CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \ + CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" \ + "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \ + --manifest-path "${abs_top_srcdir:-.}/src/rust/${crate}/Cargo.toml" \ + || exitcode=1 + cd - done exit $exitcode diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 724a6b56b2..ebba71266c 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -299,10 +299,6 @@ channel_more_to_flush_mock(channel_t *chan) flush_mock_channel_t *found_mock_ch = NULL; - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock, flush_mock_channel_t *, flush_mock_ch) { @@ -444,8 +440,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type) /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); @@ -457,7 +451,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type) ch2 = new_fake_channel(); tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); @@ -668,8 +661,6 @@ test_scheduler_loop_vanilla(void *arg) /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); @@ -684,7 +675,6 @@ test_scheduler_loop_vanilla(void *arg) ch2->magic = TLS_CHAN_MAGIC; tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); /* @@ -836,7 +826,6 @@ test_scheduler_loop_kist(void *arg) tt_assert(ch1); ch1->magic = TLS_CHAN_MAGIC; ch1->state = CHANNEL_STATE_OPENING; - ch1->cmux = circuitmux_alloc(); channel_register(ch1); tt_assert(ch1->registered); channel_change_state_open(ch1); @@ -847,7 +836,6 @@ test_scheduler_loop_kist(void *arg) tt_assert(ch2); ch2->magic = TLS_CHAN_MAGIC; ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); channel_change_state_open(ch2); @@ -1083,6 +1071,271 @@ test_scheduler_ns_changed(void *arg) return; } +/* + * Mocked functions for the kist_pending_list test. + */ + +static int mock_flush_some_cells_num = 1; +static int mock_more_to_flush = 0; +static int mock_update_socket_info_limit = 0; + +static ssize_t +channel_flush_some_cells_mock_var(channel_t *chan, ssize_t num_cells) +{ + (void) chan; + (void) num_cells; + return mock_flush_some_cells_num; +} + +/* Because when we flush cells, it is possible that the connection outbuf gets + * fully drained, the wants to write scheduler event is fired back while we + * are in the scheduler loop so this mock function does it for us. + * Furthermore, the socket limit is set to 0 so once this is triggered, it + * informs the scheduler that it can't write on the socket anymore. */ +static void +channel_write_to_kernel_mock_trigger_24700(channel_t *chan) +{ + static int chan_id_seen[2] = {0}; + if (++chan_id_seen[chan->global_identifier - 1] > 1) { + tt_assert(0); + } + + scheduler_channel_wants_writes(chan); + + done: + return; +} + +static int +channel_more_to_flush_mock_var(channel_t *chan) +{ + (void) chan; + return mock_more_to_flush; +} + +static void +update_socket_info_impl_mock_var(socket_table_ent_t *ent) +{ + ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0; + ent->limit = mock_update_socket_info_limit; +} + +static void +test_scheduler_kist_pending_list(void *arg) +{ + (void) arg; + +#ifndef HAVE_KIST_SUPPORT + return; +#endif + + /* This is for testing the channel flow with the pending list that is + * depending on the channel state, what will be the expected behavior of the + * scheduler with that list. + * + * For instance, we want to catch double channel add or removing a channel + * that doesn't exists, or putting a channel in the list in a wrong state. + * Essentially, this will articifically test cases of the KIST main loop and + * entry point in the channel subsystem. + * + * In part, this is to also catch things like #24700 and provide a test bed + * for more testing in the future like so. */ + + /* Mocking a series of scheduler function to control the flow of the + * scheduler loop to test every use cases and assess the pending list. */ + MOCK(get_options, mock_get_options); + MOCK(channel_flush_some_cells, channel_flush_some_cells_mock_var); + MOCK(channel_more_to_flush, channel_more_to_flush_mock_var); + MOCK(update_socket_info_impl, update_socket_info_impl_mock_var); + MOCK(channel_write_to_kernel, channel_write_to_kernel_mock); + MOCK(channel_should_write_to_kernel, channel_should_write_to_kernel_mock); + + /* Setup options so we're sure about what sched we are running */ + mocked_options.KISTSchedRunInterval = 10; + set_scheduler_options(SCHEDULER_KIST); + + /* Init scheduler. */ + scheduler_init(); + + /* Initialize a channel. We'll need a second channel for the #24700 bug + * test. */ + channel_t *chan1 = new_fake_channel(); + channel_t *chan2 = new_fake_channel(); + tt_assert(chan1); + tt_assert(chan2); + chan1->magic = chan2->magic = TLS_CHAN_MAGIC; + channel_register(chan1); + channel_register(chan2); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE); + tt_int_op(chan1->sched_heap_idx, OP_EQ, -1); + tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE); + tt_int_op(chan2->sched_heap_idx, OP_EQ, -1); + + /* Once a channel becomes OPEN, it always have at least one cell in it so + * the scheduler is notified that the channel wants to write so this is the + * first step. Might not make sense to you but it is the way it is. */ + scheduler_channel_wants_writes(chan1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + + /* Signal the scheduler that it has waiting cells which means the channel + * will get scheduled. */ + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + /* Subsequent call should not add it more times. It is possible we add many + * cells in rapid succession before the channel is scheduled. */ + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + + /* We'll flush one cell and make it that the socket can write but no more to + * flush else we end up in an infinite loop. We expect the channel to be put + * in waiting for cells state and the pending list empty. */ + mock_update_socket_info_limit = INT_MAX; + mock_more_to_flush = 0; + the_scheduler->run(); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Lets make believe that a cell is now in the channel but this time the + * channel can't write so obviously it has more to flush. We expect the + * channel to be back in the pending list. */ + scheduler_channel_has_waiting_cells(chan1); + mock_update_socket_info_limit = 0; + mock_more_to_flush = 1; + the_scheduler->run(); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + + /* Channel is in the pending list now, during that time, we'll trigger a + * wants to write event because maybe the channel buffers were emptied in + * the meantime. This is possible because once the connection outbuf is + * flushed down the low watermark, the scheduler is notified. + * + * We expect the channel to NOT be added in the pending list again and stay + * in PENDING state. */ + scheduler_channel_wants_writes(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + + /* Make it that the channel can write now but has nothing else to flush. We + * expect that it is removed from the pending list and waiting for cells. */ + mock_update_socket_info_limit = INT_MAX; + mock_more_to_flush = 0; + the_scheduler->run(); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS); + + /* While waiting for cells, lets say we were able to write more things on + * the connection outbuf (unlikely that this can happen but let say it + * does). We expect the channel to stay in waiting for cells. */ + scheduler_channel_wants_writes(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS); + + /* We'll not put it in the pending list and make the flush cell fail with 0 + * cell flushed. We expect that it is put back in waiting for cells. */ + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + mock_flush_some_cells_num = 0; + the_scheduler->run(); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Set the channel to a state where it doesn't want to write more. We expect + * that the channel becomes idle. */ + scheduler_channel_doesnt_want_writes(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE); + + /* Some cells arrive on the channel now. We expect it to go back in waiting + * to write. You might wonder why it is not put in the pending list? Because + * once the channel becomes OPEN again (the doesn't want to write event only + * occurs if the channel goes in MAINT mode), if there are cells in the + * channel, the wants to write event is triggered thus putting the channel + * in pending mode. + * + * Else, if no cells, it stays IDLE and then once a cell comes in, it should + * go in waiting to write which is a BUG itself because the channel can't be + * scheduled until a second cell comes in. Hopefully, #24554 will fix that + * for KIST. */ + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE); + + /* Second cell comes in, unfortunately, it won't get scheduled until a wants + * to write event occurs like described above. */ + scheduler_channel_has_waiting_cells(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE); + + /* Unblock everything putting the channel in the pending list. */ + scheduler_channel_wants_writes(chan1); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + + /* Testing bug #24700 which is the situation where we have at least two + * different channels in the pending list. The first one gets flushed and + * bytes are written on the wire which triggers a wants to write event + * because the outbuf is below the low watermark. The bug was that this + * exact channel was added back in the pending list because its state wasn't + * PENDING. + * + * The following does some ninja-tsu to try to make it happen. We need two + * different channels so we create a second one and add it to the pending + * list. Then, we have a custom function when we write to kernel that does + * two important things: + * + * 1) Calls scheduler_channel_wants_writes(chan) on the channel. + * 2) Keeps track of how many times it sees the channel going through. If + * that limit goes > 1, it means we've added the channel twice in the + * pending list. + * + * In the end, we expect both channels to be in the pending list after this + * scheduler run. */ + + /* Put the second channel in the pending list. */ + scheduler_channel_wants_writes(chan2); + scheduler_channel_has_waiting_cells(chan2); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2); + tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + + /* This makes it that the first pass on socket_can_write() will be true but + * then when a single cell is flushed (514 + 29 bytes), the second call to + * socket_can_write() will be false. If it wasn't sending back false on the + * second run, we end up in an infinite loop of the scheduler. */ + mock_update_socket_info_limit = 600; + /* We want to hit "Case 3:" of the scheduler so channel_more_to_flush() is + * true but socket_can_write() has to be false on the second check on the + * channel. */ + mock_more_to_flush = 1; + mock_flush_some_cells_num = 1; + MOCK(channel_write_to_kernel, channel_write_to_kernel_mock_trigger_24700); + the_scheduler->run(); + tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2); + tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING); + + done: + chan1->state = chan2->state = CHANNEL_STATE_CLOSED; + chan1->registered = chan2->registered = 0; + channel_free(chan1); + channel_free(chan2); + scheduler_free_all(); + + UNMOCK(get_options); + UNMOCK(channel_flush_some_cells); + UNMOCK(channel_more_to_flush); + UNMOCK(update_socket_info_impl); + UNMOCK(channel_write_to_kernel); + UNMOCK(channel_should_write_to_kernel); +} + struct testcase_t scheduler_tests[] = { { "compare_channels", test_scheduler_compare_channels, TT_FORK, NULL, NULL }, @@ -1092,6 +1345,8 @@ struct testcase_t scheduler_tests[] = { { "loop_kist", test_scheduler_loop_kist, TT_FORK, NULL, NULL }, { "ns_changed", test_scheduler_ns_changed, TT_FORK, NULL, NULL}, { "should_use_kist", test_scheduler_can_use_kist, TT_FORK, NULL, NULL }, + { "kist_pending_list", test_scheduler_kist_pending_list, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index cac78baecf..4fe9ee45f0 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define SHARED_RANDOM_PRIVATE #define SHARED_RANDOM_STATE_PRIVATE #define CONFIG_PRIVATE @@ -399,7 +402,7 @@ test_sr_commit(void *arg) sizeof(our_commit->hashed_reveal))); /* Do we have a valid encoded commit and reveal. Note the following only * tests if the generated values are correct. Their could be a bug in - * the decode function but we test them seperately. */ + * the decode function but we test them separately. */ tt_int_op(0, OP_EQ, reveal_decode(our_commit->encoded_reveal, &test_commit)); tt_int_op(0, OP_EQ, commit_decode(our_commit->encoded_commit, @@ -609,7 +612,7 @@ test_vote(void *arg) ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0); tt_int_op(ret, OP_EQ, 4); tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate"); - /* Get our commitment line and will validate it agains our commit. The + /* Get our commitment line and will validate it against our commit. The * format is as follow: * "shared-rand-commitment" SP version SP algname SP identity * SP COMMIT [SP REVEAL] NL @@ -1348,7 +1351,7 @@ test_state_update(void *arg) tt_assert(state->current_srv); done: - sr_state_free(); + sr_state_free_all(); UNMOCK(get_my_v3_authority_cert); } diff --git a/src/test/test_status.c b/src/test/test_status.c index f86f8e3b9e..50ea203e4d 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define STATUS_PRIVATE #define HIBERNATE_PRIVATE #define LOG_PRIVATE diff --git a/src/test/test_util.c b/src/test/test_util.c index 0519a4758f..b67fad58e3 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -703,7 +703,7 @@ test_util_time(void *arg) * a "correct" retrospective gregorian negative year value, * which I'm pretty sure is: * -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657 - * 730485 is the number of days in two millenia, including leap days */ + * 730485 is the number of days in two millennia, including leap days */ a_time.tm_year = -292277022657-1900; CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); @@ -739,7 +739,7 @@ test_util_time(void *arg) * a "correct" proleptic gregorian year value, * which I'm pretty sure is: * (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596 - * 730485 is the number of days in two millenia, including leap days */ + * 730485 is the number of days in two millennia, including leap days */ a_time.tm_year = 292277026596-1900; CAPTURE(); tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time)); @@ -875,7 +875,7 @@ test_util_time(void *arg) * a "correct" retrospective gregorian negative year value, * which I'm pretty sure is: * -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657 - * 730485 is the number of days in two millenia, including leap days + * 730485 is the number of days in two millennia, including leap days * (int64_t)b_time.tm_year == (-292277022657LL-1900LL) without clamping */ t_res = INT64_MIN; CAPTURE(); @@ -921,7 +921,7 @@ test_util_time(void *arg) * a "correct" proleptic gregorian year value, * which I'm pretty sure is: * (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596 - * 730485 is the number of days in two millenia, including leap days + * 730485 is the number of days in two millennia, including leap days * (int64_t)b_time.tm_year == (292277026596L-1900L) without clamping */ t_res = INT64_MAX; CAPTURE(); @@ -5465,7 +5465,7 @@ is_there_a_localhost(int family) #endif /* 0 */ /* Test for socketpair and ersatz_socketpair(). We test them both, since - * the latter is a tolerably good way to exersize tor_accept_socket(). */ + * the latter is a tolerably good way to exercise tor_accept_socket(). */ static void test_util_socketpair(void *arg) { @@ -5801,6 +5801,7 @@ test_util_monotonic_time(void *arg) monotime_coarse_t mtc1, mtc2; uint64_t nsec1, nsec2, usec1, msec1; uint64_t nsecc1, nsecc2, usecc1, msecc1; + uint32_t stamp1, stamp2; monotime_init(); @@ -5812,6 +5813,7 @@ test_util_monotonic_time(void *arg) nsecc1 = monotime_coarse_absolute_nsec(); usecc1 = monotime_coarse_absolute_usec(); msecc1 = monotime_coarse_absolute_msec(); + stamp1 = monotime_coarse_to_stamp(&mtc1); tor_sleep_msec(200); @@ -5819,6 +5821,7 @@ test_util_monotonic_time(void *arg) monotime_coarse_get(&mtc2); nsec2 = monotime_absolute_nsec(); nsecc2 = monotime_coarse_absolute_nsec(); + stamp2 = monotime_coarse_to_stamp(&mtc2); /* We need to be a little careful here since we don't know the system load. */ @@ -5835,10 +5838,15 @@ test_util_monotonic_time(void *arg) tt_u64_op(usec1, OP_GE, nsec1 / 1000); tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000); tt_u64_op(usecc1, OP_GE, nsecc1 / 1000); - tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1); - tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000); - tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); - tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000); + tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 10); + tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 10000); + tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 10); + tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10000); + + uint64_t coarse_stamp_diff = + monotime_coarse_stamp_units_to_approx_msec(stamp2-stamp1); + tt_u64_op(coarse_stamp_diff, OP_GE, 120); + tt_u64_op(coarse_stamp_diff, OP_LE, 1200); done: ; @@ -5918,6 +5926,64 @@ test_util_monotonic_time_ratchet(void *arg) } static void +test_util_monotonic_time_zero(void *arg) +{ + (void) arg; + monotime_t t1; + monotime_coarse_t ct1; + monotime_init(); + /* Check 1: The current time is not zero. */ + monotime_get(&t1); + monotime_coarse_get(&ct1); + tt_assert(!monotime_is_zero(&t1)); + tt_assert(!monotime_coarse_is_zero(&ct1)); + + /* Check 2: The _zero() makes the time zero. */ + monotime_zero(&t1); + monotime_coarse_zero(&ct1); + tt_assert(monotime_is_zero(&t1)); + tt_assert(monotime_coarse_is_zero(&ct1)); + done: + ; +} + +static void +test_util_monotonic_time_add_msec(void *arg) +{ + (void) arg; + monotime_t t1, t2; + monotime_coarse_t ct1, ct2; + monotime_init(); + + monotime_get(&t1); + monotime_coarse_get(&ct1); + + /* adding zero does nothing */ + monotime_add_msec(&t2, &t1, 0); + monotime_coarse_add_msec(&ct2, &ct1, 0); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 0); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 0); + + /* Add 1337 msec; see if the diff function agree */ + monotime_add_msec(&t2, &t1, 1337); + monotime_coarse_add_msec(&ct2, &ct1, 1337); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337); + + /* Add 1337 msec twice more; make sure that any second rollover issues + * worked. */ + monotime_add_msec(&t2, &t2, 1337); + monotime_coarse_add_msec(&ct2, &ct2, 1337); + monotime_add_msec(&t2, &t2, 1337); + monotime_coarse_add_msec(&ct2, &ct2, 1337); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3); + + done: + ; +} + +static void test_util_htonll(void *arg) { (void)arg; @@ -6150,6 +6216,8 @@ struct testcase_t util_tests[] = { UTIL_TEST(calloc_check, 0), UTIL_TEST(monotonic_time, 0), UTIL_TEST(monotonic_time_ratchet, TT_FORK), + UTIL_TEST(monotonic_time_zero, 0), + UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(htonll, 0), UTIL_TEST(get_unquoted_path, 0), END_OF_TESTCASES diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 7e9c47b48d..52729147b2 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -3,12 +3,6 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; - -/* Ordinarily defined in tor_main.c; this bit is just here to provide one - * since we're not linking to tor_main.c */ -const char tor_git_revision[] = ""; - /** * \file test_common.c * \brief Common pieces to implement unit tests. @@ -284,6 +278,7 @@ main(int c, const char **v) s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; add_stream_log(&s, "", fileno(stdout)); } + init_protocol_warning_severity_level(); options->command = CMD_RUN_UNITTESTS; if (crypto_global_init(accel_crypto, NULL, NULL)) { @@ -299,6 +294,9 @@ main(int c, const char **v) setup_directory(); options_init(options); options->DataDirectory = tor_strdup(temp_dir); + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + options->CacheDirectory = tor_strdup(temp_dir); options->EntryStatistics = 1; if (set_options(options, &errmsg) < 0) { printf("Failed to set initial options: %s\n", errmsg); diff --git a/src/tools/include.am b/src/tools/include.am index 37936c742f..92cc3f10a2 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -45,3 +45,8 @@ src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \ endif EXTRA_DIST += src/tools/tor-fw-helper/README + +if BUILD_LIBTORRUNNER +noinst_LIBRARIES += src/tools/libtorrunner.a +src_tools_libtorrunner_a_SOURCES = src/tools/tor_runner.c src/or/tor_api.c +endif diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c index 1e2409a131..966b88b3e8 100644 --- a/src/tools/tor-resolve.c +++ b/src/tools/tor-resolve.c @@ -347,7 +347,6 @@ main(int argc, char **argv) int n_args; tor_addr_t result; char *result_hostname = NULL; - log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t)); init_logging(1); sandbox_disable_getaddrinfo_cache(); @@ -398,11 +397,14 @@ main(int argc, char **argv) usage(); } + log_severity_list_t *severities = + tor_malloc_zero(sizeof(log_severity_list_t)); if (isVerbose) - set_log_severity_config(LOG_DEBUG, LOG_ERR, s); + set_log_severity_config(LOG_DEBUG, LOG_ERR, severities); else - set_log_severity_config(LOG_WARN, LOG_ERR, s); - add_stream_log(s, "<stderr>", fileno(stderr)); + set_log_severity_config(LOG_WARN, LOG_ERR, severities); + add_stream_log(severities, "<stderr>", fileno(stderr)); + tor_free(severities); if (n_args == 1) { log_debug(LD_CONFIG, "defaulting to localhost"); diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c new file mode 100644 index 0000000000..9ed2ee5775 --- /dev/null +++ b/src/tools/tor_runner.c @@ -0,0 +1,101 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file tor_runner.c + * @brief Experimental module to emulate tor_run_main() API with fork+exec + * + * The functions here are meant to allow the application developer to + * use the tor_run_main() API without having to care whether Tor is + * running in-process or out-of-process. For in-process usage, the + * developer can link Tor as a library and call tor_run_main(); for + * out-of-process usage, the developer can link this library instead. + * + * This interface is EXPERIMENTAL; please let us know if you would like + * to depend on it. We don't know yet whether it will be reliable in + * practice. + */ + +/* NOTE: This module is supposed to work without the standard Tor utility + * functions. Don't add more dependencies! + */ + +#include "tor_api.h" +#include "tor_api_internal.h" + +#include "orconfig.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <stdlib.h> +#include <string.h> + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +static void child(const tor_main_configuration_t *cfg) + __attribute__((noreturn)); + +int +tor_run_main(const tor_main_configuration_t *cfg) +{ + pid_t pid = fork(); + if (pid == 0) { + child(cfg); + exit(0); /* Unreachable */ + } + + pid_t stopped_pid; + int status = 0; + do { + stopped_pid = waitpid(pid, &status, 0); + } while (stopped_pid == -1); + + /* Note: these return values are not documented. No return value is + * documented! */ + + if (stopped_pid != pid) { + return -99999; + } + if (WIFSTOPPED(status)) { + return WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + return -WTERMSIG(status); + } + + return -999988; +} + +/* circumlocution to avoid getting warned about calling calloc instead of + * tor_calloc. */ +#define real_calloc calloc + +static void +child(const tor_main_configuration_t *cfg) +{ + /* XXXX Close unused file descriptors. */ + + char **args = real_calloc(cfg->argc+1, sizeof(char *)); + memcpy(args, cfg->argv, cfg->argc * sizeof(char *)); + args[cfg->argc] = NULL; + + int rv = execv(BINDIR "/tor", args); + + if (rv < 0) { + exit(254); + } else { + abort(); /* Unreachable */ + } +} + diff --git a/src/trunnel/hs/cell_introduce1.trunnel b/src/trunnel/hs/cell_introduce1.trunnel index 7577c1526f..574382b163 100644 --- a/src/trunnel/hs/cell_introduce1.trunnel +++ b/src/trunnel/hs/cell_introduce1.trunnel @@ -14,7 +14,7 @@ const TRUNNEL_REND_COOKIE_LEN = 20; /* INTRODUCE1 payload. See details in section 3.2.1. */ struct trn_cell_introduce1 { - /* Always zeroed. MUST be checked explicitely by the caller. */ + /* Always zeroed. MUST be checked explicitly by the caller. */ u8 legacy_key_id[TRUNNEL_SHA1_LEN]; /* Authentication key material. */ diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index d3645d9fb2..218cc36ec9 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -218,7 +218,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.3.2.9-dev" +#define VERSION "0.3.3.2-alpha-dev" |