diff options
123 files changed, 6240 insertions, 2867 deletions
diff --git a/.gitignore b/.gitignore index 3a28ecfd44..8034ae6611 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ *.log # Autotools stuff .deps +.dirstamp # Stuff made by our makefiles *.bak @@ -41,6 +42,7 @@ /config.guess /config.sub /conftest* +/micro-revision.* /patch-stamp /stamp-h /stamp-h.in @@ -140,7 +142,6 @@ /src/or/Makefile /src/or/Makefile.in /src/or/or_sha1.i -/src/or/micro-revision.* /src/or/tor /src/or/tor.exe /src/or/libtor.a @@ -1,3 +1,418 @@ +Changes in version 0.2.3.22-rc - 2012-09-11 + Tor 0.2.3.22-rc fixes another opportunity for a remotely triggerable + assertion. + + o Security fixes: + - Fix an assertion failure in tor_timegm() that could be triggered + by a badly formatted directory object. Bug found by fuzzing with + Radamsa. Fixes bug 6811; bugfix on 0.2.0.20-rc. + + o Minor bugfixes: + - Avoid segfault when starting up having run with an extremely old + version of Tor and parsing its state file. Fixes bug 6801; bugfix + on 0.2.2.23-alpha. + + +Changes in version 0.2.2.39 - 2012-09-11 + Tor 0.2.2.39 fixes two more opportunities for remotely triggerable + assertions. + + o Security fixes: + - Fix an assertion failure in tor_timegm() that could be triggered + by a badly formatted directory object. Bug found by fuzzing with + Radamsa. Fixes bug 6811; bugfix on 0.2.0.20-rc. + - Do not crash when comparing an address with port value 0 to an + address policy. This bug could have been used to cause a remote + assertion failure by or against directory authorities, or to + allow some applications to crash clients. Fixes bug 6690; bugfix + on 0.2.1.10-alpha. + + +Changes in version 0.2.4.2-alpha - 2012-09-10 + Tor 0.2.4.2-alpha enables port forwarding for pluggable transports, + raises the default rate limiting even more, and makes the bootstrapping + log messages less noisy. + + o Major features: + - Automatically forward the TCP ports of pluggable transport + proxies using tor-fw-helper if PortForwarding is enabled. Implements + ticket 4567. + + o Major bugfixes: + - Raise the default BandwidthRate/BandwidthBurst values from 5MB/10MB + to 1GB/1GB. The previous defaults were intended to be "basically + infinite", but it turns out they're now limiting our 100mbit+ + relays and bridges. Fixes bug 6605; bugfix on 0.2.0.10-alpha (the + last time we raised it). + + o Minor features: + - Detect when we're running with a version of OpenSSL other than the + one we compiled with. This has occasionally given people hard-to- + track-down errors. + - Log fewer lines at level "notice" about our OpenSSL and Libevent + versions and capabilities when everything is going right. Resolves + part of ticket 6736. + - Directory authorities no long accept descriptors for any version of + Tor before 0.2.2.35, or for any 0.2.3 release before 0.2.3.10-alpha. + These versions are insecure, unsupported, or both. Implements + ticket 6789. + + o Minor bugfixes: + - Rename the (internal-use-only) UsingTestingNetworkDefaults option + to start with a triple-underscore so the controller won't touch it. + Patch by Meejah. Fixes bug 3155. Bugfix on 0.2.2.23-alpha. + - Avoid segfault when starting up having run with an extremely old + version of Tor and parsing its state file. Fixes bug 6801; bugfix + on 0.2.2.23-alpha. + - Rename the (testing-use-only) _UseFilteringSSLBufferevents option + so it doesn't start with _. Fixes bug 3155. Bugfix on 0.2.3.1-alpha. + - Don't follow the NULL pointer if microdescriptor generation fails. + (This does not appear to be triggerable, but it's best to be safe.) + Found by "f. tp.". Fixes bug 6797; bugfix on 0.2.4.1-alpha. + - Fix mis-declared dependencies on src/common/crypto.c and + src/or/tor_main.c that could break out-of-tree builds under some + circumstances. Fixes bug 6778; bugfix on 0.2.4.1-alpha. + - Avoid a warning when building common_sha1.i out of tree. Fixes bug + 6778; bugfix on 0.2.4.1-alpha. + - Fix a harmless (in this case) build warning for implicitly + converting a strlen() to an int. Bugfix on 0.2.4.1-alpha. + + o Removed features: + - Now that all versions before 0.2.2.x are disallowed, we no longer + need to work around their missing features. Thus we can remove a + bunch of compatibility code. + + o Code refactoring: + - Tweak tor-fw-helper to accept an arbitrary amount of arbitrary + TCP ports to forward. In the past it only accepted two ports: + the ORPort and the DirPort. + + +Changes in version 0.2.4.1-alpha - 2012-09-05 + Tor 0.2.4.1-alpha lets bridges publish their pluggable transports to + bridgedb; lets relays use IPv6 addresses and directory authorities + advertise them; and switches to a cleaner build interface. + + This is the first alpha release in a new series, so expect there to + be bugs. Users who would rather test out a more stable branch should + stay with 0.2.3.x for now. + + o Major features (bridges): + - Bridges now report the pluggable transports they support to the + bridge authority, so it can pass the supported transports on to + bridgedb and/or eventually do reachability testing. Implements + ticket 3589. + + o Major features (IPv6): + - Bridge authorities now accept IPv6 bridge addresses and include + them in network status documents. Implements ticket 5534. + - Clients who set "ClientUseIPv6 1" may connect to entry nodes over + IPv6. Set "ClientPreferIPv6ORPort 1" to make this even more likely + to happen. Implements ticket 5535. + - All kind of relays, not just bridges, can now advertise an IPv6 + OR port. Implements ticket 6362. + - Directory authorities vote on IPv6 OR ports using the new consensus + method 14. Implements ticket 6363. + + o Major features (build): + - Switch to a nonrecursive Makefile structure. Now instead of each + Makefile.am invoking other Makefile.am's, there is a master + Makefile.am that includes the others. This change makes our build + process slightly more maintainable, and improves parallelism for + building with make -j. Original patch by Stewart Smith; various + fixes by Jim Meyering. + - Where available, we now use automake's "silent" make rules by + default, so that warnings are easier to spot. You can get the old + behavior with "make V=1". Patch by Stewart Smith for ticket 6522. + + o Minor features (code security and spec conformance): + - Clear keys and key-derived material left on the stack in + rendservice.c and rendclient.c. Check return value of + crypto_pk_write_private_key_to_string() in end_service_load_keys(). + These fixes should make us more forward-secure against cold-boot + attacks and the like. Fixes bug 2385. + - Reject EXTEND cells sent to nonexistent streams. According to the + spec, an EXTEND cell sent to _any_ nonzero stream ID is invalid, but + we were only checking for stream IDs that were currently in use. + Found while hunting for more instances of bug 6271. Bugfix on + 0.0.2pre8, which introduced incremental circuit construction. + + o Minor features (streamlining); + - No longer include the "opt" prefix when generating routerinfos + or v2 directories: it has been needless since Tor 0.1.2. Closes + ticket 5124. + - Remove some now-needless code that tried to aggressively flush + OR connections as data was added to them. Since 0.2.0.1-alpha, our + cell queue logic has saved us from the failure mode that this code + was supposed to prevent. Removing this code will limit the number + of baroque control flow paths through Tor's network logic. Reported + pseudonymously on IRC. Fixes bug 6468; bugfix on 0.2.0.1-alpha. + + o Minor features (controller): + - Add a "GETINFO signal/names" control port command. Implements + ticket 3842. + - Provide default values for all options via "GETINFO config/defaults". + Implements ticket 4971. + + o Minor features (IPv6): + - New config option "AuthDirHasIPv6Connectivity 1" that directory + authorities should set if they have IPv6 connectivity and want to + do reachability tests for IPv6 relays. Implements feature 5974. + - A relay with an IPv6 OR port now sends that address in NETINFO + cells (in addition to its other address). Implements ticket 6364. + + o Minor features (log messages): + - Omit the first heartbeat log message, because it never has anything + useful to say, and it clutters up the bootstrapping messages. + Resolves ticket 6758. + - Don't log about reloading the microdescriptor cache at startup. Our + bootstrap warnings are supposed to tell the user when there's a + problem, and our bootstrap notices say when there isn't. Resolves + ticket 6759; bugfix on 0.2.2.6-alpha. + - Don't log "I learned some more directory information" when we're + reading cached directory information. Reserve it for when new + directory information arrives in response to a fetch. Resolves + ticket 6760. + - Prevent rounding error in path bias counts when scaling + them down, and use the correct scale factor default. Also demote + some path bias related log messages down a level and make others + less scary sounding. Fixes bug 6647. Bugfix against 0.2.3.17-beta. + - We no longer warn so much when generating manpages from their + asciidoc source. + + o Code simplifications and refactoring: + - Enhance our internal sscanf replacement so that we can eliminate + the last remaining uses of the system sscanf. (Though those uses + of sscanf were safe, sscanf itself is generally error prone, so + we want to eliminate when we can.) Fixes ticket 4195 and Coverity + CID 448. + - Move ipv6_preferred from routerinfo_t to node_t. Addresses bug 4620. + - Move last_reachable and testing_since from routerinfo_t to node_t. + Implements ticket 5529. + - Add replaycache_t structure, functions and unit tests, then refactor + rend_service_introduce() to be more clear to read, improve, debug, + and test. Resolves bug 6177. + - Finally remove support for malloc_good_size and malloc_usable_size. + We had hoped that these functions would let us eke a little more + memory out of our malloc implementation. Unfortunately, the only + implementations that provided these functions are also ones that + are already efficient about not overallocation: they never got us + more than 7 or so bytes per allocation. Removing them saves us a + little code complexity and a nontrivial amount of build complexity. + + o New requirements: + - Tor maintainers now require Automake version 1.9 or later to build + Tor from the Git repository. (Automake is not required when building + from a source distribution.) + + +Changes in version 0.2.3.21-rc - 2012-09-05 + Tor 0.2.3.21-rc is the fourth release candidate for the Tor 0.2.3.x + series. It fixes a trio of potential security bugs, fixes a bug where + we were leaving some of the fast relays out of the microdescriptor + consensus, resumes interpreting "ORPort 0" and "DirPort 0" correctly, + and cleans up other smaller issues. + + o Major bugfixes (security): + - Tear down the circuit if we get an unexpected SENDME cell. Clients + could use this trick to make their circuits receive cells faster + than our flow control would have allowed, or to gum up the network, + or possibly to do targeted memory denial-of-service attacks on + entry nodes. Fixes bug 6252. Bugfix on the 54th commit on Tor -- + from July 2002, before the release of Tor 0.0.0. We had committed + this patch previously, but we had to revert it because of bug 6271. + Now that 6271 is fixed, this patch appears to work. + - Reject any attempt to extend to an internal address. Without + this fix, a router could be used to probe addresses on an internal + network to see whether they were accepting connections. Fixes bug + 6710; bugfix on 0.0.8pre1. + - Do not crash when comparing an address with port value 0 to an + address policy. This bug could have been used to cause a remote + assertion failure by or against directory authorities, or to + allow some applications to crash clients. Fixes bug 6690; bugfix + on 0.2.1.10-alpha. + + o Major bugfixes: + - Remove the upper bound on microdescriptor length. We were hitting + the limit for routers with complex exit policies or family + declarations, causing clients to not use them. Fixes the first + piece of bug 6404; fix on 0.2.2.6-alpha. + - Detect "ORPort 0" as meaning, uniformly, that we're not running + as a relay. Previously, some of our code would treat the presence + of any ORPort line as meaning that we should act like a relay, + even though our new listener code would correctly not open any + ORPorts for ORPort 0. Similar bugs in other Port options are also + fixed. Fixes the first half of bug 6507; bugfix on 0.2.3.3-alpha. + + o Minor bugfixes: + - Avoid a pair of double-free and use-after-mark bugs that can + occur with certain timings in canceled and re-received DNS + requests. Fixes bug 6472; bugfix on 0.0.7rc1. + - Fix build and 64-bit compile warnings from --enable-openbsd-malloc. + Fixes bug 6379. Bugfix on 0.2.0.20-rc. + - Allow one-hop directory fetching circuits the full "circuit build + timeout" period, rather than just half of it, before failing them + and marking the relay down. This fix should help reduce cases where + clients declare relays (or worse, bridges) unreachable because + the TLS handshake takes a few seconds to complete. Fixes bug 6743; + bugfix on 0.2.2.2-alpha, where we changed the timeout from a static + 30 seconds. + - Authorities no longer include any router in their microdescriptor + consensuses for which they couldn't generate or agree on a + microdescriptor. Fixes the second piece of bug 6404; fix on + 0.2.2.6-alpha. + - Detect and reject attempts to specify both "FooPort" and + "FooPort 0" in the same configuration domain. (It's still okay + to have a FooPort in your configuration file, and use "FooPort 0" + on the command line to disable it.) Fixes the second half of bug + 6507; bugfix on 0.2.3.3-alpha. + - Make wildcarded addresses (that is, ones beginning with "*.") work + when provided via the controller's MapAddress command. Previously, + they were accepted, but we never actually noticed that they were + wildcards. Fixes bug 6244; bugfix on 0.2.3.9-alpha. + - Avoid crashing on a malformed state file where EntryGuardPathBias + precedes EntryGuard. Fix for bug 6774; bugfix on 0.2.3.17-beta. + - Add a (probably redundant) memory clear between iterations of + the router status voting loop, to prevent future coding errors + where data might leak between iterations of the loop. Resolves + ticket 6514. + + o Minor bugfixes (log messages): + - Downgrade "set buildtimeout to low value" messages to "info" + severity; they were never an actual problem, there was never + anything reasonable to do about them, and they tended to spam logs + from time to time. Fixes bug 6251; bugfix on 0.2.2.2-alpha. + - Downgrade path-bias warning messages to "info". We'll try to get + them working better in 0.2.4. Add internal circuit construction + state to protect against the noisy warn message "Unexpectedly high + circuit_successes". Also add some additional rate-limited notice + messages to help determine the root cause of the warn. Fixes bug + 6475. Bugfix against 0.2.3.17-beta. + - Move log message when unable to find a microdesc in a routerstatus + entry to parse time. Previously we'd spam this warning every time + we tried to figure out which microdescriptors to download. Fixes + the third piece of bug 6404; fix on 0.2.3.18-rc. + + o Minor features: + - Consider new, removed or changed IPv6 OR ports a non-cosmetic + change when the authority is deciding whether to accept a newly + uploaded descriptor. Implements ticket 6423. + - Add missing documentation for consensus and microdesc files. + Resolves ticket 6732. + + +Changes in version 0.2.2.38 - 2012-08-12 + Tor 0.2.2.38 fixes a remotely triggerable crash bug, and fixes a timing + attack that could in theory leak path information. + + o Security fixes: + - Avoid an uninitialized memory read when reading a vote or consensus + document that has an unrecognized flavor name. This read could + lead to a remote crash bug. Fixes bug 6530; bugfix on 0.2.2.6-alpha. + - Try to leak less information about what relays a client is + choosing to a side-channel attacker. Previously, a Tor client would + stop iterating through the list of available relays as soon as it + had chosen one, thus finishing a little earlier when it picked + a router earlier in the list. If an attacker can recover this + timing information (nontrivial but not proven to be impossible), + they could learn some coarse-grained information about which relays + a client was picking (middle nodes in particular are likelier to + be affected than exits). The timing attack might be mitigated by + other factors (see bug 6537 for some discussion), but it's best + not to take chances. Fixes bug 6537; bugfix on 0.0.8rc1. + + +Changes in version 0.2.3.20-rc - 2012-08-05 + Tor 0.2.3.20-rc is the third release candidate for the Tor 0.2.3.x + series. It fixes a pair of code security bugs and a potential anonymity + issue, updates our RPM spec files, and cleans up other smaller issues. + + o Security fixes: + - Avoid read-from-freed-memory and double-free bugs that could occur + when a DNS request fails while launching it. Fixes bug 6480; + bugfix on 0.2.0.1-alpha. + - Avoid an uninitialized memory read when reading a vote or consensus + document that has an unrecognized flavor name. This read could + lead to a remote crash bug. Fixes bug 6530; bugfix on 0.2.2.6-alpha. + - Try to leak less information about what relays a client is + choosing to a side-channel attacker. Previously, a Tor client would + stop iterating through the list of available relays as soon as it + had chosen one, thus finishing a little earlier when it picked + a router earlier in the list. If an attacker can recover this + timing information (nontrivial but not proven to be impossible), + they could learn some coarse-grained information about which relays + a client was picking (middle nodes in particular are likelier to + be affected than exits). The timing attack might be mitigated by + other factors (see bug 6537 for some discussion), but it's best + not to take chances. Fixes bug 6537; bugfix on 0.0.8rc1. + + o Minor features: + - Try to make the warning when giving an obsolete SOCKSListenAddress + a little more useful. + - Terminate active server managed proxies if Tor stops being a + relay. Addresses parts of bug 6274; bugfix on 0.2.3.6-alpha. + - Provide a better error message about possible OSX Asciidoc failure + reasons. Fixes bug 6436. + - Warn when Tor is configured to use accounting in a way that can + link a hidden service to some other hidden service or public + address. Resolves ticket 6490. + + o Minor bugfixes: + - Check return value of fputs() when writing authority certificate + file. Fixes Coverity issue 709056; bugfix on 0.2.0.1-alpha. + - Ignore ServerTransportPlugin lines when Tor is not configured as + a relay. Fixes bug 6274; bugfix on 0.2.3.6-alpha. + - When disabling guards for having too high a proportion of failed + circuits, make sure to look at each guard. Fixes bug 6397; bugfix + on 0.2.3.17-beta. + + o Packaging (RPM): + - Update our default RPM spec files to work with mock and rpmbuild + on RHEL/Fedora. They have an updated set of dependencies and + conflicts, a fix for an ancient typo when creating the "_tor" + user, and better instructions. Thanks to Ondrej Mikle for the + patch series. Fixes bug 6043. + + o Testing: + - Make it possible to set the TestingTorNetwork configuration + option using AlternateDirAuthority and AlternateBridgeAuthority + as an alternative to setting DirServer. Addresses ticket 6377. + + o Documentation: + - Clarify the documentation for the Alternate*Authority options. + Fixes bug 6387. + - Fix some typos in the manpages. Patch from A. Costa. Fixes bug 6500. + + o Code simplification and refactoring: + - Do not use SMARTLIST_FOREACH for any loop whose body exceeds + 10 lines. Also, don't nest them. Doing so in the past has + led to hard-to-debug code. The new style is to use the + SMARTLIST_FOREACH_{BEGIN,END} pair. Addresses issue 6400. + + +Changes in version 0.2.3.19-rc - 2012-07-06 + Tor 0.2.3.19-rc is the second release candidate for the Tor 0.2.3.x + series. It fixes the compile on Windows, reverts to a GeoIP database + that isn't as broken, and fixes a flow control bug that has been around + since the beginning of Tor. + + o Major bugfixes: + - Fix a bug handling SENDME cells on nonexistent streams that could + result in bizarre window values. Report and patch contributed + pseudonymously. Fixes part of bug 6271. This bug was introduced + before the first Tor release, in svn commit r152. + - Revert to the May 1 2012 Maxmind GeoLite Country database. In the + June 2012 database, Maxmind marked many Tor relays as country "A1", + which will cause risky behavior for clients that set EntryNodes + or ExitNodes. Addresses bug 6334; bugfix on 0.2.3.17-beta. + - Instead of ENOBUFS on Windows, say WSAENOBUFS. Fixes compilation + on Windows. Fixes bug 6296; bugfix on 0.2.3.18-rc. + + o Minor bugfixes: + - Fix wrong TCP port range in parse_port_range(). Fixes bug 6218; + bugfix on 0.2.1.10-alpha. + + Changes in version 0.2.3.18-rc - 2012-06-28 Tor 0.2.3.18-rc is the first release candidate for the Tor 0.2.3.x series. It fixes a few smaller bugs, but generally appears stable. @@ -225,7 +640,7 @@ Changes in version 0.2.3.16-alpha - 2012-06-05 indefinitely. Fixes bug 5380; bugfix on 0.2.1.14-rc. - When fetching a bridge descriptor from a bridge authority, always do so anonymously, whether we have been able to open - circuits or not. Partial fix for bug 1938; bugfix on 2.0.7-alpha. + circuits or not. Partial fix for bug 1938; bugfix on 0.2.0.7-alpha. This behavior makes it *safer* to use UpdateBridgesFromAuthority, but we'll need to wait for bug 6010 before it's actually usable. @@ -955,7 +1370,8 @@ Changes in version 0.2.3.11-alpha - 2012-01-22 FastFlagMaxThreshold) to control the range of allowable bandwidths for the Fast directory flag. These allow authorities to run experiments on appropriate requirements for being a "Fast" node. - The AuthDirFastGuarantee config value still applies. + The AuthDirFastGuarantee config value still applies. Implements + ticket 3946. - Document the GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays directory authority option (introduced in Tor 0.2.2.34). @@ -1270,6 +1686,29 @@ Changes in version 0.2.2.35 - 2011-12-16 by removing an absolute path from makensis.exe command. +Changes in version 0.2.1.32 - 2011-12-16 + Tor 0.2.1.32 backports important security and privacy fixes for + oldstable. This release is intended only for package maintainers and + others who cannot use the 0.2.2 stable series. All others should be + using Tor 0.2.2.x or newer. + + The Tor 0.2.1.x series will reach formal end-of-life some time in + early 2012; we will stop releasing patches for it then. + + o Major bugfixes (also included in 0.2.2.x): + - Correctly sanity-check that we don't underflow on a memory + allocation (and then assert) for hidden service introduction + point decryption. Bug discovered by Dan Rosenberg. Fixes bug 4410; + bugfix on 0.2.1.5-alpha. + - Fix a heap overflow bug that could occur when trying to pull + data into the first chunk of a buffer, when that chunk had + already had some data drained from it. Fixes CVE-2011-2778; + bugfix on 0.2.0.16-alpha. Reported by "Vektor". + + o Minor features: + - Update to the December 6 2011 Maxmind GeoLite Country database. + + Changes in version 0.2.3.9-alpha - 2011-12-08 Tor 0.2.3.9-alpha introduces initial IPv6 support for bridges, adds a "DisableNetwork" security feature that bundles can use to avoid diff --git a/Makefile.am b/Makefile.am index 29bba715bd..787be4951c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,22 +4,31 @@ # See LICENSE for licensing information # "foreign" means we don't follow GNU package layout standards -# 1.7 means we require automake vesion 1.7 -AUTOMAKE_OPTIONS = foreign 1.7 +# 1.9 means we require automake vesion 1.9 +AUTOMAKE_OPTIONS = foreign 1.9 subdir-objects -SUBDIRS = src doc contrib +noinst_LIBRARIES= +EXTRA_DIST= +noinst_HEADERS= +bin_PROGRAMS= +CLEANFILES= +TESTS= +noinst_PROGRAMS= +DISTCLEANFILES= +bin_SCRIPTS= +AM_CPPFLAGS= +include src/include.am +include doc/include.am +include contrib/include.am -DIST_SUBDIRS = src doc contrib -EXTRA_DIST = \ +EXTRA_DIST+= \ ChangeLog \ INSTALL \ LICENSE \ Makefile.nmake \ README \ - ReleaseNotes \ - tor.spec \ - tor.spec.in + ReleaseNotes #install-data-local: # $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor diff --git a/ReleaseNotes b/ReleaseNotes index 93e38faf3f..9e02374bac 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -3,6 +3,42 @@ This document summarizes new features and bugfixes in each stable release of Tor. If you want to see more detailed descriptions of the changes in each development snapshot, see the ChangeLog file. +Changes in version 0.2.2.39 - 2012-09-11 + Tor 0.2.2.39 fixes two more opportunities for remotely triggerable + assertions. + + o Security fixes: + - Fix an assertion failure in tor_timegm() that could be triggered + by a badly formatted directory object. Bug found by fuzzing with + Radamsa. Fixes bug 6811; bugfix on 0.2.0.20-rc. + - Do not crash when comparing an address with port value 0 to an + address policy. This bug could have been used to cause a remote + assertion failure by or against directory authorities, or to + allow some applications to crash clients. Fixes bug 6690; bugfix + on 0.2.1.10-alpha. + + +Changes in version 0.2.2.38 - 2012-08-12 + Tor 0.2.2.38 fixes a remotely triggerable crash bug, and fixes a timing + attack that could in theory leak path information. + + o Security fixes: + - Avoid an uninitialized memory read when reading a vote or consensus + document that has an unrecognized flavor name. This read could + lead to a remote crash bug. Fixes bug 6530; bugfix on 0.2.2.6-alpha. + - Try to leak less information about what relays a client is + choosing to a side-channel attacker. Previously, a Tor client would + stop iterating through the list of available relays as soon as it + had chosen one, thus finishing a little earlier when it picked + a router earlier in the list. If an attacker can recover this + timing information (nontrivial but not proven to be impossible), + they could learn some coarse-grained information about which relays + a client was picking (middle nodes in particular are likelier to + be affected than exits). The timing attack might be mitigated by + other factors (see bug 6537 for some discussion), but it's best + not to take chances. Fixes bug 6537; bugfix on 0.0.8rc1. + + Changes in version 0.2.2.37 - 2012-06-06 Tor 0.2.2.37 introduces a workaround for a critical renegotiation bug in OpenSSL 1.0.1 (where 20% of the Tor network can't talk to itself @@ -294,6 +330,29 @@ Changes in version 0.2.2.35 - 2011-12-16 by removing an absolute path from makensis.exe command. +Changes in version 0.2.1.32 - 2011-12-16 + Tor 0.2.1.32 backports important security and privacy fixes for + oldstable. This release is intended only for package maintainers and + others who cannot use the 0.2.2 stable series. All others should be + using Tor 0.2.2.x or newer. + + The Tor 0.2.1.x series will reach formal end-of-life some time in + early 2012; we will stop releasing patches for it then. + + o Major bugfixes (also included in 0.2.2.x): + - Correctly sanity-check that we don't underflow on a memory + allocation (and then assert) for hidden service introduction + point decryption. Bug discovered by Dan Rosenberg. Fixes bug 4410; + bugfix on 0.2.1.5-alpha. + - Fix a heap overflow bug that could occur when trying to pull + data into the first chunk of a buffer, when that chunk had + already had some data drained from it. Fixes CVE-2011-2778; + bugfix on 0.2.0.16-alpha. Reported by "Vektor". + + o Minor features: + - Update to the December 6 2011 Maxmind GeoLite Country database. + + Changes in version 0.2.2.34 - 2011-10-26 Tor 0.2.2.34 fixes a critical anonymity vulnerability where an attacker can deanonymize Tor users. Everybody should upgrade. diff --git a/acinclude.m4 b/acinclude.m4 index 9ab684877a..5ea3097cea 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,4 +1,4 @@ -dnl Helper macros for Tor configure.in +dnl Helper macros for Tor configure.ac dnl Copyright (c) 2001-2004, Roger Dingledine dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson dnl Copyright (c) 2007-2008, Roger Dingledine, Nick Mathewson diff --git a/changes/bug6043 b/changes/bug6043 deleted file mode 100644 index b88bafb788..0000000000 --- a/changes/bug6043 +++ /dev/null @@ -1,6 +0,0 @@ - o Packaging (RPM): - - Our default RPM spec files have been updated to work with mock - and rpmbuild on RHEL/Fedora. They have an updated set of - dependencies and conflicts, a fix for an ancient typo when creating - the "_tor" user, and better instructions. Thanks to Ondrej - Mikle for the patch series; fix for bug 6043. diff --git a/changes/bug6218 b/changes/bug6218 deleted file mode 100644 index 5d5d108b00..0000000000 --- a/changes/bug6218 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes: - - Fix wrong TCP port range in parse_port_range(). Fixes bug 6218; - bugfix on 0.2.1.10-alpha. diff --git a/changes/bug6252_again b/changes/bug6252_again deleted file mode 100644 index f7fd00cb38..0000000000 --- a/changes/bug6252_again +++ /dev/null @@ -1,11 +0,0 @@ - o Security fixes: - - Tear down the circuit if we get an unexpected SENDME cell. Clients - could use this trick to make their circuits receive cells faster - than our flow control would have allowed, or to gum up the network, - or possibly to do targeted memory denial-of-service attacks on - entry nodes. Fixes bug 6252. Bugfix on the 54th commit on Tor -- - from July 2002, before the release of Tor 0.0.0. We had committed - this patch previously, but we had to revert it because of bug 6271. - Now that 6271 is fixed, this appears to work. - - diff --git a/changes/bug6271 b/changes/bug6271 deleted file mode 100644 index 06b129f73f..0000000000 --- a/changes/bug6271 +++ /dev/null @@ -1,7 +0,0 @@ - o Major bugfixes - - - Fix a bug handling SENDME cells on nonexistent streams that - could result in bizarre window values. Report and patch - contributed pseudymously. Fixes part of bug 6271. This bug - was introduced before the first Tor release, in svn commit - r152. diff --git a/changes/bug6274 b/changes/bug6274 deleted file mode 100644 index ad1abcde54..0000000000 --- a/changes/bug6274 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes: - - Ignore ServerTransportPlugin lines when Tor is not configured as - a relay. Fixes bug 6274; bugfix on 0.2.3.6-alpha. diff --git a/changes/bug6274_2 b/changes/bug6274_2 deleted file mode 100644 index 89576f9328..0000000000 --- a/changes/bug6274_2 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features: - - Terminate active server managed proxies if Tor stops being a - relay. Addresses parts of bug 6274; bugfix on 0.2.3.6-alpha. diff --git a/changes/bug6296 b/changes/bug6296 deleted file mode 100644 index b452b1745d..0000000000 --- a/changes/bug6296 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes: - - - Instead of ENOBUFS on Windows, say WSAENOBUFS. Fixes - compilation on Windows. Fixes bug 6296; bugfix on 0.2.3.18-rc. diff --git a/changes/bug6377 b/changes/bug6377 deleted file mode 100644 index a3a3672783..0000000000 --- a/changes/bug6377 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Make it possible to set the TestingTorNetwork configuration - option using AlternateDirAuthority and AlternateBridgeAuthority - as an alternative to setting DirServer. diff --git a/changes/bug6387 b/changes/bug6387 deleted file mode 100644 index 73fc4f7cfe..0000000000 --- a/changes/bug6387 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation: - - Clarify the documentation for the Alternate*Authority options. - Fixes bug 6387. diff --git a/changes/bug6397 b/changes/bug6397 deleted file mode 100644 index 23d8359bd2..0000000000 --- a/changes/bug6397 +++ /dev/null @@ -1,4 +0,0 @@ - o Major bugfixes: - - When disabling guards for having too high a proportion of failed - circuits, make sure to look at each guard. Fix for bug 6397; bugfix - on 0.2.3.17-beta. diff --git a/changes/bug6436 b/changes/bug6436 deleted file mode 100644 index 2c163df105..0000000000 --- a/changes/bug6436 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features: - - Provide a better error message about possible OSX Asciidoc failure - reasons. Fix for bug 6436. diff --git a/changes/bug6480 b/changes/bug6480 deleted file mode 100644 index 83ae00b251..0000000000 --- a/changes/bug6480 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes: - - Avoid read-from-freed-RAM bug and related double-free bug that - could occur when a DNS request fails while launching it. Fixes - bug 6480; bugfix on 0.2.0.1-alpha. - diff --git a/changes/bug6490 b/changes/bug6490 deleted file mode 100644 index c92daad8f4..0000000000 --- a/changes/bug6490 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features: - - Warn when Tor is configured to use accounting in a way that will - link a hidden service to some other hidden service or public - address. Fix for bug 6490. diff --git a/changes/bug6500 b/changes/bug6500 deleted file mode 100644 index cac2054a3a..0000000000 --- a/changes/bug6500 +++ /dev/null @@ -1,2 +0,0 @@ - o Minor bugfixes: - - Fix some typos in the manpages. Patch from A. Costa. Fixes bug 6500. diff --git a/changes/bug6530 b/changes/bug6530 deleted file mode 100644 index 825bbb752a..0000000000 --- a/changes/bug6530 +++ /dev/null @@ -1,5 +0,0 @@ - o Major security fixes: - - Avoid a read of uninitializd RAM when reading a vote or consensus - document with an unrecognized flavor name. This could lead to a - remote crash bug. Fixes bug 6530; bugfix on 0.2.2.6-alpha. - diff --git a/changes/bug6811 b/changes/bug6811 new file mode 100644 index 0000000000..841ec1c54a --- /dev/null +++ b/changes/bug6811 @@ -0,0 +1,5 @@ + o Major security fixes: + - Fix an assertion failure in tor_timegm that could be triggered + by a badly formatted directory object. Bug found by fuzzing with + Radamsa. Fixes bug 6811; bugfix on 0.2.0.20-rc. + diff --git a/changes/cov709056 b/changes/cov709056 deleted file mode 100644 index 64a75ad8a2..0000000000 --- a/changes/cov709056 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes: - - Check return value of fputs() when writing authority certificate - file. Fixes Coverity issue 709056; bugfix on 0.2.0.1-alpha. - diff --git a/changes/pathsel-BUGGY-a b/changes/pathsel-BUGGY-a deleted file mode 100644 index 2e642c7953..0000000000 --- a/changes/pathsel-BUGGY-a +++ /dev/null @@ -1,14 +0,0 @@ - o Security fixes: - - - Try to leak less information about what relays a client is - choosing to a side-channel attacker. Previously, a Tor client - would stop iterating through the list of available relays as - soon as it had chosen one, thus finishing a little earlier - when it picked a router earlier in the list. If an attacker - can recover this timing information (nontrivial but not - proven to be impossible), they could learn some coarse- - grained information about which relays a client was picking - (middle nodes in particular are likelier to be affected than - exits). The timing attack might be mitigated by other factors - (see bug #6537 for some discussion), but it's best not to - take chances. Fixes bug 6537; bugfix on 0.0.8rc1. diff --git a/changes/port_doc b/changes/port_doc deleted file mode 100644 index 0e8662f0ab..0000000000 --- a/changes/port_doc +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (usability): - - Try to make the warning when giving an obsolete SOCKSListenAddress - a littel more useful. diff --git a/changes/revert-geoip-may2012 b/changes/revert-geoip-may2012 deleted file mode 100644 index e420947a34..0000000000 --- a/changes/revert-geoip-may2012 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes: - - Revert to the May 1 2012 Maxmind GeoLite Country database. In the - June 2012 database, Maxmind marked many Tor relays as country "A1", - which will cause risky behavior for clients that set EntryNodes - or ExitNodes. Addresses bug 6334; bugfix on 0.2.3.17-beta. - diff --git a/changes/smartlist_foreach b/changes/smartlist_foreach deleted file mode 100644 index 2fd3a1a85c..0000000000 --- a/changes/smartlist_foreach +++ /dev/null @@ -1,8 +0,0 @@ - o Code simplification and refactoring: - - Do not use SMARTLIST_FOREACH for any loop whose body exceeds - 10 lines. Doing so in the past has led to hard-to-debug code. - The new style is to use the SMARTLIST_FOREACH_{BEGIN,END} pair. - Issue 6400. - - Do not nest SMARTLIST_FOREACH blocks within one another. Any - nested block ought to be using SMARTLIST_FOREACH_{BEGIN,END}. - Issue 6400. diff --git a/configure.in b/configure.ac index f5f8eacb7e..7f33828669 100644 --- a/configure.in +++ b/configure.ac @@ -3,9 +3,11 @@ dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson dnl Copyright (c) 2007-2012, The Tor Project, Inc. dnl See LICENSE for licensing information -AC_INIT -AM_INIT_AUTOMAKE(tor, 0.2.3.18-rc-dev) -AM_CONFIG_HEADER(orconfig.h) +AC_INIT([tor],[0.2.4.2-alpha-dev]) +AC_CONFIG_SRCDIR([src/or/main.c]) +AM_INIT_AUTOMAKE +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_CONFIG_HEADERS([orconfig.h]) AC_CANONICAL_HOST @@ -300,6 +302,7 @@ AC_CHECK_FUNCS( gmtime_r \ inet_aton \ ioctl \ + issetugid \ llround \ localtime_r \ lround \ @@ -317,18 +320,6 @@ AC_CHECK_FUNCS( vasprintf \ ) -using_custom_malloc=no -if test x$enable_openbsd_malloc = xyes ; then - AC_DEFINE(HAVE_MALLOC_GOOD_SIZE, 1, [Defined if we have the malloc_good_size function]) - using_custom_malloc=yes -fi -if test x$tcmalloc = xyes ; then - using_custom_malloc=yes -fi -if test $using_custom_malloc = no ; then - AC_CHECK_FUNCS(mallinfo malloc_good_size malloc_usable_size) -fi - if test "$enable_threads" = "yes"; then AC_CHECK_HEADERS(pthread.h) AC_CHECK_FUNCS(pthread_create) @@ -703,14 +694,6 @@ AC_CHECK_HEADERS( AC_CHECK_HEADERS(sys/param.h) -TOR_CHECK_PROTOTYPE(malloc_good_size, HAVE_MALLOC_GOOD_SIZE_PROTOTYPE, -[#ifdef HAVE_MALLOC_H -#include <malloc.h> -#endif -#ifdef HAVE_MALLOC_MALLOC_H -#include <malloc/malloc.h> -#endif]) - AC_CHECK_HEADERS(net/if.h, net_if_found=1, net_if_found=0, [#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> @@ -1032,6 +1015,17 @@ if test x$tcmalloc = xyes ; then LDFLAGS="-ltcmalloc $LDFLAGS" fi +using_custom_malloc=no +if test x$enable_openbsd_malloc = xyes ; then + using_custom_malloc=yes +fi +if test x$tcmalloc = xyes ; then + using_custom_malloc=yes +fi +if test $using_custom_malloc = no ; then + AC_CHECK_FUNCS(mallinfo) +fi + # By default, we're going to assume we don't have mlockall() # bionic and other platforms have various broken mlockall subsystems. # Some systems don't have a working mlockall, some aren't linkable, @@ -1317,24 +1311,12 @@ CPPFLAGS="$CPPFLAGS $TOR_CPPFLAGS_libevent $TOR_CPPFLAGS_openssl $TOR_CPPFLAGS_z AC_CONFIG_FILES([ Doxyfile Makefile - contrib/Makefile - contrib/suse/Makefile contrib/suse/tor.sh contrib/tor.logrotate contrib/tor.sh contrib/torctl contrib/torify - doc/Makefile - src/Makefile - src/common/Makefile - src/config/Makefile src/config/torrc.sample - src/or/Makefile - src/test/Makefile - src/tools/Makefile - src/tools/tor-fw-helper/Makefile - src/win32/Makefile - tor.spec ]) AC_OUTPUT @@ -1342,4 +1324,3 @@ AC_OUTPUT if test -x /usr/bin/perl && test -x ./contrib/updateVersions.pl ; then ./contrib/updateVersions.pl fi - diff --git a/contrib/Makefile.am b/contrib/Makefile.am deleted file mode 100644 index 795c351f3a..0000000000 --- a/contrib/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -SUBDIRS = suse -DIST_SUBDIRS = suse - -confdir = $(sysconfdir)/tor - -EXTRA_DIST = \ - cross.sh \ - exitlist \ - linux-tor-prio.sh \ - package_nsis-mingw.sh \ - rc.subr \ - tor-ctrl.sh \ - tor-exit-notice.html \ - tor-mingw.nsi.in \ - tor-tsocks.conf \ - tor.ico \ - tor.nsi.in \ - tor.sh \ - torctl - -conf_DATA = tor-tsocks.conf - -bin_SCRIPTS = torify diff --git a/contrib/findMergedChanges.pl b/contrib/findMergedChanges.pl index 46e070f943..e4ff6163e7 100755 --- a/contrib/findMergedChanges.pl +++ b/contrib/findMergedChanges.pl @@ -19,7 +19,7 @@ my $look_for_type = "merged"; if (! @ARGV) { print <<EOF Usage: - findMergedChanges.pl [--merged/--unmerged/--weird/--list] changes/* + findMergedChanges.pl [--merged/--unmerged/--weird/--list] [--branch=<branchname] changes/* A change is "merged" if it has ever been merged to release-0.2.2 and it has had no subsequent changes in master. diff --git a/contrib/include.am b/contrib/include.am new file mode 100644 index 0000000000..4a995a37d2 --- /dev/null +++ b/contrib/include.am @@ -0,0 +1,20 @@ +include contrib/suse/include.am + +EXTRA_DIST+= \ + contrib/cross.sh \ + contrib/exitlist \ + contrib/linux-tor-prio.sh \ + contrib/package_nsis-mingw.sh \ + contrib/rc.subr \ + contrib/tor-ctrl.sh \ + contrib/tor-exit-notice.html \ + contrib/tor-mingw.nsi.in \ + contrib/tor-tsocks.conf \ + contrib/tor.ico \ + contrib/tor.nsi.in \ + contrib/tor.sh \ + contrib/torctl + +conf_DATA+= contrib/tor-tsocks.conf + +bin_SCRIPTS+= contrib/torify diff --git a/contrib/suse/Makefile.am b/contrib/suse/Makefile.am deleted file mode 100644 index 06511c0425..0000000000 --- a/contrib/suse/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -confdir = $(sysconfdir)/tor - -EXTRA_DIST = tor.sh diff --git a/contrib/suse/include.am b/contrib/suse/include.am new file mode 100644 index 0000000000..4aed0e123e --- /dev/null +++ b/contrib/suse/include.am @@ -0,0 +1 @@ +EXTRA_DIST+= contrib/suse/tor.sh diff --git a/contrib/tor-mingw.nsi.in b/contrib/tor-mingw.nsi.in index 2133a471b3..96e27b4630 100644 --- a/contrib/tor-mingw.nsi.in +++ b/contrib/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.2.3.18-rc-dev" +!define VERSION "0.2.4.2-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/contrib/updateVersions.pl b/contrib/updateVersions.pl index 76b6fe5677..710d7d9359 100755 --- a/contrib/updateVersions.pl +++ b/contrib/updateVersions.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -$CONFIGURE_IN = './configure.in'; +$CONFIGURE_IN = './configure.ac'; $ORCONFIG_H = './src/win32/orconfig.h'; $TOR_NSI = './contrib/tor-mingw.nsi.in'; @@ -13,12 +13,12 @@ demand($CONFIGURE_IN); demand($ORCONFIG_H); demand($TOR_NSI); -# extract version from configure.in +# extract version from configure.ac open(F, $CONFIGURE_IN) or die "$!"; $version = undef; while (<F>) { - if (/AM_INIT_AUTOMAKE\(tor,\s*([^\)]*)\)/) { + if (/AC_INIT\(\[tor\],\s*\[([^\]]*)\]\)/) { $version = $1; last; } diff --git a/doc/HACKING b/doc/HACKING index bc409dc0d0..e76b374d30 100644 --- a/doc/HACKING +++ b/doc/HACKING @@ -467,7 +467,7 @@ a stable release, add it to the ReleaseNotes file too. If we're adding to a release-0.2.x branch, manually commit the changelogs to the later git branches too. -4) Bump the version number in configure.in and rebuild. +4) Bump the version number in configure.ac and rebuild. 5) Make dist, put the tarball up somewhere, and tell #tor about it. Wait a while to see if anybody has problems building it. Try to get Sebastian diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 6cdd66d517..0000000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,89 +0,0 @@ -# We use a two-step process to generate documentation from asciidoc files. -# -# First, we use asciidoc/a2x to process the asciidoc files into .1.in and -# .html.in files (see the asciidoc-helper.sh script). These are the same as -# the regular .1 and .html files, except that they still have some autoconf -# variables set in them. -# -# Second, we use config.status to turn .1.in files into .1 files and -# .html.in files into .html files. -# -# We do the steps in this order so that we can ship the .*.in files as -# part of the source distribution, so that people without asciidoc can -# just use the .1 and .html files. - -regular_mans = tor tor-gencert tor-resolve torify -all_mans = $(regular_mans) tor-fw-helper - -if USE_ASCIIDOC -if USE_FW_HELPER -nodist_man_MANS = $(all_mans:=.1) -doc_DATA = $(all_mans:=.html) -else -nodist_man_MANS = $(regular_mans:=.1) -doc_DATA = $(regular_mans:=.html) -endif -html_in = $(all_mans:=.html.in) -man_in = $(all_mans:=.1.in) -txt_in = $(all_mans:=.1.txt) -else -html_in = -man_in = -txt_in = -nodist_man_MANS = -doc_DATA = -endif - -EXTRA_DIST = HACKING asciidoc-helper.sh \ - $(html_in) $(man_in) $(txt_in) \ - tor-rpm-creation.txt \ - tor-win32-mingw-creation.txt spec/README \ - state-contents.txt - -docdir = @docdir@ - -asciidoc_product = $(nodist_man_MANS) $(doc_DATA) - -# Generate the html documentation from asciidoc, but don't do -# machine-specific replacements yet -$(html_in) : - $(top_srcdir)/doc/asciidoc-helper.sh html @ASCIIDOC@ $(top_srcdir)/doc/$@ - -tor.html.in : tor.1.txt -torify.html.in : torify.1.txt -tor-gencert.html.in : tor-gencert.1.txt -tor-resolve.html.in : tor-resolve.1.txt -tor-fw-helper.html.in : tor-fw-helper.1.txt - -# Generate the manpage from asciidoc, but don't do -# machine-specific replacements yet -$(man_in) : - $(top_srcdir)/doc/asciidoc-helper.sh man @A2X@ $(top_srcdir)/doc/$@ - -tor.1.in : tor.1.txt -torify.1.in : torify.1.txt -tor-gencert.1.in : tor-gencert.1.txt -tor-resolve.1.in : tor-resolve.1.txt -tor-fw-helper.1.in : tor-fw-helper.1.txt - -# use ../config.status to swap all machine-specific magic strings -# in the asciidoc with their replacements. -$(asciidoc_product) : - if test -e $(top_srcdir)/doc/$@.in && ! test -e ./$@.in ; then \ - cp $(top_srcdir)/doc/$@.in .; \ - fi - ../config.status --file=$@; - -tor.1 : tor.1.in -torify.1 : torify.1.in -tor-gencert.1 : tor-gencert.1.in -tor-resolve.1 : tor-resolve.1.in -tor-fw-helper.1 : tor-fw-helper.1.in -tor.html : tor.html.in -torify.html : torify.html.in -tor-gencert.html : tor-gencert.html.in -tor-resolve.html : tor-resolve.html.in -tor-fw-helper.html : tor-fw-helper.html.in - -CLEANFILES = $(asciidoc_product) config.log -DISTCLEANFILES = $(html_in) $(man_in) diff --git a/doc/include.am b/doc/include.am new file mode 100644 index 0000000000..bb01038d4c --- /dev/null +++ b/doc/include.am @@ -0,0 +1,69 @@ +# We use a two-step process to generate documentation from asciidoc files. +# +# First, we use asciidoc/a2x to process the asciidoc files into .1.in and +# .html.in files (see the asciidoc-helper.sh script). These are the same as +# the regular .1 and .html files, except that they still have some autoconf +# variables set in them. +# +# Second, we use config.status to turn .1.in files into .1 files and +# .html.in files into .html files. +# +# We do the steps in this order so that we can ship the .*.in files as +# part of the source distribution, so that people without asciidoc can +# just use the .1 and .html files. + +regular_mans = doc/tor doc/tor-gencert doc/tor-resolve doc/torify +all_mans = $(regular_mans) doc/tor-fw-helper + +if USE_ASCIIDOC +if USE_FW_HELPER +nodist_man1_MANS = $(all_mans:=.1) +doc_DATA = $(all_mans:=.html) +else +nodist_man1_MANS = $(regular_mans:=.1) +doc_DATA = $(regular_mans:=.html) +endif +html_in = $(all_mans:=.html.in) +man_in = $(all_mans:=.1.in) +txt_in = $(all_mans:=.1.txt) +else +html_in = +man_in = +txt_in = +nodist_man1_MANS = +doc_DATA = +endif + +EXTRA_DIST+= doc/HACKING doc/asciidoc-helper.sh \ + $(html_in) $(man_in) $(txt_in) \ + doc/tor-rpm-creation.txt \ + doc/tor-win32-mingw-creation.txt doc/spec/README \ + doc/state-contents.txt + +docdir = @docdir@ + +asciidoc_product = $(nodist_man1_MANS) $(doc_DATA) + +# Generate the html documentation from asciidoc, but don't do +# machine-specific replacements yet +$(html_in) : $(txt_in) + $(AM_V_GEN)$(top_srcdir)/doc/asciidoc-helper.sh html @ASCIIDOC@ $(top_srcdir)/$@ + +# Generate the manpage from asciidoc, but don't do +# machine-specific replacements yet +$(man_in) : $(txt_in) + $(AM_V_GEN)$(top_srcdir)/doc/asciidoc-helper.sh man @A2X@ $(top_srcdir)/$@ + +# use ../config.status to swap all machine-specific magic strings +# in the asciidoc with their replacements. +$(asciidoc_product) : $(txt_in) $(man_in) + $(AM_V_GEN)$(MKDIR_P) $(@D) + $(AM_V_at)if test -e $(top_srcdir)/$@.in && ! test -e $@.in ; then \ + cp $(top_srcdir)/$@.in $@; \ + fi + $(AM_V_at)./config.status -q --file=$@; + +$(doc_DATA) : $(html_in) + +CLEANFILES+= $(asciidoc_product) config.log +DISTCLEANFILES+= $(html_in) $(man_in) diff --git a/doc/tor-fw-helper.1.txt b/doc/tor-fw-helper.1.txt index 49b0910380..cf769d9654 100644 --- a/doc/tor-fw-helper.1.txt +++ b/doc/tor-fw-helper.1.txt @@ -2,6 +2,8 @@ // See LICENSE for licensing information // This is an asciidoc file used to generate the manpage/html reference. // Learn asciidoc on http://www.methods.co.nz/asciidoc/userguide.html +:man source: Tor +:man manual: Tor Manual tor-fw-helper(1) ================ Jacob Appelbaum @@ -39,18 +41,8 @@ OPTIONS **-g** or **--fetch-public-ip**:: Fetch the the public ip address for each supported NAT helper method. -**-i** or **--internal-or-port** __port__:: - Inform **tor-fw-helper** of your internal OR port. This is the only - required argument. - -**-e** or **--external-or-port** __port__:: - Inform **tor-fw-helper** of your external OR port. - -**-d** or **--internal-dir-port** __port__:: - Inform **tor-fw-helper** of your internal Dir port. - -**-p** or **--external-dir-port** __port__:: - Inform **tor-fw-helper** of your external Dir port. +**-p** or **--forward-port** __external_port__:__internal_port__:: + Forward external_port to internal_port. BUGS ---- diff --git a/doc/tor-gencert.1.txt b/doc/tor-gencert.1.txt index 2a2d1179c5..aa61ec3ec6 100644 --- a/doc/tor-gencert.1.txt +++ b/doc/tor-gencert.1.txt @@ -2,6 +2,8 @@ // See LICENSE for licensing information // This is an asciidoc file used to generate the manpage/html reference. // Learn asciidoc on http://www.methods.co.nz/asciidoc/userguide.html +:man source: Tor +:man manual: Tor Manual tor-gencert(1) ============== Nick Mathewson diff --git a/doc/tor-resolve.1.txt b/doc/tor-resolve.1.txt index bdc636b08b..341d302244 100644 --- a/doc/tor-resolve.1.txt +++ b/doc/tor-resolve.1.txt @@ -2,6 +2,8 @@ // See LICENSE for licensing information // This is an asciidoc file used to generate the manpage/html reference. // Learn asciidoc on http://www.methods.co.nz/asciidoc/userguide.html +:man source: Tor +:man manual: Tor Manual tor-resolve(1) ============== Peter Palfrader diff --git a/doc/tor.1.txt b/doc/tor.1.txt index e7ba8485c0..cf5945df11 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2,6 +2,8 @@ // See LICENSE for licensing information // This is an asciidoc file used to generate the manpage/html reference. // Learn asciidoc on http://www.methods.co.nz/asciidoc/userguide.html +:man source: Tor +:man manual: Tor Manual TOR(1) ====== @@ -126,12 +128,12 @@ GENERAL OPTIONS A token bucket limits the average incoming bandwidth usage on this node to the specified number of bytes per second, and the average outgoing bandwidth usage to that same value. If you want to run a relay in the - public network, this needs to be _at the very least_ 20 KB (that is, - 20480 bytes). (Default: 5 MB) + public network, this needs to be _at the very least_ 30 KB (that is, + 30720 bytes). (Default: 1 GB) **BandwidthBurst** __N__ **bytes**|**KB**|**MB**|**GB**:: Limit the maximum token bucket size (also known as the burst) to the given - number of bytes in each direction. (Default: 10 MB) + number of bytes in each direction. (Default: 1 GB) **MaxAdvertisedBandwidth** __N__ **bytes**|**KB**|**MB**|**GB**:: If set, we will not advertise more than this amount of bandwidth for our @@ -1169,6 +1171,18 @@ The following options are useful only for clients (that is, if If no defaults are available there, these options default to 20, .70, 0.0, 200, and 4 respectively. +**ClientUseIPv6** **0**|**1**:: + If this option is set to 1, Tor might connect to entry nodes over + IPv6. Note that clients configured with an IPv6 address in a + **Bridge** line will try connecting over IPv6 even if + **ClientUseIPv6** is set to 0. (Default: 0) + +**ClientPreferIPv6ORPort** **0**|**1**:: + If this option is set to 1, Tor prefers an OR port with an IPv6 + address over one with IPv4 if a given entry node has both. Other + things may influence the choice. This option breaks a tie to the + favor of IPv6. (Default: 0) + SERVER OPTIONS -------------- @@ -1470,6 +1484,11 @@ is non-zero): its extra-info documents that it uploads to the directory authorities. (Default: 1) +**ExtendAllowPrivateAddresses** **0**|**1**:: + When this option is enabled, Tor routers allow EXTEND request to + localhost, RFC1918 addresses, and so on. This can create security issues; + you should probably leave it off. (Default: 0) + DIRECTORY SERVER OPTIONS ------------------------ @@ -1728,6 +1747,12 @@ DIRECTORY AUTHORITY SERVER OPTIONS votes on whether to accept relays as hidden service directories. (Default: 1) +**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) + HIDDEN SERVICE OPTIONS ---------------------- @@ -1795,6 +1820,7 @@ The following options are used for running a testing Tor network. ClientRejectInternalAddresses 0 CountPrivateBandwidth 1 ExitPolicyRejectPrivate 0 + ExtendAllowPrivateAddresses 1 V3AuthVotingInterval 5 minutes V3AuthVoteDelay 20 seconds V3AuthDistDelay 20 seconds @@ -1876,7 +1902,10 @@ FILES __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. + identity key fingerprints of the directory authorities. Mostly obsolete. + +__DataDirectory__**/cached-consensus** and/or **cached-microdesc-consensus**:: + The most recent consensus network status document we've downloaded. __DataDirectory__**/cached-descriptors** and **cached-descriptors.new**:: These files hold downloaded router statuses. Some routers may appear more @@ -1885,6 +1914,12 @@ __DataDirectory__**/cached-descriptors** and **cached-descriptors.new**:: 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-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**:: Obsolete versions of cached-descriptors and cached-descriptors.new. When Tor can't find the newer files, it looks here instead. diff --git a/doc/torify.1.txt b/doc/torify.1.txt index 4a4be1250a..22b3fe5370 100644 --- a/doc/torify.1.txt +++ b/doc/torify.1.txt @@ -2,10 +2,10 @@ // See LICENSE for licensing information // This is an asciidoc file used to generate the manpage/html reference. // Learn asciidoc on http://www.methods.co.nz/asciidoc/userguide.html +:man source: Tor +:man manual: Tor Manual torify(1) ========= -Peter Palfrader -Jacob Appelbaum NAME ---- @@ -48,3 +48,7 @@ SEE ALSO -------- **tor**(1), **tor-resolve**(1), **torsocks**(1), **tsocks**(1), **tsocks.conf**(5). + +AUTHORS +------- +Peter Palfrader and Jacob Appelbaum wrote this manual. diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index fa2dd560a6..0000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ - -# leave in dependency order, since common must be built first -SUBDIRS = common or test tools win32 config -DIST_SUBDIRS = common or test tools win32 config - diff --git a/src/common/Makefile.am b/src/common/Makefile.am deleted file mode 100644 index 5e7684259a..0000000000 --- a/src/common/Makefile.am +++ /dev/null @@ -1,67 +0,0 @@ - -noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a - -EXTRA_DIST = common_sha1.i sha256.c Makefile.nmake - -#CFLAGS = -Wall -Wpointer-arith -O2 - -if USE_OPENBSD_MALLOC -libor_extra_source=OpenBSD_malloc_Linux.c -else -libor_extra_source= -endif - -libor_a_SOURCES = \ - address.c \ - compat.c \ - container.c \ - di_ops.c \ - log.c \ - memarea.c \ - mempool.c \ - procmon.c \ - util.c \ - util_codedigest.c \ - $(libor_extra_source) - -libor_crypto_a_SOURCES = \ - aes.c \ - crypto.c \ - torgzip.c \ - tortls.c - -libor_event_a_SOURCES = compat_libevent.c - -noinst_HEADERS = \ - address.h \ - aes.h \ - ciphers.inc \ - compat.h \ - compat_libevent.h \ - container.h \ - crypto.h \ - di_ops.h \ - ht.h \ - memarea.h \ - mempool.h \ - procmon.h \ - strlcat.c \ - strlcpy.c \ - torgzip.h \ - torint.h \ - torlog.h \ - tortls.h \ - util.h - -common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) - if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ - else \ - rm common_sha1.i; \ - touch common_sha1.i; \ - fi - -util_codedigest.o: common_sha1.i -crypto.o: sha256.c diff --git a/src/common/OpenBSD_malloc_Linux.c b/src/common/OpenBSD_malloc_Linux.c index 92ca9c0066..da82729811 100644 --- a/src/common/OpenBSD_malloc_Linux.c +++ b/src/common/OpenBSD_malloc_Linux.c @@ -14,6 +14,10 @@ * ---------------------------------------------------------------------------- */ +/* We use this macro to remove some code that we don't actually want, + * rather than to fix its warnings. */ +#define BUILDING_FOR_TOR + /* * Defining MALLOC_EXTRA_SANITY will enable extra checks which are * related to internal conditions and consistency in malloc.c. This has @@ -79,6 +83,7 @@ static size_t g_alignment = 0; extern int __libc_enable_secure; +#ifndef HAVE_ISSETUGID static int issetugid(void) { if (__libc_enable_secure) return 1; @@ -86,8 +91,10 @@ static int issetugid(void) if (getgid() != getegid()) return 1; return 0; } +#endif #define PGSHIFT 12 +#undef MADV_FREE #define MADV_FREE MADV_DONTNEED #include <pthread.h> static pthread_mutex_t gen_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -443,6 +450,7 @@ extern char *__progname; static void wrterror(const char *p) { +#ifndef BUILDING_FOR_TOR const char *q = " error: "; struct iovec iov[5]; @@ -457,7 +465,9 @@ wrterror(const char *p) iov[4].iov_base = (char*)"\n"; iov[4].iov_len = 1; writev(STDERR_FILENO, iov, 5); - +#else + (void)p; +#endif suicide = 1; #ifdef MALLOC_STATS if (malloc_stats) @@ -471,14 +481,17 @@ wrterror(const char *p) static void wrtwarning(const char *p) { +#ifndef BUILDING_FOR_TOR const char *q = " warning: "; struct iovec iov[5]; +#endif if (malloc_abort) wrterror(p); else if (malloc_silent) return; +#ifndef BUILDING_FOR_TOR iov[0].iov_base = __progname; iov[0].iov_len = strlen(__progname); iov[1].iov_base = (char*)malloc_func; @@ -489,8 +502,11 @@ wrtwarning(const char *p) iov[3].iov_len = strlen(p); iov[4].iov_base = (char*)"\n"; iov[4].iov_len = 1; - - writev(STDERR_FILENO, iov, 5); + + (void) writev(STDERR_FILENO, iov, 5); +#else + (void)p; +#endif } #ifdef MALLOC_STATS @@ -665,7 +681,7 @@ malloc_init(void) for (i = 0; i < 3; i++) { switch (i) { case 0: - j = readlink("/etc/malloc.conf", b, sizeof b - 1); + j = (int) readlink("/etc/malloc.conf", b, sizeof b - 1); if (j <= 0) continue; b[j] = '\0'; @@ -1145,9 +1161,10 @@ malloc_bytes(size_t size) if (size == 0) j = 0; else { + size_t ii; j = 1; - i = size - 1; - while (i >>= 1) + ii = size - 1; + while (ii >>= 1) j++; } @@ -1971,6 +1988,7 @@ calloc(size_t num, size_t size) return(p); } +#ifndef BUILDING_FOR_TOR static int ispowerof2 (size_t a) { size_t b; for (b = 1ULL << (sizeof(size_t)*NBBY - 1); b > 1; b >>= 1) @@ -1978,7 +1996,9 @@ static int ispowerof2 (size_t a) { return 1; return 0; } +#endif +#ifndef BUILDING_FOR_TOR int posix_memalign(void **memptr, size_t alignment, size_t size) { void *r; @@ -2015,18 +2035,20 @@ void *valloc(size_t size) posix_memalign(&r, malloc_pagesize, size); return r; } +#endif size_t malloc_good_size(size_t size) { if (size == 0) { return 1; } else if (size <= malloc_maxsize) { - int i, j; + int j; + size_t ii; /* round up to the nearest power of 2, with same approach * as malloc_bytes() uses. */ j = 1; - i = size - 1; - while (i >>= 1) + ii = size - 1; + while (ii >>= 1) j++; return ((size_t)1) << j; } else { diff --git a/src/common/address.c b/src/common/address.c index e88869f1d8..dffbcaff44 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1697,3 +1697,15 @@ tor_addr_hostname_is_local(const char *name) !strcasecmpend(name, ".local"); } +/** Return a newly allocated tor_addr_port_t with <b>addr</b> and + <b>port</b> filled in. */ +tor_addr_port_t * +tor_addr_port_new(const tor_addr_t *addr, uint16_t port) +{ + tor_addr_port_t *ap = tor_malloc_zero(sizeof(tor_addr_port_t)); + if (addr) + tor_addr_copy(&ap->addr, addr); + ap->port = port; + return ap; +} + diff --git a/src/common/address.h b/src/common/address.h index c6c126862a..7a779d8880 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -40,7 +40,7 @@ typedef struct tor_addr_port_t uint16_t port; } tor_addr_port_t; -#define TOR_ADDR_NULL {AF_UNSPEC, {0}}; +#define TOR_ADDR_NULL {AF_UNSPEC, {0}} static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); @@ -221,5 +221,7 @@ int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; int get_interface_address(int severity, uint32_t *addr); +tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); + #endif diff --git a/src/common/aes.c b/src/common/aes.c index 59d864a3d0..c6a997f473 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -134,8 +134,8 @@ int evaluate_evp_for_aes(int force_val) { (void) force_val; - log_notice(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " - "counter-mode implementation. Using it."); + log_info(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " + "counter-mode implementation. Using it."); return 0; } int @@ -212,11 +212,11 @@ evaluate_evp_for_aes(int force_val) e = ENGINE_get_cipher_engine(NID_aes_128_ecb); if (e) { - log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", + log_info(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", ENGINE_get_name(e)); should_use_EVP = 1; } else { - log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions."); + log_info(LD_CRYPTO, "No AES engine found; using AES_* functions."); should_use_EVP = 0; } #endif @@ -263,12 +263,12 @@ evaluate_ctr_for_aes(void) "not using it."); } else { /* Counter mode is okay */ - log_notice(LD_CRYPTO, "This OpenSSL has a good implementation of counter " + log_info(LD_CRYPTO, "This OpenSSL has a good implementation of counter " "mode; using it."); should_use_openssl_CTR = 1; } #else - log_notice(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " + log_info(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " "counter mode; not using it."); #endif return 0; diff --git a/src/common/compat.c b/src/common/compat.c index ca850a3038..12025b227a 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -18,7 +18,7 @@ /* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, * and get this (and other important stuff!) automatically. Once we do that, * make sure to also change the extern char **environ detection in - * configure.in, because whether that is declared or not depends on whether + * configure.ac, because whether that is declared or not depends on whether * we have _GNU_SOURCE defined! Maybe that means that once we take this out, * we can also take out the configure check. */ #define _GNU_SOURCE diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 6655ca87d3..0d06c49c9f 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -266,7 +266,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) #if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD) /* Making this a NOTICE for now so we can link bugs to a libevent versions * or methods better. */ - log(LOG_NOTICE, LD_GENERAL, + log(LOG_INFO, LD_GENERAL, "Initialized libevent version %s using method %s. Good.", event_get_version(), tor_libevent_get_method()); #else diff --git a/src/common/crypto.c b/src/common/crypto.c index a69e6c5cb8..7768cc37b1 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -221,6 +221,30 @@ try_load_engine(const char *path, const char *engine) } #endif +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 = SSLeay_version(SSLEAY_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) + crypto_openssl_version_str = tor_strndup(raw_version, + end_of_version-raw_version); + else + crypto_openssl_version_str = tor_strdup(raw_version); + } + return crypto_openssl_version_str; +} + /** Initialize the crypto library. Return 0 on success, -1 on failure. */ int @@ -231,6 +255,19 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) OpenSSL_add_all_algorithms(); _crypto_global_initialized = 1; setup_openssl_threading(); + + if (SSLeay() == OPENSSL_VERSION_NUMBER && + !strcmp(SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_TEXT)) { + log_info(LD_CRYPTO, "OpenSSL version matches version from headers " + "(%lx: %s).", SSLeay(), SSLeay_version(SSLEAY_VERSION)); + } else { + log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the " + "version we're running with. If you get weird crashes, that " + "might be why. (Compiled with %lx: %s; running with %lx: %s).", + (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, + SSLeay(), SSLeay_version(SSLEAY_VERSION)); + } + if (useAccel > 0) { #ifdef DISABLE_ENGINES (void)accelName; @@ -3018,6 +3055,7 @@ crypto_global_cleanup(void) tor_free(ms); } #endif + tor_free(crypto_openssl_version_str); return 0; } diff --git a/src/common/crypto.h b/src/common/crypto.h index 76bcbf7d43..456a61173f 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -111,6 +111,7 @@ typedef struct crypto_digest_t crypto_digest_t; typedef struct crypto_dh_t crypto_dh_t; /* global state */ +const char * crypto_openssl_get_version_str(void); int crypto_global_init(int hardwareAccel, const char *accelName, const char *accelPath); diff --git a/src/common/include.am b/src/common/include.am new file mode 100644 index 0000000000..299c92e065 --- /dev/null +++ b/src/common/include.am @@ -0,0 +1,75 @@ + +noinst_LIBRARIES+= src/common/libor.a src/common/libor-crypto.a src/common/libor-event.a + +EXTRA_DIST+= \ + src/common/common_sha1.i \ + src/common/sha256.c \ + src/common/Makefile.nmake + +#CFLAGS = -Wall -Wpointer-arith -O2 +AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common + +if USE_OPENBSD_MALLOC +libor_extra_source=src/common/OpenBSD_malloc_Linux.c +else +libor_extra_source= +endif + +src_common_libor_a_SOURCES = \ + src/common/address.c \ + src/common/compat.c \ + src/common/container.c \ + src/common/di_ops.c \ + src/common/log.c \ + src/common/memarea.c \ + src/common/mempool.c \ + src/common/procmon.c \ + src/common/util.c \ + src/common/util_codedigest.c \ + $(libor_extra_source) + +src_common_libor_crypto_a_SOURCES = \ + src/common/aes.c \ + src/common/crypto.c \ + src/common/torgzip.c \ + src/common/tortls.c + +src_common_libor_event_a_SOURCES = src/common/compat_libevent.c + +COMMONHEADERS = \ + src/common/address.h \ + src/common/aes.h \ + src/common/ciphers.inc \ + src/common/compat.h \ + src/common/compat_libevent.h \ + src/common/container.h \ + src/common/crypto.h \ + src/common/di_ops.h \ + src/common/ht.h \ + src/common/memarea.h \ + src/common/mempool.h \ + src/common/procmon.h \ + src/common/strlcat.c \ + src/common/strlcpy.c \ + src/common/torgzip.h \ + src/common/torint.h \ + src/common/torlog.h \ + src/common/tortls.h \ + src/common/util.h + +noinst_HEADERS+= $(COMMONHEADERS) + +DISTCLEANFILES+= src/common/common_sha1.i + +src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(COMMONHEADERS) + $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ + (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \ + elif test "@OPENSSL@" != none; then \ + (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \ + else \ + rm $@; \ + touch $@; \ + fi + +src/common/util_codedigest.o: src/common/common_sha1.i +src/common/crypto.o: src/common/sha256.c diff --git a/src/common/memarea.c b/src/common/memarea.c index 07bd593cc9..e87c0fbed6 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -118,7 +118,7 @@ alloc_chunk(size_t sz, int freelist_ok) size_t chunk_size = freelist_ok ? CHUNK_SIZE : sz; memarea_chunk_t *res; chunk_size += SENTINEL_LEN; - res = tor_malloc_roundup(&chunk_size); + res = tor_malloc(chunk_size); res->next_chunk = NULL; res->mem_size = chunk_size - CHUNK_HEADER_SIZE - SENTINEL_LEN; res->next_mem = res->u.mem; diff --git a/src/common/mempool.c b/src/common/mempool.c index 2416bce473..b255386700 100644 --- a/src/common/mempool.c +++ b/src/common/mempool.c @@ -70,7 +70,6 @@ #define ASSERT(x) tor_assert(x) #undef ALLOC_CAN_RETURN_NULL #define TOR -//#define ALLOC_ROUNDUP(p) tor_malloc_roundup(p) /* End Tor dependencies */ #else /* If you're not building this as part of Tor, you'll want to define the @@ -165,25 +164,16 @@ static mp_chunk_t * mp_chunk_new(mp_pool_t *pool) { size_t sz = pool->new_chunk_capacity * pool->item_alloc_size; -#ifdef ALLOC_ROUNDUP - size_t alloc_size = CHUNK_OVERHEAD + sz; - mp_chunk_t *chunk = ALLOC_ROUNDUP(&alloc_size); -#else mp_chunk_t *chunk = ALLOC(CHUNK_OVERHEAD + sz); -#endif + #ifdef MEMPOOL_STATS ++pool->total_chunks_allocated; #endif CHECK_ALLOC(chunk); memset(chunk, 0, sizeof(mp_chunk_t)); /* Doesn't clear the whole thing. */ chunk->magic = MP_CHUNK_MAGIC; -#ifdef ALLOC_ROUNDUP - chunk->mem_size = alloc_size - CHUNK_OVERHEAD; - chunk->capacity = chunk->mem_size / pool->item_alloc_size; -#else chunk->capacity = pool->new_chunk_capacity; chunk->mem_size = sz; -#endif chunk->next_mem = chunk->mem; chunk->pool = pool; return chunk; diff --git a/src/common/tortls.c b/src/common/tortls.c index 53bcc98919..a3485c7686 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -478,7 +478,7 @@ tor_tls_init(void) * a test of intelligence and determination. */ if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) { - log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but " + log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but " "some vendors have backported renegotiation code from " "0.9.8m without updating the version number. " "I will try SSL3_FLAGS and SSL_OP to enable renegotation.", @@ -486,12 +486,12 @@ tor_tls_init(void) use_unsafe_renegotiation_flag = 1; use_unsafe_renegotiation_op = 1; } else if (version > OPENSSL_V(0,9,8,'l')) { - log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " + log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " "I will try SSL_OP to enable renegotiation", SSLeay_version(SSLEAY_VERSION)); use_unsafe_renegotiation_op = 1; } else if (version <= OPENSSL_V(0,9,8,'k')) { - log_notice(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " + log_info(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " "0.9.8l, but some vendors have backported 0.9.8l's " "renegotiation code to earlier versions, and some have " "backported the code from 0.9.8m or 0.9.8n. I'll set both " diff --git a/src/common/util.c b/src/common/util.c index cea63e5c02..e79e73a0ff 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -290,37 +290,6 @@ _tor_free(void *mem) tor_free(mem); } -#if defined(HAVE_MALLOC_GOOD_SIZE) && !defined(HAVE_MALLOC_GOOD_SIZE_PROTOTYPE) -/* Some version of Mac OSX have malloc_good_size in their libc, but not - * actually defined in malloc/malloc.h. We detect this and work around it by - * prototyping. - */ -extern size_t malloc_good_size(size_t size); -#endif - -/** Allocate and return a chunk of memory of size at least *<b>size</b>, using - * the same resources we would use to malloc *<b>sizep</b>. Set *<b>sizep</b> - * to the number of usable bytes in the chunk of memory. */ -void * -_tor_malloc_roundup(size_t *sizep DMALLOC_PARAMS) -{ -#ifdef HAVE_MALLOC_GOOD_SIZE - tor_assert(*sizep < SIZE_T_CEILING); - *sizep = malloc_good_size(*sizep); - return _tor_malloc(*sizep DMALLOC_FN_ARGS); -#elif 0 && defined(HAVE_MALLOC_USABLE_SIZE) && !defined(USE_DMALLOC) - /* Never use malloc_usable_size(); it makes valgrind really unhappy, - * and doesn't win much in terms of usable space where it exists. */ - void *result; - tor_assert(*sizep < SIZE_T_CEILING); - result = _tor_malloc(*sizep DMALLOC_FN_ARGS); - *sizep = malloc_usable_size(result); - return result; -#else - return _tor_malloc(*sizep DMALLOC_FN_ARGS); -#endif -} - /** Call the platform malloc info function, and dump the results to the log at * level <b>severity</b>. If no such function exists, do nothing. */ void @@ -670,6 +639,16 @@ fast_memcmpstart(const void *mem, size_t memlen, return fast_memcmp(mem, prefix, plen); } +/** Given a nul-terminated string s, set every character before the nul + * to zero. */ +void +tor_strclear(char *s) +{ + while (*s) { + *s++ = '\0'; + } +} + /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ @@ -1351,11 +1330,11 @@ n_leapdays(int y1, int y2) static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -/** Return a time_t given a struct tm. The result is given in GMT, and - * does not account for leap seconds. +/** Compute a time_t given a struct tm. The result is given in GMT, and + * does not account for leap seconds. Return 0 on success, -1 on failure. */ -time_t -tor_timegm(struct tm *tm) +int +tor_timegm(const struct tm *tm, time_t *time_out) { /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. * It's way more brute-force than fiddling with tzset(). @@ -1363,11 +1342,11 @@ tor_timegm(struct tm *tm) time_t year, days, hours, minutes, seconds; int i; year = tm->tm_year + 1900; - if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11) { + if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11 || + tm->tm_year >= INT32_MAX-1900) { log_warn(LD_BUG, "Out-of-range argument to tor_timegm"); return -1; } - tor_assert(year < INT_MAX); days = 365 * (year-1970) + n_leapdays(1970,(int)year); for (i = 0; i < tm->tm_mon; ++i) days += days_per_month[i]; @@ -1378,7 +1357,8 @@ tor_timegm(struct tm *tm) minutes = hours*60 + tm->tm_min; seconds = minutes*60 + tm->tm_sec; - return seconds; + *time_out = seconds; + return 0; } /* strftime is locale-specific, so we need to replace those parts */ @@ -1438,7 +1418,7 @@ parse_rfc1123_time(const char *buf, time_t *t) return -1; } if (tm_mday < 1 || tm_mday > 31 || tm_hour > 23 || tm_min > 59 || - tm_sec > 60) { + tm_sec > 60 || tm_year >= INT32_MAX || tm_year < 1970) { char *esc = esc_for_log(buf); log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc); tor_free(esc); @@ -1474,8 +1454,7 @@ parse_rfc1123_time(const char *buf, time_t *t) } tm.tm_year -= 1900; - *t = tor_timegm(&tm); - return 0; + return tor_timegm(&tm, t); } /** Set <b>buf</b> to the ISO8601 encoding of the local value of <b>t</b>. @@ -1537,13 +1516,13 @@ parse_iso_time(const char *cp, time_t *t) return -1; } if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || - hour > 23 || minute > 59 || second > 60) { + hour > 23 || minute > 59 || second > 60 || year >= INT32_MAX) { char *esc = esc_for_log(cp); log_warn(LD_GENERAL, "ISO time %s was nonsensical", esc); tor_free(esc); return -1; } - st_tm.tm_year = year-1900; + st_tm.tm_year = (int)year-1900; st_tm.tm_mon = month-1; st_tm.tm_mday = day; st_tm.tm_hour = hour; @@ -1556,8 +1535,7 @@ parse_iso_time(const char *cp, time_t *t) tor_free(esc); return -1; } - *t = tor_timegm(&st_tm); - return 0; + return tor_timegm(&st_tm, t); } /** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh), @@ -2699,9 +2677,9 @@ digit_to_num(char d) * success, store the result in <b>out</b>, advance bufp to the next * character, and return 0. On failure, return -1. */ static int -scan_unsigned(const char **bufp, unsigned *out, int width, int base) +scan_unsigned(const char **bufp, unsigned long *out, int width, int base) { - unsigned result = 0; + unsigned long result = 0; int scanned_so_far = 0; const int hex = base==16; tor_assert(base == 10 || base == 16); @@ -2713,8 +2691,8 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base) while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp)) && scanned_so_far < width) { int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); - unsigned new_result = result * base + digit; - if (new_result > UINT32_MAX || new_result < result) + unsigned long new_result = result * base + digit; + if (new_result < result) return -1; /* over/underflow. */ result = new_result; ++scanned_so_far; @@ -2727,6 +2705,89 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base) return 0; } +/** Helper: Read an signed int from *<b>bufp</b> of up to <b>width</b> + * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On + * success, store the result in <b>out</b>, advance bufp to the next + * character, and return 0. On failure, return -1. */ +static int +scan_signed(const char **bufp, long *out, int width) +{ + int neg = 0; + unsigned long result = 0; + + if (!bufp || !*bufp || !out) + return -1; + if (width<0) + width=MAX_SCANF_WIDTH; + + if (**bufp == '-') { + neg = 1; + ++*bufp; + --width; + } + + if (scan_unsigned(bufp, &result, width, 10) < 0) + return -1; + + if (neg) { + if (result > ((unsigned long)LONG_MAX) + 1) + return -1; /* Underflow */ + *out = -(long)result; + } else { + if (result > LONG_MAX) + return -1; /* Overflow */ + *out = (long)result; + } + + return 0; +} + +/** Helper: Read a decimal-formatted double from *<b>bufp</b> of up to + * <b>width</b> characters. (Handle arbitrary width if <b>width</b> is less + * than 0.) On success, store the result in <b>out</b>, advance bufp to the + * next character, and return 0. On failure, return -1. */ +static int +scan_double(const char **bufp, double *out, int width) +{ + int neg = 0; + double result = 0; + int scanned_so_far = 0; + + if (!bufp || !*bufp || !out) + return -1; + if (width<0) + width=MAX_SCANF_WIDTH; + + if (**bufp == '-') { + neg = 1; + ++*bufp; + } + + while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) { + const int digit = digit_to_num(*(*bufp)++); + result = result * 10 + digit; + ++scanned_so_far; + } + if (**bufp == '.') { + double fracval = 0, denominator = 1; + ++*bufp; + ++scanned_so_far; + while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) { + const int digit = digit_to_num(*(*bufp)++); + fracval = fracval * 10 + digit; + denominator *= 10; + ++scanned_so_far; + } + result += fracval / denominator; + } + + if (!scanned_so_far) /* No actual digits scanned */ + return -1; + + *out = neg ? -result : result; + return 0; +} + /** Helper: copy up to <b>width</b> non-space characters from <b>bufp</b> to * <b>out</b>. Make sure <b>out</b> is nul-terminated. Advance <b>bufp</b> * to the next non-space character or the EOS. */ @@ -2763,6 +2824,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) } } else { int width = -1; + int longmod = 0; ++pattern; if (TOR_ISDIGIT(*pattern)) { width = digit_to_num(*pattern++); @@ -2775,17 +2837,57 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) if (!width) /* No zero-width things. */ return -1; } + if (*pattern == 'l') { + longmod = 1; + ++pattern; + } if (*pattern == 'u' || *pattern == 'x') { - unsigned *u = va_arg(ap, unsigned *); + unsigned long u; const int base = (*pattern == 'u') ? 10 : 16; if (!*buf) return n_matched; - if (scan_unsigned(&buf, u, width, base)<0) + if (scan_unsigned(&buf, &u, width, base)<0) return n_matched; + if (longmod) { + unsigned long *out = va_arg(ap, unsigned long *); + *out = u; + } else { + unsigned *out = va_arg(ap, unsigned *); + if (u > UINT_MAX) + return n_matched; + *out = (unsigned) u; + } + ++pattern; + ++n_matched; + } else if (*pattern == 'f') { + double *d = va_arg(ap, double *); + if (!longmod) + return -1; /* float not supported */ + if (!*buf) + return n_matched; + if (scan_double(&buf, d, width)<0) + return n_matched; + ++pattern; + ++n_matched; + } else if (*pattern == 'd') { + long lng=0; + if (scan_signed(&buf, &lng, width)<0) + return n_matched; + if (longmod) { + long *out = va_arg(ap, long *); + *out = lng; + } else { + int *out = va_arg(ap, int *); + if (lng < INT_MIN || lng > INT_MAX) + return n_matched; + *out = (int)lng; + } ++pattern; ++n_matched; } else if (*pattern == 's') { char *s = va_arg(ap, char *); + if (longmod) + return -1; if (width < 0) return -1; if (scan_string(&buf, s, width)<0) @@ -2794,6 +2896,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) ++n_matched; } else if (*pattern == 'c') { char *ch = va_arg(ap, char *); + if (longmod) + return -1; if (width != -1) return -1; if (!*buf) @@ -2804,6 +2908,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) } else if (*pattern == '%') { if (*buf != '%') return n_matched; + if (longmod) + return -1; ++buf; ++pattern; } else { @@ -2817,9 +2923,14 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) /** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b> * and store the results in the corresponding argument fields. Differs from - * sscanf in that it: Only handles %u, %x, %c and %Ns. Does not handle - * arbitrarily long widths. %u and %x do not consume any space. Is - * locale-independent. Returns -1 on malformed patterns. + * sscanf in that: + * <ul><li>It only handles %u, %lu, %x, %lx, %<NUM>s, %d, %ld, %lf, and %c. + * <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1) + * <li>It does not handle arbitrarily long widths. + * <li>Numbers do not consume any space characters. + * <li>It is locale-independent. + * <li>%u and %x do not consume any space. + * <li>It returns -1 on malformed patterns.</ul> * * (As with other locale-independent functions, we need this to parse data that * is in ASCII without worrying that the C library's locale-handling will make @@ -3800,10 +3911,15 @@ tor_process_handle_destroy(process_handle_t *process_handle, if (also_terminate_process) { if (tor_terminate_process(process_handle) < 0) { - log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", - tor_process_get_pid(process_handle)); + log_notice(LD_GENERAL, "Failed to terminate process with " + "PID '%d' ('%s').", tor_process_get_pid(process_handle), +#ifdef _WIN32 + format_win32_error(GetLastError())); +#else + strerror(errno)); +#endif } else { - log_info(LD_GENERAL, "Terminated process with PID '%d'", + log_info(LD_GENERAL, "Terminated process with PID '%d'.", tor_process_get_pid(process_handle)); } } @@ -4272,6 +4388,50 @@ tor_split_lines(smartlist_t *sl, char *buf, int len) } #ifdef _WIN32 + +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +smartlist_t * +tor_get_lines_from_handle(HANDLE *handle, + enum stream_status *stream_status_out) +{ + int pos; + char stdout_buf[600] = {0}; + smartlist_t *lines = NULL; + + tor_assert(stream_status_out); + + *stream_status_out = IO_STREAM_TERM; + + pos = tor_read_all_handle(handle, stdout_buf, sizeof(stdout_buf) - 1, NULL); + if (pos < 0) { + *stream_status_out = IO_STREAM_TERM; + return NULL; + } + if (pos == 0) { + *stream_status_out = IO_STREAM_EAGAIN; + return NULL; + } + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + stdout_buf[pos] = '\0'; + + /* Split up the buffer */ + lines = smartlist_new(); + tor_split_lines(lines, stdout_buf, pos); + + /* Currently 'lines' is populated with strings residing on the + stack. Replace them with their exact copies on the heap: */ + SMARTLIST_FOREACH(lines, char *, line, + SMARTLIST_REPLACE_CURRENT(lines, line, tor_strdup(line))); + + *stream_status_out = IO_STREAM_OKAY; + + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns -1 if there is a error reading, and 0 otherwise. * If the generated stream is flushed more often than on new lines, or @@ -4319,6 +4479,33 @@ log_from_handle(HANDLE *pipe, int severity) #else +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +smartlist_t * +tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out) +{ + enum stream_status stream_status; + char stdout_buf[400]; + smartlist_t *lines = NULL; + + while (1) { + memset(stdout_buf, 0, sizeof(stdout_buf)); + + stream_status = get_string_from_pipe(handle, + stdout_buf, sizeof(stdout_buf) - 1); + if (stream_status != IO_STREAM_OKAY) + goto done; + + if (!lines) lines = smartlist_new(); + smartlist_add(lines, tor_strdup(stdout_buf)); + } + + done: + *stream_status_out = stream_status; + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns 1 if stream is closed normally, -1 if there is a error reading, and * 0 otherwise. Handles lines from tor-fw-helper and @@ -4437,9 +4624,130 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count) return IO_STREAM_TERM; } -/* DOCDOC tor_check_port_forwarding */ +/** Parse a <b>line</b> from tor-fw-helper and issue an appropriate + * log message to our user. */ +static void +handle_fw_helper_line(const char *line) +{ + smartlist_t *tokens = smartlist_new(); + char *message = NULL; + char *message_for_log = NULL; + const char *external_port = NULL; + const char *internal_port = NULL; + const char *result = NULL; + int port = 0; + int success = 0; + + smartlist_split_string(tokens, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(tokens) < 5) + goto err; + + if (strcmp(smartlist_get(tokens, 0), "tor-fw-helper") || + strcmp(smartlist_get(tokens, 1), "tcp-forward")) + goto err; + + external_port = smartlist_get(tokens, 2); + internal_port = smartlist_get(tokens, 3); + result = smartlist_get(tokens, 4); + + if (smartlist_len(tokens) > 5) { + /* If there are more than 5 tokens, they are part of [<message>]. + Let's use a second smartlist to form the whole message; + strncat loops suck. */ + int i; + int message_words_n = smartlist_len(tokens) - 5; + smartlist_t *message_sl = smartlist_new(); + for (i = 0; i < message_words_n; i++) + smartlist_add(message_sl, smartlist_get(tokens, 5+i)); + + tor_assert(smartlist_len(message_sl) > 0); + message = smartlist_join_strings(message_sl, " ", 0, NULL); + + /* wrap the message in log-friendly wrapping */ + tor_asprintf(&message_for_log, " ('%s')", message); + + smartlist_free(message_sl); + } + + port = atoi(external_port); + if (port < 1 || port > 65535) + goto err; + + port = atoi(internal_port); + if (port < 1 || port > 65535) + goto err; + + if (!strcmp(result, "SUCCESS")) + success = 1; + else if (!strcmp(result, "FAIL")) + success = 0; + else + goto err; + + if (!success) { + log_warn(LD_GENERAL, "Tor was unable to forward TCP port '%s' to '%s'%s. " + "Please make sure that your router supports port " + "forwarding protocols (like NAT-PMP). Note that if '%s' is " + "your ORPort, your relay will be unable to receive inbound " + "traffic.", external_port, internal_port, + message_for_log ? message_for_log : "", + internal_port); + } else { + log_info(LD_GENERAL, + "Tor successfully forwarded TCP port '%s' to '%s'%s.", + external_port, internal_port, + message_for_log ? message_for_log : ""); + } + + goto done; + + err: + log_warn(LD_GENERAL, "tor-fw-helper sent us a string we could not " + "parse (%s).", line); + + done: + SMARTLIST_FOREACH(tokens, char *, cp, tor_free(cp)); + smartlist_free(tokens); + tor_free(message); + tor_free(message_for_log); +} + +/** Read what tor-fw-helper has to say in its stdout and handle it + * appropriately */ +static int +handle_fw_helper_output(process_handle_t *process_handle) +{ + smartlist_t *fw_helper_output = NULL; + enum stream_status stream_status = 0; + + fw_helper_output = + tor_get_lines_from_handle(tor_process_get_stdout_pipe(process_handle), + &stream_status); + if (!fw_helper_output) { /* didn't get any output from tor-fw-helper */ + /* if EAGAIN we should retry in the future */ + return (stream_status == IO_STREAM_EAGAIN) ? 0 : -1; + } + + /* Handle the lines we got: */ + SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) { + handle_fw_helper_line(line); + tor_free(line); + } SMARTLIST_FOREACH_END(line); + + smartlist_free(fw_helper_output); + + return 0; +} + +/** Spawn tor-fw-helper and ask it to forward the ports in + * <b>ports_to_forward</b>. <b>ports_to_forward</b> contains strings + * of the form "<external port>:<internal port>", which is the format + * that tor-fw-helper expects. */ void -tor_check_port_forwarding(const char *filename, int dir_port, int or_port, +tor_check_port_forwarding(const char *filename, + smartlist_t *ports_to_forward, time_t now) { /* When fw-helper succeeds, how long do we wait until running it again */ @@ -4453,32 +4761,51 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; - int stdout_status, stderr_status, retval; - const char *argv[10]; - char s_dirport[6], s_orport[6]; + int stderr_status, retval; + int stdout_status = 0; tor_assert(filename); - /* Set up command line for tor-fw-helper */ - snprintf(s_dirport, sizeof s_dirport, "%d", dir_port); - snprintf(s_orport, sizeof s_orport, "%d", or_port); - - /* TODO: Allow different internal and external ports */ - argv[0] = filename; - argv[1] = "--internal-or-port"; - argv[2] = s_orport; - argv[3] = "--external-or-port"; - argv[4] = s_orport; - argv[5] = "--internal-dir-port"; - argv[6] = s_dirport; - argv[7] = "--external-dir-port"; - argv[8] = s_dirport; - argv[9] = NULL; - /* Start the child, if it is not already running */ if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { - int status; + /*tor-fw-helper cli looks like this: tor_fw_helper -p :5555 -p 4555:1111 */ + const char **argv; /* cli arguments */ + int args_n, status; + int argv_index = 0; /* index inside 'argv' */ + + tor_assert(smartlist_len(ports_to_forward) > 0); + + /* check for overflow during 'argv' allocation: + (len(ports_to_forward)*2 + 2)*sizeof(char*) > SIZE_MAX == + len(ports_to_forward) > (((SIZE_MAX/sizeof(char*)) - 2)/2) */ + if ((size_t) smartlist_len(ports_to_forward) > + (((SIZE_MAX/sizeof(char*)) - 2)/2)) { + log_warn(LD_GENERAL, + "Overflow during argv allocation. This shouldn't happen."); + return; + } + /* check for overflow during 'argv_index' increase: + ((len(ports_to_forward)*2 + 2) > INT_MAX) == + len(ports_to_forward) > (INT_MAX - 2)/2 */ + if (smartlist_len(ports_to_forward) > (INT_MAX - 2)/2) { + log_warn(LD_GENERAL, + "Overflow during argv_index increase. This shouldn't happen."); + return; + } + + /* Calculate number of cli arguments: one for the filename, two + for each smartlist element (one for "-p" and one for the + ports), and one for the final NULL. */ + args_n = 1 + 2*smartlist_len(ports_to_forward) + 1; + argv = tor_malloc_zero(sizeof(char*)*args_n); + + argv[argv_index++] = filename; + SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) { + argv[argv_index++] = "-p"; + argv[argv_index++] = port; + } SMARTLIST_FOREACH_END(port); + argv[argv_index] = NULL; /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; @@ -4495,6 +4822,8 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif + tor_free(argv); + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); @@ -4512,16 +4841,17 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Read from stdout/stderr and log result */ retval = 0; #ifdef _WIN32 - stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); - /* If we got this far (on Windows), the process started */ - retval = 0; + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO); #else - stdout_status = log_from_pipe(child_handle->stdout_handle, - LOG_INFO, filename, &retval); stderr_status = log_from_pipe(child_handle->stderr_handle, - LOG_WARN, filename, &retval); + LOG_INFO, filename, &retval); #endif + if (handle_fw_helper_output(child_handle) < 0) { + log_warn(LD_GENERAL, "Failed to handle fw helper output."); + stdout_status = -1; + retval = -1; + } + if (retval) { /* There was a problem in the child process */ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; diff --git a/src/common/util.h b/src/common/util.h index f700e9f4cb..04812df7ca 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -76,7 +76,6 @@ /* Memory management */ void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC; -void *_tor_malloc_roundup(size_t *size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS); char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); @@ -189,6 +188,7 @@ int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix); +void tor_strclear(char *s); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -245,7 +245,7 @@ int64_t tv_to_msec(const struct timeval *tv); int64_t tv_to_usec(const struct timeval *tv); long tv_udiff(const struct timeval *start, const struct timeval *end); long tv_mdiff(const struct timeval *start, const struct timeval *end); -time_t tor_timegm(struct tm *tm); +int tor_timegm(const struct tm *tm, time_t *time_out); #define RFC1123_TIME_LEN 29 void format_rfc1123_time(char *buf, time_t t); int parse_rfc1123_time(const char *buf, time_t *t); @@ -374,7 +374,8 @@ void write_pidfile(char *filename); /* Port forwarding */ void tor_check_port_forwarding(const char *filename, - int dir_port, int or_port, time_t now); + struct smartlist_t *ports_to_forward, + time_t now); typedef struct process_handle_t process_handle_t; typedef struct process_environment_t process_environment_t; @@ -465,6 +466,16 @@ HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); #endif +#ifdef _WIN32 +struct smartlist_t * +tor_get_lines_from_handle(HANDLE *handle, + enum stream_status *stream_status); +#else +struct smartlist_t * +tor_get_lines_from_handle(FILE *handle, + enum stream_status *stream_status); +#endif + int tor_terminate_process(process_handle_t *process_handle); void tor_process_handle_destroy(process_handle_t *process_handle, int also_terminate_process); diff --git a/src/config/Makefile.am b/src/config/Makefile.am deleted file mode 100644 index 90dd218b40..0000000000 --- a/src/config/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -confdir = $(sysconfdir)/tor - -tordatadir = $(datadir)/tor - -EXTRA_DIST = geoip -# fallback-consensus - -conf_DATA = torrc.sample - -tordata_DATA = geoip -# fallback_consensus - -# If we don't have it, fake it. -fallback-consensus: - touch fallback-consensus - diff --git a/src/config/include.am b/src/config/include.am new file mode 100644 index 0000000000..e6e1fe0440 --- /dev/null +++ b/src/config/include.am @@ -0,0 +1,16 @@ +confdir = $(sysconfdir)/tor + +tordatadir = $(datadir)/tor + +EXTRA_DIST+= src/config/geoip +# fallback-consensus + +conf_DATA = src/config/torrc.sample + +tordata_DATA = src/config/geoip +# fallback_consensus + +# If we don't have it, fake it. +src_config_fallback-consensus: + touch src/config/fallback-consensus + diff --git a/src/include.am b/src/include.am new file mode 100644 index 0000000000..965a494042 --- /dev/null +++ b/src/include.am @@ -0,0 +1,6 @@ +include src/common/include.am +include src/or/include.am +include src/test/include.am +include src/tools/include.am +include src/win32/include.am +include src/config/include.am
\ No newline at end of file diff --git a/src/or/Makefile.am b/src/or/Makefile.am deleted file mode 100644 index 3cc789a1be..0000000000 --- a/src/or/Makefile.am +++ /dev/null @@ -1,158 +0,0 @@ -bin_PROGRAMS = tor -noinst_LIBRARIES = libtor.a - -if BUILD_NT_SERVICES -tor_platform_source=ntmain.c -else -tor_platform_source= -endif - -EXTRA_DIST=ntmain.c or_sha1.i Makefile.nmake - -if USE_EXTERNAL_EVDNS -evdns_source= -else -evdns_source=eventdns.c -endif - -libtor_a_SOURCES = \ - buffers.c \ - circuitbuild.c \ - circuitlist.c \ - circuituse.c \ - command.c \ - config.c \ - connection.c \ - connection_edge.c \ - connection_or.c \ - control.c \ - cpuworker.c \ - directory.c \ - dirserv.c \ - dirvote.c \ - dns.c \ - dnsserv.c \ - geoip.c \ - hibernate.c \ - main.c \ - microdesc.c \ - networkstatus.c \ - nodelist.c \ - onion.c \ - transports.c \ - policies.c \ - reasons.c \ - relay.c \ - rendclient.c \ - rendcommon.c \ - rendmid.c \ - rendservice.c \ - rephist.c \ - router.c \ - routerlist.c \ - routerparse.c \ - status.c \ - $(evdns_source) \ - $(tor_platform_source) \ - config_codedigest.c - -#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \ -# ../common/libor-event.a - - -tor_SOURCES = tor_main.c - -AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ - -DLOCALSTATEDIR="\"$(localstatedir)\"" \ - -DBINDIR="\"$(bindir)\"" - -# -L flags need to go in LDFLAGS. -l flags need to go in LDADD. -# This seems to matter nowhere but on windows, but I assure you that it -# matters a lot there, and is quite hard to debug if you forget to do it. - - -tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ -tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \ - ../common/libor-event.a \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ - -noinst_HEADERS = \ - buffers.h \ - circuitbuild.h \ - circuitlist.h \ - circuituse.h \ - command.h \ - config.h \ - connection.h \ - connection_edge.h \ - connection_or.h \ - control.h \ - cpuworker.h \ - directory.h \ - dirserv.h \ - dirvote.h \ - dns.h \ - dnsserv.h \ - eventdns.h \ - eventdns_tor.h \ - geoip.h \ - hibernate.h \ - main.h \ - microdesc.h \ - networkstatus.h \ - nodelist.h \ - ntmain.h \ - onion.h \ - or.h \ - transports.h \ - policies.h \ - reasons.h \ - relay.h \ - rendclient.h \ - rendcommon.h \ - rendmid.h \ - rendservice.h \ - rephist.h \ - router.h \ - routerlist.h \ - routerparse.h \ - status.h \ - micro-revision.i - -config_codedigest.o: or_sha1.i - -tor_main.o: micro-revision.i - -micro-revision.i: FORCE - @rm -f micro-revision.tmp; \ - if test -d "$(top_srcdir)/.git" && \ - test -x "`which git 2>&1;true`"; then \ - HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ - echo \"$$HASH\" > micro-revision.tmp; \ - fi; \ - if test ! -f micro-revision.tmp ; then \ - if test ! -f micro-revision.i ; then \ - echo '""' > micro-revision.i; \ - fi; \ - elif test ! -f micro-revision.i || \ - test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ - mv micro-revision.tmp micro-revision.i; \ - fi; true - -or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES) - if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(tor_SOURCES) $(libtor_a_SOURCES)) | \ - "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \ - "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \ - else \ - rm or_sha1.i; \ - touch or_sha1.i; \ - fi - -CLEANFILES = micro-revision.i - -#Dummy target to ensure that micro-revision.i _always_ gets built. -FORCE: diff --git a/src/or/buffers.c b/src/or/buffers.c index 9acc22971a..a8d06cef1e 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -192,8 +192,6 @@ chunk_new_with_alloc_size(size_t alloc) freelist->lowest_length = freelist->cur_length; ++freelist->n_hit; } else { - /* XXXX take advantage of tor_malloc_roundup, once we know how that - * affects freelists. */ if (freelist) ++freelist->n_alloc; else @@ -216,7 +214,7 @@ static INLINE chunk_t * chunk_new_with_alloc_size(size_t alloc) { chunk_t *ch; - ch = tor_malloc_roundup(&alloc); + ch = tor_malloc(alloc); ch->next = NULL; ch->datalen = 0; ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b82cce9881..fe327cab45 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -135,6 +135,9 @@ static entry_guard_t *entry_guard_get_by_id_digest(const char *digest); static void bridge_free(bridge_info_t *bridge); +static int entry_guard_inc_first_hop_count(entry_guard_t *guard); +static void pathbias_count_success(origin_circuit_t *circ); + /** * This function decides if CBT learning should be disabled. It returns * true if one or more of the following four conditions are met: @@ -1624,7 +1627,7 @@ circuit_build_times_set_timeout(circuit_build_times_t *cbt) return; if (cbt->timeout_ms < circuit_build_times_min_timeout()) { - log_warn(LD_CIRC, "Set buildtimeout to low value %fms. Setting to %dms", + log_info(LD_CIRC, "Set buildtimeout to low value %fms. Setting to %dms", cbt->timeout_ms, circuit_build_times_min_timeout()); cbt->timeout_ms = circuit_build_times_min_timeout(); if (cbt->close_ms < cbt->timeout_ms) { @@ -2285,28 +2288,11 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) } log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); - /* Don't count cannibalized or onehop circs for path bias */ + if (circ->build_state->onehop_tunnel || circ->has_opened) { control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0); - } else { - entry_guard_t *guard = - entry_guard_get_by_id_digest(circ->_base.n_conn->identity_digest); - - if (guard) { - guard->circuit_successes++; - - log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s=%s", - guard->circuit_successes, guard->first_hops, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); - - if (guard->first_hops < guard->circuit_successes) { - log_warn(LD_BUG, "Unexpectedly high circuit_successes (%u/%u) " - "for guard %s", - guard->circuit_successes, guard->first_hops, - guard->nickname); - } - } } + if (!can_complete_circuit && !circ->build_state->onehop_tunnel) { const or_options_t *options = get_options(); can_complete_circuit=1; @@ -2322,6 +2308,8 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) consider_testing_reachability(1, 1); } } + + pathbias_count_success(circ); circuit_rep_hist_note_result(circ); circuit_has_opened(circ); /* do other actions as necessary */ @@ -2444,6 +2432,13 @@ circuit_extend(cell_t *cell, circuit_t *circ) return -1; } + if (tor_addr_is_internal(&n_addr, 0) && + !get_options()->ExtendAllowPrivateAddresses) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Client asked me to extend to a private address"); + return -1; + } + /* Check if they asked us for 0000..0000. We support using * an empty fingerprint for the first hop (e.g. for a bridge relay), * but we don't want to let people send us extend cells for empty @@ -2475,7 +2470,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) log_debug(LD_CIRC|LD_OR,"Next router (%s:%d): %s", fmt_addr(&n_addr), (int)n_port, msg?msg:"????"); - circ->n_hop = extend_info_alloc(NULL /*nickname*/, + circ->n_hop = extend_info_new(NULL /*nickname*/, id_digest, NULL /*onion_key*/, &n_addr, n_port); @@ -2613,12 +2608,219 @@ pathbias_get_scale_threshold(const or_options_t *options) static int pathbias_get_scale_factor(const or_options_t *options) { -#define DFLT_PATH_BIAS_SCALE_FACTOR 4 +#define DFLT_PATH_BIAS_SCALE_FACTOR 2 if (options->PathBiasScaleFactor >= 1) return options->PathBiasScaleFactor; else return networkstatus_get_param(NULL, "pb_scalefactor", - DFLT_PATH_BIAS_SCALE_THRESHOLD, 1, INT32_MAX); + DFLT_PATH_BIAS_SCALE_FACTOR, 1, INT32_MAX); +} + +static const char * +pathbias_state_to_string(path_state_t state) +{ + switch (state) { + case PATH_STATE_NEW_CIRC: + return "new"; + case PATH_STATE_DID_FIRST_HOP: + return "first hop"; + case PATH_STATE_SUCCEEDED: + return "succeeded"; + } + + return "unknown"; +} + +/** + * Check our circuit state to see if this is a successful first hop. + * If so, record it in the current guard's path bias first_hop count. + * + * Also check for several potential error cases for bug #6475. + */ +static int +pathbias_count_first_hop(origin_circuit_t *circ) +{ +#define FIRST_HOP_NOTICE_INTERVAL (600) + static ratelim_t first_hop_notice_limit = + RATELIM_INIT(FIRST_HOP_NOTICE_INTERVAL); + char *rate_msg = NULL; + + /* We can't do path bias accounting without entry guards. + * Testing and controller circuits also have no guards. */ + if (get_options()->UseEntryGuards == 0 || + circ->_base.purpose == CIRCUIT_PURPOSE_TESTING || + circ->_base.purpose == CIRCUIT_PURPOSE_CONTROLLER) { + return 0; + } + + /* Completely ignore one hop circuits */ + if (circ->build_state->onehop_tunnel) { + tor_assert(circ->build_state->desired_path_len == 1); + return 0; + } + + if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) { + /* Help track down the real cause of bug #6475: */ + if (circ->has_opened && circ->path_state != PATH_STATE_DID_FIRST_HOP) { + if ((rate_msg = rate_limit_log(&first_hop_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Opened circuit is in strange path state %s. " + "Circuit is a %s currently %s. %s", + pathbias_state_to_string(circ->path_state), + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + + /* Don't count cannibalized circs for path bias */ + if (!circ->has_opened) { + entry_guard_t *guard; + + guard = entry_guard_get_by_id_digest( + circ->_base.n_conn->identity_digest); + if (guard) { + if (circ->path_state == PATH_STATE_NEW_CIRC) { + circ->path_state = PATH_STATE_DID_FIRST_HOP; + + if (entry_guard_inc_first_hop_count(guard) < 0) { + /* Bogus guard; we already warned. */ + return -END_CIRC_REASON_TORPROTOCOL; + } + } else { + if ((rate_msg = rate_limit_log(&first_hop_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Unopened circuit has strange path state %s. " + "Circuit is a %s currently %s. %s", + pathbias_state_to_string(circ->path_state), + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + } else { + if ((rate_msg = rate_limit_log(&first_hop_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Unopened circuit has no known guard. " + "Circuit is a %s currently %s. %s", + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + } + } else { + /* Help track down the real cause of bug #6475: */ + if (circ->path_state == PATH_STATE_NEW_CIRC) { + if ((rate_msg = rate_limit_log(&first_hop_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "A %s circuit is in cpath state %d (opened: %d). " + "Circuit is a %s currently %s. %s", + pathbias_state_to_string(circ->path_state), + circ->cpath->state, circ->has_opened, + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + } + + return 0; +} + +/** + * Check our circuit state to see if this is a successful circuit + * completion. If so, record it in the current guard's path bias + * success count. + * + * Also check for several potential error cases for bug #6475. + */ +static void +pathbias_count_success(origin_circuit_t *circ) +{ +#define SUCCESS_NOTICE_INTERVAL (600) + static ratelim_t success_notice_limit = + RATELIM_INIT(SUCCESS_NOTICE_INTERVAL); + char *rate_msg = NULL; + + /* We can't do path bias accounting without entry guards. + * Testing and controller circuits also have no guards. */ + if (get_options()->UseEntryGuards == 0 || + circ->_base.purpose == CIRCUIT_PURPOSE_TESTING || + circ->_base.purpose == CIRCUIT_PURPOSE_CONTROLLER) { + return; + } + + /* Ignore one hop circuits */ + if (circ->build_state->onehop_tunnel) { + tor_assert(circ->build_state->desired_path_len == 1); + return; + } + + /* Don't count cannibalized/reused circs for path bias */ + if (!circ->has_opened) { + entry_guard_t *guard = + entry_guard_get_by_id_digest(circ->_base.n_conn->identity_digest); + + if (guard) { + if (circ->path_state == PATH_STATE_DID_FIRST_HOP) { + circ->path_state = PATH_STATE_SUCCEEDED; + guard->circuit_successes++; + + log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s=%s", + guard->circuit_successes, guard->first_hops, + guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + } else { + if ((rate_msg = rate_limit_log(&success_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Succeeded circuit is in strange path state %s. " + "Circuit is a %s currently %s. %s", + pathbias_state_to_string(circ->path_state), + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + + if (guard->first_hops < guard->circuit_successes) { + log_notice(LD_BUG, "Unexpectedly high circuit_successes (%u/%u) " + "for guard %s=%s", + guard->circuit_successes, guard->first_hops, + guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + } + /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to + * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here. + * No need to log that case. */ + } else if (circ->_base.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + if ((rate_msg = rate_limit_log(&success_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Completed circuit has no known guard. " + "Circuit is a %s currently %s. %s", + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + } else { + if (circ->path_state != PATH_STATE_SUCCEEDED) { + if ((rate_msg = rate_limit_log(&success_notice_limit, + approx_time()))) { + log_info(LD_BUG, + "Opened circuit is in strange path state %s. " + "Circuit is a %s currently %s. %s", + pathbias_state_to_string(circ->path_state), + circuit_purpose_to_string(circ->_base.purpose), + circuit_state_to_string(circ->_base.state), + rate_msg); + } + } + } } /** Increment the number of times we successfully extended a circuit to @@ -2639,9 +2841,11 @@ entry_guard_inc_first_hop_count(entry_guard_t *guard) if (guard->circuit_successes/((double)guard->first_hops) < pathbias_get_disable_rate(options)) { + /* This message is currently disabled by default. */ log_warn(LD_PROTOCOL, "Extremely low circuit success rate %u/%u for guard %s=%s. " - "This might indicate an attack, or a bug.", + "This indicates either an overloaded guard, an attack, or " + "a bug.", guard->circuit_successes, guard->first_hops, guard->nickname, hex_str(guard->identity, DIGEST_LEN)); @@ -2662,12 +2866,23 @@ entry_guard_inc_first_hop_count(entry_guard_t *guard) /* If we get a ton of circuits, just scale everything down */ if (guard->first_hops > (unsigned)pathbias_get_scale_threshold(options)) { const int scale_factor = pathbias_get_scale_factor(options); - guard->first_hops /= scale_factor; - guard->circuit_successes /= scale_factor; + /* For now, only scale if there will be no rounding error... + * XXX024: We want to switch to a real moving average for 0.2.4. */ + if ((guard->first_hops % scale_factor) == 0 && + (guard->circuit_successes % scale_factor) == 0) { + log_info(LD_PROTOCOL, + "Scaling pathbias counts to (%u/%u)/%d for guard %s=%s", + guard->circuit_successes, guard->first_hops, + scale_factor, guard->nickname, hex_str(guard->identity, + DIGEST_LEN)); + guard->first_hops /= scale_factor; + guard->circuit_successes /= scale_factor; + } } guard->first_hops++; - log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s", - guard->circuit_successes, guard->first_hops, guard->nickname); + log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s=%s", + guard->circuit_successes, guard->first_hops, guard->nickname, + hex_str(guard->identity, DIGEST_LEN)); return 0; } @@ -2687,22 +2902,13 @@ circuit_finish_handshake(origin_circuit_t *circ, uint8_t reply_type, { char keys[CPATH_KEY_MATERIAL_LEN]; crypt_path_t *hop; + int rv; + + if ((rv = pathbias_count_first_hop(circ)) < 0) + return rv; if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) { hop = circ->cpath; - /* Don't count cannibalized or onehop circs for path bias */ - if (!circ->has_opened && !circ->build_state->onehop_tunnel) { - entry_guard_t *guard; - - guard = entry_guard_get_by_id_digest( - circ->_base.n_conn->identity_digest); - if (guard) { - if (entry_guard_inc_first_hop_count(guard) < 0) { - /* Bogus guard; we already warned. */ - return -END_CIRC_REASON_TORPROTOCOL; - } - } - } } else { hop = onion_next_hop_in_cpath(circ->cpath); if (!hop) { /* got an extended when we're all done? */ @@ -3586,12 +3792,10 @@ onion_extend_cpath(origin_circuit_t *circ) } else if (cur_len == 0) { /* picking first node */ const node_t *r = choose_good_entry_server(purpose, state); if (r) { - /* If we're extending to a bridge, use the preferred address - rather than the primary, for potentially extending to an IPv6 - bridge. */ - int use_pref_addr = (r->ri != NULL && - r->ri->purpose == ROUTER_PURPOSE_BRIDGE); - info = extend_info_from_node(r, use_pref_addr); + /* If we're a client, use the preferred address rather than the + primary address, for potentially connecting to an IPv6 OR + port. */ + info = extend_info_from_node(r, server_mode(get_options()) == 0); tor_assert(info); } } else { @@ -3642,7 +3846,7 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) /** Allocate a new extend_info object based on the various arguments. */ extend_info_t * -extend_info_alloc(const char *nickname, const char *digest, +extend_info_new(const char *nickname, const char *digest, crypto_pk_t *onion_key, const tor_addr_t *addr, uint16_t port) { @@ -3657,47 +3861,45 @@ extend_info_alloc(const char *nickname, const char *digest, return info; } -/** Allocate and return a new extend_info_t that can be used to build - * a circuit to or through the router <b>r</b>. Use the primary - * address of the router unless <b>for_direct_connect</b> is true, in - * which case the preferred address is used instead. */ +/** Allocate and return a new extend_info that can be used to build a + * circuit to or through the node <b>node</b>. Use the primary address + * of the node (i.e. its IPv4 address) unless + * <b>for_direct_connect</b> is true, in which case the preferred + * address is used instead. May return NULL if there is not enough + * info about <b>node</b> to extend to it--for example, if there is no + * routerinfo_t or microdesc_t. + **/ extend_info_t * -extend_info_from_router(const routerinfo_t *r, int for_direct_connect) +extend_info_from_node(const node_t *node, int for_direct_connect) { tor_addr_port_t ap; - tor_assert(r); + + if (node->ri == NULL && (node->rs == NULL || node->md == NULL)) + return NULL; if (for_direct_connect) - router_get_pref_orport(r, &ap); + node_get_pref_orport(node, &ap); else - router_get_prim_orport(r, &ap); - return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &ap.addr, ap.port); -} + node_get_prim_orport(node, &ap); -/** Allocate and return a new extend_info that can be used to build a - * circuit to or through the node <b>node</b>. Use the primary address - * of the node unless <b>for_direct_connect</b> is true, in which case - * the preferred address is used instead. May return NULL if there is - * not enough info about <b>node</b> to extend to it--for example, if - * there is no routerinfo_t or microdesc_t. - **/ -extend_info_t * -extend_info_from_node(const node_t *node, int for_direct_connect) -{ - if (node->ri) { - return extend_info_from_router(node->ri, for_direct_connect); - } else if (node->rs && node->md) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); - return extend_info_alloc(node->rs->nickname, + log_debug(LD_CIRC, "using %s:%d for %s", + fmt_and_decorate_addr(&ap.addr), ap.port, + node->ri ? node->ri->nickname : node->rs->nickname); + + if (node->ri) + return extend_info_new(node->ri->nickname, + node->identity, + node->ri->onion_pkey, + &ap.addr, + ap.port); + else if (node->rs && node->md) + return extend_info_new(node->rs->nickname, node->identity, node->md->onion_pkey, - &addr, - node->rs->or_port); - } else { + &ap.addr, + ap.port); + else return NULL; - } } /** Release storage held by an extend_info_t struct. */ @@ -4702,6 +4904,12 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) const or_options_t *options = get_options(); unsigned hop_cnt, success_cnt; + if (!node) { + *msg = tor_strdup("Unable to parse entry nodes: " + "EntryGuardPathBias without EntryGuard"); + break; + } + if (tor_sscanf(line->value, "%u %u", &success_cnt, &hop_cnt) != 2) { log_warn(LD_GENERAL, "Unable to parse guard path bias info: " "Misformated EntryGuardPathBias %s", escaped(line->value)); @@ -4969,203 +5177,37 @@ bridge_free(bridge_info_t *bridge) tor_free(bridge); } -/** A list of pluggable transports found in torrc. */ -static smartlist_t *transport_list = NULL; - -/** Mark every entry of the transport list to be removed on our next call to - * sweep_transport_list unless it has first been un-marked. */ -void -mark_transport_list(void) -{ - if (!transport_list) - transport_list = smartlist_new(); - SMARTLIST_FOREACH(transport_list, transport_t *, t, - t->marked_for_removal = 1); -} - -/** Remove every entry of the transport list that was marked with - * mark_transport_list if it has not subsequently been un-marked. */ -void -sweep_transport_list(void) -{ - if (!transport_list) - transport_list = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, t) { - if (t->marked_for_removal) { - SMARTLIST_DEL_CURRENT(transport_list, t); - transport_free(t); - } - } SMARTLIST_FOREACH_END(t); -} - -/** Initialize the pluggable transports list to empty, creating it if - * needed. */ -void -clear_transport_list(void) -{ - if (!transport_list) - transport_list = smartlist_new(); - SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t)); - smartlist_clear(transport_list); -} - -/** Free the pluggable transport struct <b>transport</b>. */ -void -transport_free(transport_t *transport) -{ - if (!transport) - return; - - tor_free(transport->name); - tor_free(transport); -} - -/** Returns the transport in our transport list that has the name <b>name</b>. - * Else returns NULL. */ -transport_t * -transport_get_by_name(const char *name) +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches any of the + * tor_addr_port_t's in <b>orports</b>, return that bridge. Else return + * NULL. */ +static bridge_info_t * +get_configured_bridge_by_orports_digest(const char *digest, + const smartlist_t *orports) { - tor_assert(name); - - if (!transport_list) + if (!bridge_list) return NULL; - - SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) { - if (!strcmp(transport->name, name)) - return transport; - } SMARTLIST_FOREACH_END(transport); - - return NULL; -} - -/** Returns a transport_t struct for a transport proxy supporting the - protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using - SOCKS version <b>socks_ver</b>. */ -transport_t * -transport_new(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver) -{ - transport_t *t = tor_malloc_zero(sizeof(transport_t)); - - tor_addr_copy(&t->addr, addr); - t->port = port; - t->name = tor_strdup(name); - t->socks_version = socks_ver; - - return t; -} - -/** Resolve any conflicts that the insertion of transport <b>t</b> - * might cause. - * Return 0 if <b>t</b> is OK and should be registered, 1 if there is - * a transport identical to <b>t</b> already registered and -1 if - * <b>t</b> cannot be added due to conflicts. */ -static int -transport_resolve_conflicts(transport_t *t) -{ - /* This is how we resolve transport conflicts: - - If there is already a transport with the same name and addrport, - we either have duplicate torrc lines OR we are here post-HUP and - this transport was here pre-HUP as well. In any case, mark the - old transport so that it doesn't get removed and ignore the new - one. Our caller has to free the new transport so we return '1' to - signify this. - - If there is already a transport with the same name but different - addrport: - * if it's marked for removal, it means that it either has a lower - priority than 't' in torrc (otherwise the mark would have been - cleared by the paragraph above), or it doesn't exist at all in - the post-HUP torrc. We destroy the old transport and register 't'. - * if it's *not* marked for removal, it means that it was newly - added in the post-HUP torrc or that it's of higher priority, in - this case we ignore 't'. */ - transport_t *t_tmp = transport_get_by_name(t->name); - if (t_tmp) { /* same name */ - if (tor_addr_eq(&t->addr, &t_tmp->addr) && (t->port == t_tmp->port)) { - /* same name *and* addrport */ - t_tmp->marked_for_removal = 0; - return 1; - } else { /* same name but different addrport */ - if (t_tmp->marked_for_removal) { /* marked for removal */ - log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " - "but there was already a transport marked for deletion at " - "'%s:%u'. We deleted the old transport and registered the " - "new one.", t->name, fmt_addr(&t->addr), t->port, - fmt_addr(&t_tmp->addr), t_tmp->port); - smartlist_remove(transport_list, t_tmp); - transport_free(t_tmp); - } else { /* *not* marked for removal */ - log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " - "but the same transport already exists at '%s:%u'. " - "Skipping.", t->name, fmt_addr(&t->addr), t->port, - fmt_addr(&t_tmp->addr), t_tmp->port); - return -1; + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) + { + if (tor_digest_is_zero(bridge->identity)) { + SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, ap) + { + if (tor_addr_compare(&bridge->addr, &ap->addr, CMP_EXACT) == 0 && + bridge->port == ap->port) + return bridge; + } + SMARTLIST_FOREACH_END(ap); } + if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) + return bridge; } - } - - return 0; -} - -/** Add transport <b>t</b> to the internal list of pluggable - * transports. - * Returns 0 if the transport was added correctly, 1 if the same - * transport was already registered (in this case the caller must - * free the transport) and -1 if there was an error. */ -int -transport_add(transport_t *t) -{ - int r; - tor_assert(t); - - r = transport_resolve_conflicts(t); - - switch (r) { - case 0: /* should register transport */ - if (!transport_list) - transport_list = smartlist_new(); - smartlist_add(transport_list, t); - return 0; - default: /* let our caller know the return code */ - return r; - } -} - -/** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>. - * <b>name</b> is set to the name of the protocol this proxy uses. - * <b>socks_ver</b> is set to the SOCKS version of the proxy. */ -int -transport_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver) -{ - transport_t *t = transport_new(addr, port, name, socks_ver); - - int r = transport_add(t); - - switch (r) { - case -1: - default: - log_notice(LD_GENERAL, "Could not add transport %s at %s:%u. Skipping.", - t->name, fmt_addr(&t->addr), t->port); - transport_free(t); - return -1; - case 1: - log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", - t->name, fmt_addr(&t->addr), t->port); - transport_free(t); /* falling */ - return 0; - case 0: - log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", - t->name, fmt_addr(&t->addr), t->port); - return 0; - } + SMARTLIST_FOREACH_END(bridge); + return NULL; } -/** Return a bridge pointer if <b>ri</b> is one of our known bridges - * (either by comparing keys if possible, else by comparing addr/port). - * Else return NULL. */ +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches <b>addr</b>:<b>/port</b>, + * return that bridge. Else return NULL. */ static bridge_info_t * get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, uint16_t port, @@ -5191,11 +5233,13 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, static bridge_info_t * get_configured_bridge_by_routerinfo(const routerinfo_t *ri) { - tor_addr_port_t ap; - - router_get_pref_orport(ri, &ap); - return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, - ri->cache_info.identity_digest); + bridge_info_t *bi = NULL; + smartlist_t *orports = router_get_all_orports(ri); + bi = get_configured_bridge_by_orports_digest(ri->cache_info.identity_digest, + orports); + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + return bi; } /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ @@ -5209,30 +5253,12 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) int node_is_a_configured_bridge(const node_t *node) { - int retval = 0; /* Negative. */ - smartlist_t *orports = NULL; - - if (!node) - goto out; - - orports = node_get_all_orports(node); - if (orports == NULL) - goto out; - - SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) { - if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port, - node->identity) != NULL) { - retval = 1; - goto out; - } - } SMARTLIST_FOREACH_END(orport); - - out: - if (orports != NULL) { - SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); - smartlist_free(orports); - orports = NULL; - } + int retval = 0; + smartlist_t *orports = node_get_all_orports(node); + retval = get_configured_bridge_by_orports_digest(node->identity, + orports) != NULL; + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); return retval; } @@ -5356,7 +5382,7 @@ routerset_contains_bridge(const routerset_t *routerset, if (!routerset) return 0; - extinfo = extend_info_alloc( + extinfo = extend_info_new( NULL, bridge->identity, NULL, &bridge->addr, bridge->port); result = routerset_contains_extendinfo(routerset, extinfo); extend_info_free(extinfo); @@ -5449,9 +5475,8 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) address = tor_dup_addr(&bridge->addr); directory_initiate_command(address, &bridge->addr, - bridge->port, 0, - 0, /* does not matter */ - 1, bridge->identity, + bridge->port, 0/*no dirport*/, + bridge->identity, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE, 0, "authority.z", NULL, 0, 0); @@ -5594,21 +5619,26 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } } - /* Indicate that we prefer connecting to this bridge over the - protocol that the bridge address indicates. Last bridge - descriptor handled wins. */ - ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + /* Mark bridge as preferably connected to over IPv6 if its IPv6 + address is in a Bridge line and ClientPreferIPv6ORPort is + set. Unless both is true, a potential IPv6 OR port of this + bridge won't get selected. + + XXX ipv6_preferred is never reset (#6757) */ + if (get_options()->ClientPreferIPv6ORPort == 1 && + tor_addr_family(&bridge->addr) == AF_INET6) + node->ipv6_preferred = 1; /* XXXipv6 we lack support for falling back to another address for the same relay, warn the user */ if (!tor_addr_is_null(&ri->ipv6_addr)) { tor_addr_port_t ap; - router_get_pref_orport(ri, &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:%d).", ri->nickname, - ri->ipv6_preferred ? "IPv6" : "IPv4", + node->ipv6_preferred ? "IPv6" : "IPv4", fmt_addr(&ap.addr), ap.port); } } @@ -5798,10 +5828,7 @@ entry_guards_free_all(void) entry_guards = NULL; } clear_bridge_list(); - clear_transport_list(); smartlist_free(bridge_list); - smartlist_free(transport_list); bridge_list = NULL; - transport_list = NULL; } diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 984d04a99e..c3905ca21a 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -12,21 +12,6 @@ #ifndef _TOR_CIRCUITBUILD_H #define _TOR_CIRCUITBUILD_H -/** Represents a pluggable transport proxy used by a bridge. */ -typedef struct { - /** SOCKS version: One of PROXY_SOCKS4, PROXY_SOCKS5. */ - int socks_version; - /** Name of pluggable transport protocol */ - char *name; - /** Address of proxy */ - tor_addr_t addr; - /** Port of proxy */ - uint16_t port; - /** Boolean: We are re-parsing our transport list, and we are going to remove - * this one if we don't find it in the list of configured transports. */ - unsigned marked_for_removal : 1; -} transport_t; - 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, @@ -56,13 +41,10 @@ int circuit_all_predicted_ports_handled(time_t now, int *need_uptime, int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info); int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info); void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); -extend_info_t *extend_info_alloc(const char *nickname, const char *digest, +extend_info_t *extend_info_new(const char *nickname, const char *digest, crypto_pk_t *onion_key, const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_router(const routerinfo_t *r, - int for_direct_connect); -extend_info_t *extend_info_from_node(const node_t *node, - int for_direct_connect); +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); const node_t *build_state_get_exit_node(cpath_build_state_t *state); @@ -82,8 +64,6 @@ int getinfo_helper_entry_guards(control_connection_t *conn, void mark_bridge_list(void); void sweep_bridge_list(void); -void mark_transport_list(void); -void sweep_transport_list(void); int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); int node_is_a_configured_bridge(const node_t *node); @@ -151,21 +131,14 @@ void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); /* DOCDOC circuit_build_times_get_bw_scale */ int circuit_build_times_get_bw_scale(networkstatus_t *ns); -void clear_transport_list(void); -int transport_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver); -int transport_add(transport_t *t); -void transport_free(transport_t *transport); -transport_t *transport_new(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver); - /* DOCDOC find_transport_name_by_bridge_addrport */ const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port); - +struct transport_t; int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const transport_t **transport); -transport_t *transport_get_by_name(const char *name); + const struct transport_t **transport); + +int validate_pluggable_transports_config(void); #endif diff --git a/src/or/circuituse.c b/src/or/circuituse.c index c34b698fa1..11d581148a 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -372,7 +372,7 @@ circuit_expire_building(void) } while (0) SET_CUTOFF(general_cutoff, circ_times.timeout_ms); - SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms / 2.0); + SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms); SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (4/3.0)); SET_CUTOFF(cannibalize_cutoff, circ_times.timeout_ms / 2.0); SET_CUTOFF(close_cutoff, circ_times.close_ms); @@ -1570,7 +1570,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, escaped_safe_str_client(conn->socks_request->address)); return -1; } - extend_info = extend_info_alloc(conn->chosen_exit_name+1, + extend_info = extend_info_new(conn->chosen_exit_name+1, digest, NULL, &addr, conn->socks_request->port); } else { diff --git a/src/or/command.c b/src/or/command.c index abf664c1e2..88603c924a 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -808,7 +808,7 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) time_t now = time(NULL); long apparent_skew = 0; - uint32_t my_apparent_addr = 0; + tor_addr_t my_apparent_addr = TOR_ADDR_NULL; if (conn->link_proto < 2) { log_fn(LOG_PROTOCOL_WARN, LD_OR, @@ -868,7 +868,9 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) connection_mark_for_close(TO_CONN(conn)); return; } else if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) { - my_apparent_addr = ntohl(get_uint32(my_addr_ptr)); + tor_addr_from_ipv4n(&my_apparent_addr, get_uint32(my_addr_ptr)); + } else if (my_addr_type == RESOLVED_TYPE_IPV6 && my_addr_len == 16) { + tor_addr_from_ipv6_bytes(&my_apparent_addr, (const char *) my_addr_ptr); } n_other_addrs = (uint8_t) *cp++; @@ -921,7 +923,6 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) /* XXX maybe act on my_apparent_addr, if the source is sufficiently * trustworthy. */ - (void)my_apparent_addr; if (connection_or_set_state_open(conn)<0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but " @@ -931,10 +932,13 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) connection_mark_for_close(TO_CONN(conn)); } else { log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now " - "open, using protocol version %d. Its ID digest is %s", + "open, using protocol version %d. Its ID digest is %s. " + "Our address is apparently %s.", safe_str_client(conn->_base.address), conn->_base.port, (int)conn->link_proto, - hex_str(conn->identity_digest, DIGEST_LEN)); + hex_str(conn->identity_digest, DIGEST_LEN), + tor_addr_is_null(&my_apparent_addr) ? + "<none>" : fmt_and_decorate_addr(&my_apparent_addr)); } assert_connection_ok(TO_CONN(conn),time(NULL)); } diff --git a/src/or/config.c b/src/or/config.c index a369297518..8f79c44cc1 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -130,6 +130,7 @@ static config_abbrev_t _option_abbrevs[] = { { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0}, { "StrictEntryNodes", "StrictNodes", 0, 1}, { "StrictExitNodes", "StrictNodes", 0, 1}, + { "_UseFilteringSSLBufferevents", "UseFilteringSSLBufferevents", 0, 1}, { NULL, NULL, 0, 0}, }; @@ -168,6 +169,9 @@ typedef struct config_var_t { /** An entry for config_vars: "The option <b>name</b> is obsolete." */ #define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL } +#define VPORT(member,conftype,initvalue) \ + VAR(#member, conftype, member ## _lines, initvalue) + /** Array of configuration options. Until we disallow nonstandard * abbreviations, order is significant, since the first matching option will * be chosen first. @@ -201,12 +205,13 @@ static config_var_t _option_vars[] = { V(AuthDirListBadExits, BOOL, "0"), V(AuthDirMaxServersPerAddr, UINT, "2"), V(AuthDirMaxServersPerAuthAddr,UINT, "5"), + V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), V(AutomapHostsOnResolve, BOOL, "0"), V(AutomapHostsSuffixes, CSV, ".onion,.exit"), V(AvoidDiskWrites, BOOL, "0"), - V(BandwidthBurst, MEMUNIT, "10 MB"), - V(BandwidthRate, MEMUNIT, "5 MB"), + V(BandwidthBurst, MEMUNIT, "1 GB"), + V(BandwidthRate, MEMUNIT, "1 GB"), V(BridgeAuthoritativeDir, BOOL, "0"), VAR("Bridge", LINELIST, Bridges, NULL), V(BridgePassword, STRING, NULL), @@ -220,8 +225,10 @@ static config_var_t _option_vars[] = { V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), + V(ClientPreferIPv6ORPort, BOOL, "0"), V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientTransportPlugin, LINELIST, NULL), + V(ClientUseIPv6, BOOL, "0"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), @@ -229,7 +236,7 @@ static config_var_t _option_vars[] = { V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), - V(ControlPort, LINELIST, NULL), + VPORT(ControlPort, LINELIST, NULL), V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), @@ -246,7 +253,7 @@ static config_var_t _option_vars[] = { V(DirListenAddress, LINELIST, NULL), OBSOLETE("DirFetchPeriod"), V(DirPolicy, LINELIST, NULL), - V(DirPort, LINELIST, NULL), + VPORT(DirPort, LINELIST, NULL), V(DirPortFrontPage, FILENAME, NULL), OBSOLETE("DirPostPeriod"), OBSOLETE("DirRecordUsageByCountry"), @@ -259,7 +266,7 @@ static config_var_t _option_vars[] = { V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), V(DynamicDHGroups, BOOL, "0"), - V(DNSPort, LINELIST, NULL), + VPORT(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), @@ -273,6 +280,7 @@ static config_var_t _option_vars[] = { V(ExitPolicy, LINELIST, NULL), V(ExitPolicyRejectPrivate, BOOL, "1"), V(ExitPortStatistics, BOOL, "0"), + V(ExtendAllowPrivateAddresses, BOOL, "0"), V(ExtraInfoStatistics, BOOL, "1"), #if defined (WINCE) @@ -345,7 +353,7 @@ static config_var_t _option_vars[] = { V(NewCircuitPeriod, INTERVAL, "30 seconds"), VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"), V(NATDListenAddress, LINELIST, NULL), - V(NATDPort, LINELIST, NULL), + VPORT(NATDPort, LINELIST, NULL), V(Nickname, STRING, NULL), V(WarnUnsafeSocks, BOOL, "1"), OBSOLETE("NoPublish"), @@ -353,7 +361,7 @@ static config_var_t _option_vars[] = { V(NumCPUs, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), - V(ORPort, LINELIST, NULL), + VPORT(ORPort, LINELIST, NULL), V(OutboundBindAddress, STRING, NULL), V(PathBiasCircThreshold, INT, "-1"), @@ -406,7 +414,7 @@ static config_var_t _option_vars[] = { V(ShutdownWaitLength, INTERVAL, "30 seconds"), V(SocksListenAddress, LINELIST, NULL), V(SocksPolicy, LINELIST, NULL), - V(SocksPort, LINELIST, NULL), + VPORT(SocksPort, LINELIST, NULL), V(SocksTimeout, INTERVAL, "2 minutes"), OBSOLETE("StatusFetchPeriod"), V(StrictNodes, BOOL, "0"), @@ -419,7 +427,7 @@ static config_var_t _option_vars[] = { V(TrackHostExitsExpire, INTERVAL, "30 minutes"), OBSOLETE("TrafficShaping"), V(TransListenAddress, LINELIST, NULL), - V(TransPort, LINELIST, NULL), + VPORT(TransPort, LINELIST, NULL), V(TunnelDirConns, BOOL, "1"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), @@ -442,7 +450,7 @@ static config_var_t _option_vars[] = { VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), V(VirtualAddrNetwork, STRING, "127.192.0.0/10"), V(WarnPlaintextPorts, CSV, "23,109,110,143"), - V(_UseFilteringSSLBufferevents, BOOL, "0"), + V(UseFilteringSSLBufferevents, BOOL, "0"), VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), @@ -452,7 +460,7 @@ static config_var_t _option_vars[] = { VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"), V(VoteOnHidServDirectoriesV2, BOOL, "1"), - V(_UsingTestNetworkDefaults, BOOL, "0"), + VAR("___UsingTestNetworkDefaults", BOOL, _UsingTestNetworkDefaults, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -460,7 +468,7 @@ static config_var_t _option_vars[] = { /** Override default values with these if the user sets the TestingTorNetwork * option. */ static const config_var_t testing_tor_network_defaults[] = { - V(ServerDNSAllowBrokenConfig, BOOL, "1"), + V(ServerDNSAllowBrokenConfig, BOOL, "1"), V(DirAllowPrivateAddresses, BOOL, "1"), V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), @@ -470,6 +478,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(ClientRejectInternalAddresses, BOOL, "0"), V(CountPrivateBandwidth, BOOL, "1"), V(ExitPolicyRejectPrivate, BOOL, "0"), + V(ExtendAllowPrivateAddresses, BOOL, "1"), V(V3AuthVotingInterval, INTERVAL, "5 minutes"), V(V3AuthVoteDelay, INTERVAL, "20 seconds"), V(V3AuthDistDelay, INTERVAL, "20 seconds"), @@ -479,7 +488,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), - V(_UsingTestNetworkDefaults, BOOL, "1"), + VAR("___UsingTestNetworkDefaults", BOOL, _UsingTestNetworkDefaults, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -622,7 +631,7 @@ static int parse_dir_server_line(const char *line, dirinfo_type_t required_type, int validate_only); static void port_cfg_free(port_cfg_t *port); -static int parse_ports(const or_options_t *options, int validate_only, +static int parse_ports(or_options_t *options, int validate_only, char **msg_out, int *n_ports_out); static int check_server_ports(const smartlist_t *ports, const or_options_t *options); @@ -710,7 +719,7 @@ get_dirportfrontpage(void) /** Allocate an empty configuration object of a given format type. */ static void * -config_alloc(const config_format_t *fmt) +config_new(const config_format_t *fmt) { void *opts = tor_malloc_zero(fmt->size); *(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic; @@ -1167,7 +1176,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) /* Open /dev/pf before dropping privileges. */ - if (options->TransPort) { + if (options->TransPort_set) { if (get_pf_socket() < 0) { *msg = tor_strdup("Unable to open /dev/pf for transparent proxy."); goto rollback; @@ -1549,7 +1558,7 @@ options_act(const or_options_t *old_options) monitor_owning_controller_process(options->OwningControllerProcess); /* reload keys as needed for rendezvous services. */ - if (rend_service_load_keys()<0) { + if (rend_service_load_all_keys()<0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); return -1; } @@ -1650,7 +1659,7 @@ options_act(const or_options_t *old_options) int was_relay = 0; if (options->BridgeRelay) { time_t int_start = time(NULL); - if (config_lines_eq(old_options->ORPort, options->ORPort)) { + if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) { int_start += RELAY_BRIDGE_STATS_DELAY; was_relay = 1; } @@ -1734,7 +1743,7 @@ options_act(const or_options_t *old_options) } else { options->DirReqStatistics = 0; /* Don't warn Tor clients, they don't use statistics */ - if (options->ORPort) + if (options->ORPort_set) log_notice(LD_CONFIG, "Configured to measure directory request " "statistics, but no GeoIP database found. " "Please specify a GeoIP database using the " @@ -3110,7 +3119,7 @@ options_dup(const config_format_t *fmt, const or_options_t *old) int i; config_line_t *line; - newopts = config_alloc(fmt); + newopts = config_new(fmt); for (i=0; fmt->vars[i].name; ++i) { if (fmt->vars[i].type == CONFIG_TYPE_LINELIST_S) continue; @@ -3135,7 +3144,7 @@ options_dup(const config_format_t *fmt, const or_options_t *old) or_options_t * options_new(void) { - return config_alloc(&options_format); + return config_new(&options_format); } /** Set <b>options</b> to hold reasonable defaults for most options. @@ -3181,7 +3190,7 @@ config_dump(const config_format_t *fmt, const void *default_options, char *msg = NULL; if (defaults == NULL) { - defaults = defaults_tmp = config_alloc(fmt); + defaults = defaults_tmp = config_new(fmt); config_init(fmt, defaults_tmp); } @@ -3448,7 +3457,8 @@ options_validate(or_options_t *old_options, or_options_t *options, "Tor will still run, but probably won't do anything."); #ifndef USE_TRANSPARENT - if (options->TransPort || options->TransListenAddress) + /* XXXX024 I think we can remove this TransListenAddress */ + if (options->TransPort_set || options->TransListenAddress) REJECT("TransPort and TransListenAddress are disabled in this build."); #endif @@ -3518,10 +3528,10 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->AuthoritativeDir && !options->DirPort) + if (options->AuthoritativeDir && !options->DirPort_set) REJECT("Running as authoritative directory, but no DirPort set."); - if (options->AuthoritativeDir && !options->ORPort) + if (options->AuthoritativeDir && !options->ORPort_set) REJECT("Running as authoritative directory, but no ORPort set."); if (options->AuthoritativeDir && options->ClientOnly) @@ -3708,11 +3718,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "PublishServerDescriptor line."); } - if (options->BridgeRelay && options->DirPort) { + if (options->BridgeRelay && options->DirPort_set) { log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " "DirPort"); - config_free_lines(options->DirPort); - options->DirPort = NULL; + config_free_lines(options->DirPort_lines); + options->DirPort_lines = NULL; + options->DirPort_set = 0; } if (options->MinUptimeHidServDirectoryV2 < 0) { @@ -3987,7 +3998,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->ControlPort && !options->HashedControlPassword && + if (options->ControlPort_set && !options->HashedControlPassword && !options->HashedControlSessionPassword && !options->CookieAuthentication) { log_warn(LD_CONFIG, "ControlPort is open, but no authentication method " @@ -4052,7 +4063,7 @@ options_validate(or_options_t *old_options, or_options_t *options, log_notice(LD_GENERAL, "Tor is not configured as a relay but you specified" " a ServerTransportPlugin line (%s). The ServerTransportPlugin " "line will be ignored.", - esc_for_log(options->ServerTransportPlugin->value)); + escaped(options->ServerTransportPlugin->value)); } if (options->ConstrainedSockets) { @@ -4067,7 +4078,7 @@ options_validate(or_options_t *old_options, or_options_t *options, MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER); return -1; } - if (options->DirPort) { + if (options->DirPort_set) { /* Providing cached directory entries while system TCP buffers are scarce * will exacerbate the socket errors. Suggest that this be disabled. */ COMPLAIN("You have requested constrained socket buffers while also " @@ -4226,7 +4237,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } }); - if (options->BridgeRelay == 1 && ! options->ORPort) + if (options->BridgeRelay == 1 && ! options->ORPort_set) REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " "combination."); @@ -4326,7 +4337,7 @@ options_transition_affects_workers(const or_options_t *old_options, { if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || old_options->NumCPUs != new_options->NumCPUs || - !config_lines_eq(old_options->ORPort, new_options->ORPort) || + !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) || old_options->ServerDNSSearchDomains != new_options->ServerDNSSearchDomains || old_options->_SafeLogging != new_options->_SafeLogging || @@ -4356,8 +4367,10 @@ options_transition_affects_descriptor(const or_options_t *old_options, !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || old_options->ExitPolicyRejectPrivate != new_options->ExitPolicyRejectPrivate || - !config_lines_eq(old_options->ORPort, new_options->ORPort) || - !config_lines_eq(old_options->DirPort, new_options->DirPort) || + !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 != @@ -4864,12 +4877,11 @@ config_register_addressmaps(const or_options_t *options) { smartlist_t *elts; config_line_t *opt; - char *from, *to; + const char *from, *to, *msg; addressmap_clear_configured(); elts = smartlist_new(); for (opt = options->AddressMap; opt; opt = opt->next) { - int from_wildcard = 0, to_wildcard = 0; smartlist_split_string(elts, opt->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); if (smartlist_len(elts) < 2) { @@ -4887,11 +4899,39 @@ config_register_addressmaps(const or_options_t *options) goto cleanup; } - if (!strcmp(to, "*") || !strcmp(from, "*")) { - log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from " - "or to *. Ignoring.",opt->value); + if (addressmap_register_auto(from, to, 0, ADDRMAPSRC_TORRC, &msg) < 0) { + log_warn(LD_CONFIG,"MapAddress '%s' failed: %s. Ignoring.", opt->value, + msg); goto cleanup; } + + if (smartlist_len(elts) > 2) + log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); + + cleanup: + SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); + smartlist_clear(elts); + } + smartlist_free(elts); +} + +/** As addressmap_register(), but detect the wildcarded status of "from" and + * "to", and do not steal a reference to <b>to</b>. */ +/* XXXX024 move to connection_edge.c */ +int +addressmap_register_auto(const char *from, const char *to, + time_t expires, + addressmap_entry_source_t addrmap_source, + const char **msg) +{ + int from_wildcard = 0, to_wildcard = 0; + + *msg = "whoops, forgot the error message"; + if (1) { + if (!strcmp(to, "*") || !strcmp(from, "*")) { + *msg = "can't remap from or to *"; + return -1; + } /* Detect asterisks in expressions of type: '*.example.com' */ if (!strncmp(from,"*.",2)) { from += 2; @@ -4903,30 +4943,20 @@ config_register_addressmaps(const or_options_t *options) } if (to_wildcard && !from_wildcard) { - log_warn(LD_CONFIG, - "Skipping invalid argument '%s' to MapAddress: " - "can only use wildcard (i.e. '*.') if 'from' address " - "uses wildcard also", opt->value); - goto cleanup; + *msg = "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also"; + return -1; } if (address_is_invalid_destination(to, 1)) { - log_warn(LD_CONFIG, - "Skipping invalid argument '%s' to MapAddress", opt->value); - goto cleanup; + *msg = "destination is invalid"; + return -1; } - addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC, + addressmap_register(from, tor_strdup(to), expires, addrmap_source, from_wildcard, to_wildcard); - - if (smartlist_len(elts) > 2) - log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); - - cleanup: - SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); - smartlist_clear(elts); } - smartlist_free(elts); + return 0; } /** @@ -5485,8 +5515,8 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type, fingerprint = smartlist_join_strings(items, "", 0, NULL); if (strlen(fingerprint) != HEX_DIGEST_LEN) { - log_warn(LD_CONFIG, "Key digest for DirServer is wrong length %d.", - (int)strlen(fingerprint)); + log_warn(LD_CONFIG, "Key digest '%s' for DirServer is wrong length %d.", + fingerprint, (int)strlen(fingerprint)); goto err; } if (!strcmp(fingerprint, "E623F7625FBE0C87820F11EC5F6D5377ED816294")) { @@ -5643,13 +5673,13 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) */ static int parse_port_config(smartlist_t *out, - const config_line_t *ports, - const config_line_t *listenaddrs, - const char *portname, - int listener_type, - const char *defaultaddr, - int defaultport, - unsigned flags) + const config_line_t *ports, + const config_line_t *listenaddrs, + const char *portname, + int listener_type, + const char *defaultaddr, + int defaultport, + unsigned flags) { smartlist_t *elts; int retval = -1; @@ -5660,6 +5690,7 @@ parse_port_config(smartlist_t *out, const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; const unsigned allow_spurious_listenaddr = flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; + int got_zero_port=0, got_nonzero_port=0; /* FooListenAddress is deprecated; let's make it work like it used to work, * though. */ @@ -5687,7 +5718,7 @@ parse_port_config(smartlist_t *out, if (mainport == 0) { if (allow_spurious_listenaddr) - return 1; + return 1; /*DOCDOC*/ log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used", portname, portname); return -1; @@ -5912,6 +5943,11 @@ parse_port_config(smartlist_t *out, } SMARTLIST_FOREACH_END(elt); } + if (port) + got_nonzero_port = 1; + else + got_zero_port = 1; + if (out && port) { port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); tor_addr_copy(&cfg->addr, &addr); @@ -5938,6 +5974,13 @@ parse_port_config(smartlist_t *out, warn_nonlocal_client_ports(out, portname); } + if (got_zero_port && got_nonzero_port) { + log_warn(LD_CONFIG, "You specified a nonzero %sPort along with '%sPort 0' " + "in the same configuration. Did you mean to disable %sPort or " + "not?", portname, portname, portname); + goto err; + } + retval = 0; err: SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); @@ -5968,16 +6011,34 @@ parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, return 0; } +/** Return the number of ports which are actually going to listen with type + * <b>listenertype</b>. Do not count no_listen ports. Do not count unix + * sockets. */ +static int +count_real_listeners(const smartlist_t *ports, int listenertype) +{ + int n = 0; + SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { + if (port->no_listen || port->is_unix_addr) + continue; + if (port->type != listenertype) + continue; + ++n; + } SMARTLIST_FOREACH_END(port); + return n; +} + /** Parse all client port types (Socks, DNS, Trans, NATD) from - * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of - * ports that are listed and return 0. On failure, set *<b>msg</b> to a + * <b>options</b>. On success, set *<b>n_ports_out</b> to the number + * of ports that are listed, update the *Port_set values in + * <b>options</b>, and return 0. On failure, set *<b>msg</b> to a * description of the problem and return -1. * * If <b>validate_only</b> is false, set configured_client_ports to the * new list of ports parsed from <b>options</b>. **/ static int -parse_ports(const or_options_t *options, int validate_only, +parse_ports(or_options_t *options, int validate_only, char **msg, int *n_ports_out) { smartlist_t *ports; @@ -5988,7 +6049,7 @@ parse_ports(const or_options_t *options, int validate_only, *n_ports_out = 0; if (parse_port_config(ports, - options->SocksPort, options->SocksListenAddress, + options->SocksPort_lines, options->SocksListenAddress, "Socks", CONN_TYPE_AP_LISTENER, "127.0.0.1", 9050, CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) { @@ -5996,26 +6057,26 @@ parse_ports(const or_options_t *options, int validate_only, goto err; } if (parse_port_config(ports, - options->DNSPort, options->DNSListenAddress, - "DNS", CONN_TYPE_AP_DNS_LISTENER, - "127.0.0.1", 0, - CL_PORT_WARN_NONLOCAL) < 0) { + options->DNSPort_lines, options->DNSListenAddress, + "DNS", CONN_TYPE_AP_DNS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); goto err; } if (parse_port_config(ports, - options->TransPort, options->TransListenAddress, - "Trans", CONN_TYPE_AP_TRANS_LISTENER, - "127.0.0.1", 0, - CL_PORT_WARN_NONLOCAL) < 0) { + options->TransPort_lines, options->TransListenAddress, + "Trans", CONN_TYPE_AP_TRANS_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration"); goto err; } if (parse_port_config(ports, - options->NATDPort, options->NATDListenAddress, - "NATD", CONN_TYPE_AP_NATD_LISTENER, - "127.0.0.1", 0, - CL_PORT_WARN_NONLOCAL) < 0) { + options->NATDPort_lines, options->NATDListenAddress, + "NATD", CONN_TYPE_AP_NATD_LISTENER, + "127.0.0.1", 0, + CL_PORT_WARN_NONLOCAL) < 0) { *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration"); goto err; } @@ -6028,7 +6089,8 @@ parse_ports(const or_options_t *options, int validate_only, control_port_flags |= CL_PORT_FORBID_NONLOCAL; if (parse_port_config(ports, - options->ControlPort, options->ControlListenAddress, + options->ControlPort_lines, + options->ControlListenAddress, "Control", CONN_TYPE_CONTROL_LISTENER, "127.0.0.1", 0, control_port_flags) < 0) { @@ -6045,7 +6107,7 @@ parse_ports(const or_options_t *options, int validate_only, } if (! options->ClientOnly) { if (parse_port_config(ports, - options->ORPort, options->ORListenAddress, + options->ORPort_lines, options->ORListenAddress, "OR", CONN_TYPE_OR_LISTENER, "0.0.0.0", 0, CL_PORT_SERVER_OPTIONS) < 0) { @@ -6053,7 +6115,7 @@ parse_ports(const or_options_t *options, int validate_only, goto err; } if (parse_port_config(ports, - options->DirPort, options->DirListenAddress, + options->DirPort_lines, options->DirListenAddress, "Dir", CONN_TYPE_DIR_LISTENER, "0.0.0.0", 0, CL_PORT_SERVER_OPTIONS) < 0) { @@ -6069,6 +6131,25 @@ parse_ports(const or_options_t *options, int validate_only, *n_ports_out = smartlist_len(ports); + retval = 0; + + /* Update the *Port_set options. The !! here is to force a boolean out of + an integer. */ + options->ORPort_set = + !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER); + options->SocksPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); + options->TransPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER); + options->NATDPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER); + options->ControlPort_set = + !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER); + options->DirPort_set = + !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER); + options->DNSPort_set = + !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER); + if (!validate_only) { if (configured_ports) { SMARTLIST_FOREACH(configured_ports, @@ -6079,7 +6160,6 @@ parse_ports(const or_options_t *options, int validate_only, ports = NULL; /* prevent free below. */ } - retval = 0; err: if (ports) { SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); @@ -6620,7 +6700,7 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); tor_check_libevent_version(tor_libevent_get_method(), - get_options()->ORPort != NULL, + server_mode(get_options()), &badness); if (badness) { const char *v = tor_libevent_get_version_str(); @@ -7174,6 +7254,43 @@ remove_file_if_very_old(const char *fname, time_t now) } } +/** 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. */ +smartlist_t * +get_list_of_ports_to_forward(void) +{ + smartlist_t *ports_to_forward = smartlist_new(); + int port = 0; + + /** XXX TODO tor-fw-helper does not support forwarding ports to + other hosts than the local one. If the user is binding to a + different IP address, tor-fw-helper won't work. */ + port = router_get_advertised_or_port(get_options()); /* Get ORPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + port = router_get_advertised_dir_port(get_options(), 0); /* Get DirPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + /* Get ports of transport proxies */ + { + smartlist_t *transport_ports = get_transport_proxy_ports(); + if (transport_ports) { + smartlist_add_all(ports_to_forward, transport_ports); + smartlist_free(transport_ports); + } + } + + if (!smartlist_len(ports_to_forward)) { + smartlist_free(ports_to_forward); + ports_to_forward = NULL; + } + + return ports_to_forward; +} + /** Helper to implement GETINFO functions about configuration variables (not * their values). Given a "config/names" question, set *<b>answer</b> to a * new string describing the supported configuration variables and their @@ -7191,6 +7308,9 @@ getinfo_helper_config(control_connection_t *conn, for (i = 0; _option_vars[i].name; ++i) { const config_var_t *var = &_option_vars[i]; const char *type; + /* don't tell controller about triple-underscore options */ + if (!strncmp(_option_vars[i].name, "___", 3)) + continue; switch (var->type) { case CONFIG_TYPE_STRING: type = "String"; break; case CONFIG_TYPE_FILENAME: type = "Filename"; break; @@ -7220,6 +7340,20 @@ getinfo_helper_config(control_connection_t *conn, *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); + } else if (!strcmp(question, "config/defaults")) { + smartlist_t *sl = smartlist_new(); + int i; + for (i = 0; _option_vars[i].name; ++i) { + const config_var_t *var = &_option_vars[i]; + if (var->initvalue != NULL) { + char *val = esc_for_log(var->initvalue); + smartlist_add_asprintf(sl, "%s %s\n",var->name,val); + tor_free(val); + } + } + *answer = smartlist_join_strings(sl, "", 0, NULL); + SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); + smartlist_free(sl); } return 0; } diff --git a/src/or/config.h b/src/or/config.h index ce7e319746..d207965849 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -82,6 +82,8 @@ void save_transport_to_state(const char *transport_name, const tor_addr_t *addr, uint16_t port); char *get_stored_bindaddr_for_server_transport(const char *transport); +smartlist_t *get_list_of_ports_to_forward(void); + int getinfo_helper_config(control_connection_t *conn, const char *question, char **answer, const char **errmsg); @@ -96,6 +98,11 @@ or_options_t *options_new(void); #endif void config_register_addressmaps(const or_options_t *options); +/* XXXX024 move to connection_edge.h */ +int addressmap_register_auto(const char *from, const char *to, + time_t expires, + addressmap_entry_source_t addrmap_source, + const char **msg); #endif diff --git a/src/or/connection.c b/src/or/connection.c index 364e4912da..d8f5d875c8 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -34,6 +34,7 @@ #include "rendcommon.h" #include "rephist.h" #include "router.h" +#include "transports.h" #include "routerparse.h" #ifdef USE_BUFFEREVENTS @@ -238,7 +239,16 @@ dir_connection_new(int socket_family) } /** Allocate and return a new or_connection_t, initialized as by - * connection_init(). */ + * connection_init(). + * + * Set timestamp_last_added_nonpadding to now. + * + * Assign a pseudorandom next_circ_id between 0 and 2**15. + * + * Initialize active_circuit_pqueue. + * + * Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick. + */ or_connection_t * or_connection_new(int socket_family) { @@ -256,7 +266,10 @@ or_connection_new(int socket_family) } /** Allocate and return a new entry_connection_t, initialized as by - * connection_init(). */ + * connection_init(). + * + * Allocate space to store the socks_request. + */ entry_connection_t * entry_connection_new(int type, int socket_family) { @@ -338,14 +351,11 @@ connection_new(int type, int socket_family) /** Initializes conn. (you must call connection_add() to link it into the main * array). * + * Set conn-\>magic to the correct value. + * * Set conn-\>type to <b>type</b>. Set conn-\>s and conn-\>conn_array_index to * -1 to signify they are not yet assigned. * - * If conn is not a listener type, allocate buffers for it. If it's - * an AP type, allocate space to store the socks_request. - * - * Assign a pseudorandom next_circ_id between 0 and 2**15. - * * Initialize conn's timestamps to now. */ static void @@ -1089,7 +1099,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, * nmap does). We want to detect that, and not go on with the connection. */ static int -check_sockaddr(struct sockaddr *sa, int len, int level) +check_sockaddr(const struct sockaddr *sa, int len, int level) { int ok = 1; @@ -1202,11 +1212,6 @@ connection_handle_listener_read(connection_t *conn, int new_type) return 0; } - if (check_sockaddr_family_match(remote->sa_family, conn) < 0) { - tor_close_socket(news); - return 0; - } - tor_addr_from_sockaddr(&addr, remote, &port); /* process entrance policies here, before we even create the connection */ @@ -2033,9 +2038,9 @@ connection_mark_all_noncontrol_connections(void) /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 * otherwise. * Right now this just checks if it's an internal IP address or an - * internal connection. We also check if the connection uses pluggable - * transports, since we should then limit it even if it comes from an - * internal IP address. */ + * internal connection. We also should, but don't, check if the connection + * uses pluggable transports, since we should then limit it even if it + * comes from an internal IP address. */ static int connection_is_rate_limited(connection_t *conn) { @@ -3360,13 +3365,6 @@ connection_flush(connection_t *conn) return connection_handle_write(conn, 1); } -/** OpenSSL TLS record size is 16383; this is close. The goal here is to - * push data out as soon as we know there's enough for a TLS record, so - * during periods of high load we won't read entire megabytes from - * input before pushing any data out. It also has the feature of not - * growing huge outbufs unless something is slow. */ -#define MIN_TLS_FLUSHLEN 15872 - /** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s * outbuf, and ask it to start writing. * @@ -3375,10 +3373,9 @@ connection_flush(connection_t *conn) * negative, this is the last data to be compressed, and the connection's zlib * state should be flushed. * - * If it's an OR conn and an entire TLS record is ready, then try to - * flush the record now. Similarly, if it's a local control connection - * and a 64k chunk is ready, try to flush it all, so we don't end up with - * many megabytes of controller info queued at once. + * If it's a local control connection and a 64k chunk is ready, try to flush + * it all, so we don't end up with many megabytes of controller info queued at + * once. */ void _connection_write_to_buf_impl(const char *string, size_t len, @@ -3446,7 +3443,6 @@ _connection_write_to_buf_impl(const char *string, size_t len, if (zlib) { conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; } else { - ssize_t extra = 0; conn->outbuf_flushlen += len; /* Should we try flushing the outbuf now? */ @@ -3456,14 +3452,7 @@ _connection_write_to_buf_impl(const char *string, size_t len, return; } - if (conn->type == CONN_TYPE_OR && - conn->outbuf_flushlen-len < MIN_TLS_FLUSHLEN && - conn->outbuf_flushlen >= MIN_TLS_FLUSHLEN) { - /* We just pushed outbuf_flushlen to MIN_TLS_FLUSHLEN or above; - * we can send out a full TLS frame now if we like. */ - extra = conn->outbuf_flushlen - MIN_TLS_FLUSHLEN; - conn->outbuf_flushlen = MIN_TLS_FLUSHLEN; - } else if (conn->type == CONN_TYPE_CONTROL && + if (conn->type == CONN_TYPE_CONTROL && !connection_is_rate_limited(conn) && conn->outbuf_flushlen-len < 1<<16 && conn->outbuf_flushlen >= 1<<16) { @@ -3483,10 +3472,6 @@ _connection_write_to_buf_impl(const char *string, size_t len, } return; } - if (extra) { - conn->outbuf_flushlen += extra; - connection_start_writing(conn); - } } } diff --git a/src/or/connection_or.c b/src/or/connection_or.c index d016387935..02345f98a2 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -327,7 +327,7 @@ var_cell_t * var_cell_new(uint16_t payload_len) { size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len; - var_cell_t *cell = tor_malloc(size); + var_cell_t *cell = tor_malloc_zero(size); cell->payload_len = payload_len; cell->command = 0; cell->circ_id = 0; @@ -1152,7 +1152,7 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving) #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(TO_CONN(conn))) { - const int filtering = get_options()->_UseFilteringSSLBufferevents; + const int filtering = get_options()->UseFilteringSSLBufferevents; struct bufferevent *b = tor_tls_init_bufferevent(conn->tls, conn->_base.bufev, conn->_base.s, receiving, filtering); @@ -1540,7 +1540,7 @@ connection_or_client_learned_peer_id(or_connection_t *conn, return -1; } if (authdir_mode_tests_reachability(options)) { - dirserv_orconn_tls_done(conn->_base.address, conn->_base.port, + dirserv_orconn_tls_done(&conn->_base.addr, conn->_base.port, (const char*)peer_id); } @@ -1988,12 +1988,19 @@ connection_or_send_netinfo(or_connection_t *conn) if ((public_server_mode(get_options()) || !conn->is_outgoing) && (me = router_get_my_routerinfo())) { tor_addr_t my_addr; - *out++ = 1; /* only one address is supported. */ + *out++ = 1 + !tor_addr_is_null(&me->ipv6_addr); tor_addr_from_ipv4h(&my_addr, me->addr); len = append_address_to_payload(out, &my_addr); if (len < 0) return -1; + out += len; + + if (!tor_addr_is_null(&me->ipv6_addr)) { + len = append_address_to_payload(out, &me->ipv6_addr); + if (len < 0) + return -1; + } } else { *out = 0; } diff --git a/src/or/control.c b/src/or/control.c index ce571f99f3..4a0f8d70a1 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1243,6 +1243,27 @@ handle_control_saveconf(control_connection_t *conn, uint32_t len, return 0; } +struct signal_t { + int sig; + const char *signal_name; +}; + +static const struct signal_t signal_table[] = { + { SIGHUP, "RELOAD" }, + { SIGHUP, "HUP" }, + { SIGINT, "SHUTDOWN" }, + { SIGUSR1, "DUMP" }, + { SIGUSR1, "USR1" }, + { SIGUSR2, "DEBUG" }, + { SIGUSR2, "USR2" }, + { SIGTERM, "HALT" }, + { SIGTERM, "TERM" }, + { SIGTERM, "INT" }, + { SIGNEWNYM, "NEWNYM" }, + { SIGCLEARDNSCACHE, "CLEARDNSCACHE"}, + { 0, NULL }, +}; + /** Called when we get a SIGNAL command. React to the provided signal, and * report success or failure. (If the signal results in a shutdown, success * may not be reported.) */ @@ -1250,7 +1271,8 @@ static int handle_control_signal(control_connection_t *conn, uint32_t len, const char *body) { - int sig; + int sig = -1; + int i; int n = 0; char *s; @@ -1259,27 +1281,19 @@ handle_control_signal(control_connection_t *conn, uint32_t len, while (body[n] && ! TOR_ISSPACE(body[n])) ++n; s = tor_strndup(body, n); - if (!strcasecmp(s, "RELOAD") || !strcasecmp(s, "HUP")) - sig = SIGHUP; - else if (!strcasecmp(s, "SHUTDOWN") || !strcasecmp(s, "INT")) - sig = SIGINT; - else if (!strcasecmp(s, "DUMP") || !strcasecmp(s, "USR1")) - sig = SIGUSR1; - else if (!strcasecmp(s, "DEBUG") || !strcasecmp(s, "USR2")) - sig = SIGUSR2; - else if (!strcasecmp(s, "HALT") || !strcasecmp(s, "TERM")) - sig = SIGTERM; - else if (!strcasecmp(s, "NEWNYM")) - sig = SIGNEWNYM; - else if (!strcasecmp(s, "CLEARDNSCACHE")) - sig = SIGCLEARDNSCACHE; - else { + + for (i = 0; signal_table[i].signal_name != NULL; ++i) { + if (!strcasecmp(s, signal_table[i].signal_name)) { + sig = signal_table[i].sig; + break; + } + } + + if (sig < 0) connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n", s); - sig = -1; - } tor_free(s); - if (sig<0) + if (sig < 0) return 0; send_control_done(conn); @@ -1366,9 +1380,18 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len, smartlist_add_asprintf(reply, "250-%s=%s", address, to); } } else { - addressmap_register(from, tor_strdup(to), 1, - ADDRMAPSRC_CONTROLLER, 0, 0); - smartlist_add_asprintf(reply, "250-%s", line); + const char *msg; + if (addressmap_register_auto(from, to, 1, + ADDRMAPSRC_CONTROLLER, &msg) < 0) { + smartlist_add_asprintf(reply, + "512-syntax error: invalid address mapping " + " '%s': %s", line, msg); + log_warn(LD_CONTROL, + "Skipping invalid argument '%s' in MapAddress msg: %s", + line, msg); + } else { + smartlist_add_asprintf(reply, "250-%s", line); + } } } else { smartlist_add_asprintf(reply, "512-syntax error: mapping '%s' is " @@ -1431,6 +1454,16 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, *answer = smartlist_join_strings(event_names, " ", 0, NULL); smartlist_free(event_names); + } else if (!strcmp(question, "signal/names")) { + smartlist_t *signal_names = smartlist_new(); + int j; + for (j = 0; signal_table[j].signal_name != NULL; ++j) { + smartlist_add(signal_names, (char*)signal_table[j].signal_name); + } + + *answer = smartlist_join_strings(signal_names, " ", 0, NULL); + + smartlist_free(signal_names); } else if (!strcmp(question, "features/names")) { *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS"); } else if (!strcmp(question, "address")) { @@ -2121,10 +2154,14 @@ static const getinfo_item_t getinfo_items[] = { PREFIX("config/", config, "Current configuration values."), DOC("config/names", "List of configuration options, types, and documentation."), + DOC("config/defaults", + "List of default values for configuration options. " + "See also config/names"), ITEM("info/names", misc, "List of GETINFO options, types, and documentation."), ITEM("events/names", misc, "Events that the controller can ask for with SETEVENTS."), + ITEM("signal/names", misc, "Signal names recognized by the SIGNAL command"), ITEM("features/names", misc, "What arguments can USEFEATURE take?"), PREFIX("desc/id/", dir, "Router descriptors by ID."), PREFIX("desc/name/", dir, "Router descriptors by nickname."), diff --git a/src/or/directory.c b/src/or/directory.c index f58aab256b..d22a8b877d 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -58,7 +58,6 @@ static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, - int supports_conditional_consensus, time_t if_modified_since); static int directory_handle_command(dir_connection_t *conn); static int body_is_plausible(const char *body, size_t body_len, int purpose); @@ -89,8 +88,6 @@ static void directory_initiate_command_rend(const char *address, const tor_addr_t *addr, uint16_t or_port, uint16_t dir_port, - int supports_conditional_consensus, - int supports_begindir, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, @@ -227,16 +224,9 @@ router_supports_extrainfo(const char *identity_digest, int is_authority) if (node && node->ri) { if (node->ri->caches_extra_info) return 1; - if (is_authority && node->ri->platform && - tor_version_as_new_as(node->ri->platform, - "Tor 0.2.0.0-alpha-dev (r10070)")) - return 1; } if (is_authority) { - const routerstatus_t *rs = - router_get_consensus_status_by_id(identity_digest); - if (rs && rs->version_supports_extrainfo_upload) - return 1; + return 1; } return 0; } @@ -431,8 +421,6 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, * the behavior supported by our oldest bridge; see for example * any_bridges_dont_support_microdescriptors(). */ - /* XXX024 Not all bridges handle conditional consensus downloading, - * so, for now, never assume the server supports that. -PP */ const node_t *node = choose_random_entry(NULL); if (node && node->ri) { /* every bridge has a routerinfo. */ @@ -440,9 +428,8 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, routerinfo_t *ri = node->ri; node_get_addr(node, &addr); directory_initiate_command(ri->address, &addr, - ri->or_port, 0, - 0, /* don't use conditional consensus url */ - 1, ri->cache_info.identity_digest, + ri->or_port, 0/*no dirport*/, + ri->cache_info.identity_digest, dir_purpose, router_purpose, 0, resource, NULL, 0, if_modified_since); @@ -596,8 +583,6 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, directory_initiate_command_rend(address, &addr, status->or_port, status->dir_port, - status->version_supports_conditional_consensus, - status->version_supports_begindir, status->identity_digest, dir_purpose, router_purpose, anonymized_connection, resource, @@ -855,16 +840,14 @@ directory_command_should_use_begindir(const or_options_t *options, void directory_initiate_command(const char *address, const tor_addr_t *_addr, uint16_t or_port, uint16_t dir_port, - int supports_conditional_consensus, - int supports_begindir, const char *digest, + const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, const char *resource, const char *payload, size_t payload_len, time_t if_modified_since) { directory_initiate_command_rend(address, _addr, or_port, dir_port, - supports_conditional_consensus, - supports_begindir, digest, dir_purpose, + digest, dir_purpose, router_purpose, anonymized_connection, resource, payload, payload_len, if_modified_since, NULL); @@ -889,8 +872,7 @@ is_sensitive_dir_purpose(uint8_t dir_purpose) static void directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, uint16_t or_port, uint16_t dir_port, - int supports_conditional_consensus, - int supports_begindir, const char *digest, + const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, const char *resource, @@ -901,8 +883,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, dir_connection_t *conn; const or_options_t *options = get_options(); int socket_error = 0; - int use_begindir = supports_begindir && - directory_command_should_use_begindir(options, _addr, + int use_begindir = directory_command_should_use_begindir(options, _addr, or_port, router_purpose, anonymized_connection); tor_addr_t addr; @@ -979,7 +960,6 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 1, resource, payload, payload_len, - supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); /* writable indicates finish, readable indicates broken link, @@ -1024,7 +1004,6 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 0, resource, payload, payload_len, - supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); @@ -1074,8 +1053,7 @@ _compare_strs(const void **a, const void **b) * If 'resource' is provided, it is the name of a consensus flavor to request. */ static char * -directory_get_consensus_url(int supports_conditional_consensus, - const char *resource) +directory_get_consensus_url(const char *resource) { char *url = NULL; const char *hyphen, *flavor; @@ -1087,7 +1065,7 @@ directory_get_consensus_url(int supports_conditional_consensus, hyphen = "-"; } - if (supports_conditional_consensus) { + { char *authority_id_list; smartlist_t *authority_digests = smartlist_new(); @@ -1112,9 +1090,6 @@ directory_get_consensus_url(int supports_conditional_consensus, SMARTLIST_FOREACH(authority_digests, char *, cp, tor_free(cp)); smartlist_free(authority_digests); tor_free(authority_id_list); - } else { - tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s.z", - hyphen, flavor); } return url; } @@ -1126,7 +1101,6 @@ static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, - int supports_conditional_consensus, time_t if_modified_since) { char proxystring[256]; @@ -1189,8 +1163,7 @@ directory_send_command(dir_connection_t *conn, /* resource is optional. If present, it's a flavor name */ tor_assert(!payload); httpcommand = "GET"; - url = directory_get_consensus_url(supports_conditional_consensus, - resource); + url = directory_get_consensus_url(resource); log_info(LD_DIR, "Downloading consensus from %s using %s", hoststring, url); break; @@ -2646,7 +2619,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, if ((header = http_get_header(headers, "If-Modified-Since: "))) { struct tm tm; if (parse_http_time(header, &tm) == 0) { - if_modified_since = tor_timegm(&tm); + if (tor_timegm(&tm, &if_modified_since)<0) + if_modified_since = 0; } /* The correct behavior on a malformed If-Modified-Since header is to * act as if no If-Modified-Since header had been given. */ @@ -3732,7 +3706,7 @@ download_status_reset(download_status_t *dls) const int *schedule; size_t schedule_len; - find_dl_schedule_and_len(dls, get_options()->DirPort != NULL, + find_dl_schedule_and_len(dls, get_options()->DirPort_set, &schedule, &schedule_len); dls->n_download_failures = 0; diff --git a/src/or/directory.h b/src/or/directory.h index 584fe84311..14444c0ce4 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -51,8 +51,7 @@ int connection_dir_finished_connecting(dir_connection_t *conn); void connection_dir_about_to_close(dir_connection_t *dir_conn); void directory_initiate_command(const char *address, const tor_addr_t *addr, uint16_t or_port, uint16_t dir_port, - int supports_conditional_consensus, - int supports_begindir, const char *digest, + const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, const char *resource, @@ -80,7 +79,7 @@ time_t download_status_increment_failure(download_status_t *dls, * the optional status code <b>sc</b>. */ #define download_status_failed(dls, sc) \ download_status_increment_failure((dls), (sc), NULL, \ - get_options()->DirPort!=NULL, time(NULL)) + get_options()->DirPort_set, time(NULL)) void download_status_reset(download_status_t *dls); static int download_status_is_ready(download_status_t *dls, time_t now, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index e21f5113f2..a8f2fb9de0 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -62,6 +62,16 @@ static cached_dir_t *the_directory = NULL; /** For authoritative directories: the current (v1) network status. */ static cached_dir_t the_runningrouters; +/** Array of start and end of consensus methods used for supported + microdescriptor formats. */ +static const struct consensus_method_range_t { + int low; + int high; +} microdesc_consensus_methods[] = { + {MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1}, + {MIN_METHOD_FOR_A_LINES, MAX_SUPPORTED_CONSENSUS_METHOD}, + {-1, -1}}; + static void directory_remove_invalid(void); static cached_dir_t *dirserv_regenerate_directory(void); static char *format_versions_list(config_line_t *ln); @@ -388,18 +398,18 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, strmap_size(fingerprint_list->fp_by_name), digestmap_size(fingerprint_list->status_by_digest)); - /* Versions before Tor 0.2.1.30 have known security issues that + /* Versions before Tor 0.2.2.35 have known security issues that * make them unsuitable for the current network. */ - if (platform && !tor_version_as_new_as(platform,"0.2.1.30")) { + if (platform && !tor_version_as_new_as(platform,"0.2.2.35")) { if (msg) - *msg = "Tor version is insecure. Please upgrade!"; + *msg = "Tor version is insecure or unsupported. Please upgrade!"; return FP_REJECT; - } else if (platform && tor_version_as_new_as(platform,"0.2.2.1-alpha")) { - /* Versions from 0.2.2.1-alpha...0.2.2.20-alpha have known security + } else if (platform && tor_version_as_new_as(platform,"0.2.3.0-alpha")) { + /* Versions from 0.2.3-alpha...0.2.3.9-alpha have known security * issues that make them unusable for the current network */ - if (!tor_version_as_new_as(platform, "0.2.2.21-alpha")) { + if (!tor_version_as_new_as(platform, "0.2.3.10-alpha")) { if (msg) - *msg = "Tor version is insecure. Please upgrade!"; + *msg = "Tor version is insecure or unsupported. Please upgrade!"; return FP_REJECT; } } @@ -717,7 +727,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.", ri->nickname, source, (int)ri->cache_info.signed_descriptor_len, MAX_DESCRIPTOR_UPLOAD_SIZE); - *msg = "Router descriptor was too large"; + *msg = "Router descriptor was too large."; control_event_or_authdir_new_descriptor("REJECTED", ri->cache_info.signed_descriptor_body, ri->cache_info.signed_descriptor_len, *msg); @@ -980,6 +990,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) unreachable. */ int answer; + const or_options_t *options = get_options(); node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest); tor_assert(node); @@ -988,17 +999,27 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) answer = ! we_are_hibernating(); } else if (router->is_hibernating && (router->cache_info.published_on + - HIBERNATION_PUBLICATION_SKEW) > router->last_reachable) { + HIBERNATION_PUBLICATION_SKEW) > node->last_reachable) { /* A hibernating router is down unless we (somehow) had contact with it * since it declared itself to be hibernating. */ answer = 0; - } else if (get_options()->AssumeReachable) { + } else if (options->AssumeReachable) { /* If AssumeReachable, everybody is up unless they say they are down! */ answer = 1; } else { - /* Otherwise, a router counts as up if we found it reachable in the last - REACHABLE_TIMEOUT seconds. */ - answer = (now < router->last_reachable + REACHABLE_TIMEOUT); + /* Otherwise, a router counts as up if we found all announced OR + ports reachable in the last REACHABLE_TIMEOUT seconds. + + XXX prop186 For now there's always one IPv4 and at most one + IPv6 OR port. + + If we're not on IPv6, don't consider reachability of potential + IPv6 OR port since that'd kill all dual stack relays until a + majority of the dir auths have IPv6 connectivity. */ + answer = (now < node->last_reachable + REACHABLE_TIMEOUT && + (options->AuthDirHasIPv6Connectivity != 1 || + tor_addr_is_null(&router->ipv6_addr) || + now < node->last_reachable6 + REACHABLE_TIMEOUT)); } if (!answer && running_long_enough_to_decide_unreachable()) { @@ -1008,11 +1029,13 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) REACHABILITY_TEST_CYCLE_PERIOD seconds, then the router has probably been down since at least that time after we last successfully reached it. + + XXX ipv6 */ time_t when = now; - if (router->last_reachable && - router->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now) - when = router->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD; + if (node->last_reachable && + node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now) + when = node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD; rep_hist_note_router_unreachable(router->cache_info.identity_digest, when); } @@ -1214,7 +1237,7 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); - if (options->DirPort == NULL && !refuseunknown) + if (!options->DirPort_set && !refuseunknown) return 0; if (!server_mode(options) || !advertised_server_mode()) return 0; @@ -1250,7 +1273,7 @@ directory_fetches_dir_info_later(const or_options_t *options) int directory_caches_v2_dir_info(const or_options_t *options) { - return options->DirPort != NULL; + return options->DirPort_set; } /** Return true iff we want to fetch and keep certificates for authorities @@ -1259,7 +1282,7 @@ directory_caches_v2_dir_info(const or_options_t *options) int directory_caches_unknown_auth_certs(const or_options_t *options) { - return options->DirPort || options->BridgeRelay; + return options->DirPort_set || options->BridgeRelay; } /** Return 1 if we want to keep descriptors, networkstatuses, etc around @@ -1268,7 +1291,7 @@ directory_caches_unknown_auth_certs(const or_options_t *options) int directory_caches_dir_info(const or_options_t *options) { - if (options->BridgeRelay || options->DirPort) + if (options->BridgeRelay || options->DirPort_set) return 1; if (!server_mode(options) || !advertised_server_mode()) return 0; @@ -1284,7 +1307,7 @@ directory_caches_dir_info(const or_options_t *options) int directory_permits_begindir_requests(const or_options_t *options) { - return options->BridgeRelay != 0 || options->DirPort != NULL; + return options->BridgeRelay != 0 || options->DirPort_set; } /** Return 1 if we want to allow controllers to ask us directory @@ -1293,7 +1316,7 @@ directory_permits_begindir_requests(const or_options_t *options) int directory_permits_controller_requests(const or_options_t *options) { - return options->DirPort != NULL; + return options->DirPort_set; } /** Return 1 if we have no need to fetch new descriptors. This generally @@ -2040,7 +2063,7 @@ version_from_platform(const char *platform) * non-NULL, add a "v" line for the platform. Return 0 on success, -1 on * failure. * - * The format argument has three possible values: + * 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 * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc @@ -2079,15 +2102,34 @@ routerstatus_format_entry(char *buf, size_t buf_len, log_warn(LD_BUG, "Not enough space in buffer."); return -1; } + cp = buf + strlen(buf); /* TODO: Maybe we want to pass in what we need to build the rest of * this here, instead of in the caller. Then we could use the * networkstatus_type_t values, with an additional control port value * added -MP */ - if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) + + /* V3 microdesc consensuses don't have "a" lines. */ + if (format == NS_V3_CONSENSUS_MICRODESC) + return 0; + + /* Possible "a" line. At most one for now. */ + if (!tor_addr_is_null(&rs->ipv6_addr)) { + const char *addr_str = fmt_and_decorate_addr(&rs->ipv6_addr); + r = tor_snprintf(cp, buf_len - (cp-buf), + "a %s:%d\n", + addr_str, + (int)rs->ipv6_orport); + if (r<0) { + log_warn(LD_BUG, "Not enough space in buffer."); + return -1; + } + cp += strlen(cp); + } + + if (format == NS_V3_CONSENSUS) return 0; - cp = buf + strlen(buf); /* NOTE: Whenever this list expands, be sure to increase MAX_FLAG_LINE_LEN*/ r = tor_snprintf(cp, buf_len - (cp-buf), "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", @@ -2114,7 +2156,7 @@ routerstatus_format_entry(char *buf, size_t buf_len, /* length of "opt v \n" */ #define V_LINE_OVERHEAD 7 if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { - if (tor_snprintf(cp, buf_len - (cp-buf), "opt v %s\n", version)<0) { + if (tor_snprintf(cp, buf_len - (cp-buf), "v %s\n", version)<0) { log_warn(LD_BUG, "Unable to print router version."); return -1; } @@ -2393,8 +2435,6 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, int listbaddirs, int vote_on_hsdirs) { const or_options_t *options = get_options(); - int unstable_version = - !tor_version_as_new_as(ri->platform,"0.1.1.16-rc-cvs"); uint32_t routerbw = router_get_advertised_bandwidth(ri); memset(rs, 0, sizeof(routerstatus_t)); @@ -2406,8 +2446,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->is_exit = node->is_exit; rs->is_stable = node->is_stable = router_is_active(ri, node, now) && - !dirserv_thinks_router_is_unreliable(now, ri, 1, 0) && - !unstable_version; + !dirserv_thinks_router_is_unreliable(now, ri, 1, 0); rs->is_fast = node->is_fast = router_is_active(ri, node, now) && !dirserv_thinks_router_is_unreliable(now, ri, 0, 1); @@ -2453,6 +2492,14 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); rs->or_port = ri->or_port; rs->dir_port = ri->dir_port; + if (options->AuthDirHasIPv6Connectivity == 1 && + !tor_addr_is_null(&ri->ipv6_addr) && + node->last_reachable6 >= now - REACHABLE_TIMEOUT) { + /* We're configured as having IPv6 connectivity. There's an IPv6 + 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; + } } /** Routerstatus <b>rs</b> is part of a group of routers that are on @@ -2715,6 +2762,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, microdescriptors = smartlist_new(); SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + const struct consensus_method_range_t *cmr = NULL; if (ri->cache_info.published_on >= cutoff) { routerstatus_t *rs; vote_routerstatus_t *vrs; @@ -2736,17 +2784,22 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, rs->is_flagged_running = 0; vrs->version = version_from_platform(ri->platform); - md = dirvote_create_microdescriptor(ri); - if (md) { - char buf[128]; - vote_microdesc_hash_t *h; - dirvote_format_microdesc_vote_line(buf, sizeof(buf), md); - h = tor_malloc(sizeof(vote_microdesc_hash_t)); - h->microdesc_hash_line = tor_strdup(buf); - h->next = NULL; - vrs->microdesc = h; - md->last_listed = now; - smartlist_add(microdescriptors, md); + for (cmr = microdesc_consensus_methods; + cmr->low != -1 && cmr->high != -1; + cmr++) { + md = dirvote_create_microdescriptor(ri, cmr->low); + if (md) { + char buf[128]; + vote_microdesc_hash_t *h; + dirvote_format_microdesc_vote_line(buf, sizeof(buf), md, + cmr->low, cmr->high); + h = tor_malloc_zero(sizeof(vote_microdesc_hash_t)); + h->microdesc_hash_line = tor_strdup(buf); + h->next = vrs->microdesc; + vrs->microdesc = h; + md->last_listed = now; + smartlist_add(microdescriptors, md); + } } smartlist_add(routerstatuses, vrs); @@ -3273,36 +3326,42 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, * Inform the reachability checker that we could get to this guy. */ void -dirserv_orconn_tls_done(const char *address, +dirserv_orconn_tls_done(const tor_addr_t *addr, uint16_t or_port, const char *digest_rcvd) { - routerinfo_t *ri; + node_t *node = NULL; + tor_addr_port_t orport; + routerinfo_t *ri = NULL; time_t now = time(NULL); - tor_assert(address); + tor_assert(addr); tor_assert(digest_rcvd); - ri = router_get_mutable_by_digest(digest_rcvd); - - if (ri == NULL) + node = node_get_mutable_by_id(digest_rcvd); + if (node == NULL || node->ri == NULL) return; + ri = node->ri; - if (!strcasecmp(address, ri->address) && or_port == ri->or_port) { + tor_addr_copy(&orport.addr, addr); + orport.port = or_port; + if (router_has_orport(ri, &orport)) { /* Found the right router. */ if (!authdir_mode_bridge(get_options()) || ri->purpose == ROUTER_PURPOSE_BRIDGE) { + char addrstr[TOR_ADDR_BUF_LEN]; /* This is a bridge or we're not a bridge authorititative -- mark it as reachable. */ - tor_addr_t addr, *addrp=NULL; log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.", router_describe(ri), - address, ri->or_port); - if (tor_addr_parse(&addr, ri->address) != -1) - addrp = &addr; - else - log_warn(LD_BUG, "Couldn't parse IP address \"%s\"", ri->address); - rep_hist_note_router_reachable(digest_rcvd, addrp, or_port, now); - ri->last_reachable = now; + tor_addr_to_str(addrstr, addr, sizeof(addrstr), 1), + ri->or_port); + if (tor_addr_family(addr) == AF_INET) { + rep_hist_note_router_reachable(digest_rcvd, addr, or_port, now); + node->last_reachable = now; + } else if (tor_addr_family(addr) == AF_INET6) { + /* No rephist for IPv6. */ + node->last_reachable6 = now; + } } } } @@ -3325,7 +3384,7 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri, /* It just came out of hibernation; launch a reachability test */ return 1; } - if (! routers_have_same_or_addr(ri, ri_old)) { + if (! routers_have_same_or_addrs(ri, ri_old)) { /* Address or port changed; launch a reachability test */ return 1; } @@ -3338,15 +3397,35 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri, void dirserv_single_reachability_test(time_t now, routerinfo_t *router) { + node_t *node = NULL; tor_addr_t router_addr; + + tor_assert(router); + node = node_get_mutable_by_id(router->cache_info.identity_digest); + tor_assert(node); + + /* IPv4. */ log_debug(LD_OR,"Testing reachability of %s at %s:%u.", router->nickname, router->address, router->or_port); /* Remember when we started trying to determine reachability */ - if (!router->testing_since) - router->testing_since = now; + if (!node->testing_since) + node->testing_since = now; tor_addr_from_ipv4h(&router_addr, router->addr); connection_or_connect(&router_addr, router->or_port, router->cache_info.identity_digest); + + /* Possible IPv6. */ + if (!tor_addr_is_null(&router->ipv6_addr)) { + char addrstr[TOR_ADDR_BUF_LEN]; + log_debug(LD_OR, "Testing reachability of %s at %s:%u.", + router->nickname, + tor_addr_to_str(addrstr, &router->ipv6_addr, sizeof(addrstr), 1), + router->ipv6_orport); + if (!node->testing_since6) + node->testing_since6 = now; + connection_or_connect(&router->ipv6_addr, router->ipv6_orport, + router->cache_info.identity_digest); + } } /** Auth dir server only: load balance such that we only diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 22269b2009..8508c938a8 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -107,7 +107,7 @@ int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, int is_extrainfo); int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, const char **msg); -void dirserv_orconn_tls_done(const char *address, +void dirserv_orconn_tls_done(const tor_addr_t *addr, uint16_t or_port, const char *digest_rcvd); int dirserv_should_launch_reachability_test(const routerinfo_t *ri, diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 79958739a5..b1b885cf3e 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -53,25 +53,6 @@ static int dirvote_compute_consensuses(void); static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high, const char *sep); -/** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 12 - -/** Lowest consensus method that contains a 'directory-footer' marker */ -#define MIN_METHOD_FOR_FOOTER 9 - -/** Lowest consensus method that contains bandwidth weights */ -#define MIN_METHOD_FOR_BW_WEIGHTS 9 - -/** Lowest consensus method that contains consensus params */ -#define MIN_METHOD_FOR_PARAMS 7 - -/** Lowest consensus method that generates microdescriptors */ -#define MIN_METHOD_FOR_MICRODESC 8 - -/** Lowest consensus method that ensures a majority of authorities voted - * for a param. */ -#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 - /* ===== * Voting * =====*/ @@ -426,6 +407,21 @@ _compare_vote_rs(const void **_a, const void **_b) return compare_vote_rs(a,b); } +/** Helper for sorting OR ports. */ +static int +_compare_orports(const void **_a, const void **_b) +{ + const tor_addr_port_t *a = *_a, *b = *_b; + int r; + + if ((r = tor_addr_compare(&a->addr, &b->addr, CMP_EXACT))) + return r; + if ((r = (((int) b->port) - ((int) a->port)))) + return r; + + return 0; +} + /** Given a list of vote_routerstatus_t, all for the same router identity, * return whichever is most frequent, breaking ties in favor of more * recently published vote_routerstatus_t and in case of ties there, @@ -433,7 +429,8 @@ _compare_vote_rs(const void **_a, const void **_b) */ static vote_routerstatus_t * compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, - char *microdesc_digest256_out) + char *microdesc_digest256_out, + tor_addr_port_t *best_alt_orport_out) { vote_routerstatus_t *most = NULL, *cur = NULL; int most_n = 0, cur_n = 0; @@ -469,6 +466,39 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, tor_assert(most); + /* If we're producing "a" lines, vote on potential alternative (sets + * of) OR port(s) in the winning routerstatuses. + * + * XXX prop186 There's at most one alternative OR port (_the_ IPv6 + * port) for now. */ + if (consensus_method >= MIN_METHOD_FOR_A_LINES && best_alt_orport_out) { + smartlist_t *alt_orports = smartlist_new(); + const tor_addr_port_t *most_alt_orport = NULL; + + SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { + if (compare_vote_rs(most, rs) == 0 && + !tor_addr_is_null(&rs->status.ipv6_addr) + && rs->status.ipv6_orport) { + smartlist_add(alt_orports, tor_addr_port_new(&rs->status.ipv6_addr, + rs->status.ipv6_orport)); + } + } SMARTLIST_FOREACH_END(rs); + + smartlist_sort(alt_orports, _compare_orports); + most_alt_orport = smartlist_get_most_frequent(alt_orports, + _compare_orports); + if (most_alt_orport) { + memcpy(best_alt_orport_out, most_alt_orport, sizeof(tor_addr_port_t)); + log_debug(LD_DIR, "\"a\" line winner for %s is %s:%d", + most->status.nickname, + fmt_and_decorate_addr(&most_alt_orport->addr), + most_alt_orport->port); + } + + SMARTLIST_FOREACH(alt_orports, tor_addr_port_t *, ap, tor_free(ap)); + smartlist_free(alt_orports); + } + if (consensus_method >= MIN_METHOD_FOR_MICRODESC && microdesc_digest256_out) { smartlist_t *digests = smartlist_new(); @@ -1681,6 +1711,7 @@ networkstatus_compute_consensus(smartlist_t *votes, int n_listing = 0; int i; char microdesc_digest[DIGEST256_LEN]; + tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0}; /* Of the next-to-be-considered digest in each voter, which is first? */ SMARTLIST_FOREACH(votes, networkstatus_t *, v, { @@ -1750,8 +1781,9 @@ networkstatus_compute_consensus(smartlist_t *votes, * routerinfo and its contents are. */ memset(microdesc_digest, 0, sizeof(microdesc_digest)); rs = compute_routerstatus_consensus(matching_descs, consensus_method, - microdesc_digest); + microdesc_digest, &alt_orport); /* Copy bits of that into rs_out. */ + memset(&rs_out, 0, sizeof(rs_out)); tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN)); memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, @@ -1760,6 +1792,10 @@ networkstatus_compute_consensus(smartlist_t *votes, rs_out.published_on = rs->status.published_on; rs_out.dir_port = rs->status.dir_port; rs_out.or_port = rs->status.or_port; + if (consensus_method >= MIN_METHOD_FOR_A_LINES) { + tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr); + rs_out.ipv6_orport = alt_orport.port; + } rs_out.has_bandwidth = 0; rs_out.has_exitsummary = 0; @@ -1935,6 +1971,13 @@ networkstatus_compute_consensus(smartlist_t *votes, } } + if (flavor == FLAV_MICRODESC && + consensus_method >= MIN_METHOD_FOR_MANDATORY_MICRODESC && + tor_digest256_is_zero(microdesc_digest)) { + /* With no microdescriptor digest, we omit the entry entirely. */ + continue; + } + { char buf[4096]; /* Okay!! Now we can write the descriptor... */ @@ -2503,7 +2546,7 @@ time_t dirvote_get_start_of_next_interval(time_t now, int interval) { struct tm tm; - time_t midnight_today; + time_t midnight_today=0; time_t midnight_tomorrow; time_t next; @@ -2512,7 +2555,9 @@ dirvote_get_start_of_next_interval(time_t now, int interval) tm.tm_min = 0; tm.tm_sec = 0; - midnight_today = tor_timegm(&tm); + if (tor_timegm(&tm, &midnight_today) < 0) { + log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); + } midnight_tomorrow = midnight_today + (24*60*60); next = midnight_today + ((now-midnight_today)/interval + 1)*interval; @@ -3498,13 +3543,13 @@ dirvote_get_vote(const char *fp, int flags) * particular method. **/ microdesc_t * -dirvote_create_microdescriptor(const routerinfo_t *ri) +dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) { microdesc_t *result = NULL; char *key = NULL, *summary = NULL, *family = NULL; - char buf[1024]; size_t keylen; - char *out = buf, *end = buf+sizeof(buf); + smartlist_t *chunks = smartlist_new(); + char *output = NULL; if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0) goto done; @@ -3512,23 +3557,25 @@ dirvote_create_microdescriptor(const routerinfo_t *ri) if (ri->declared_family) family = smartlist_join_strings(ri->declared_family, " ", 0, NULL); - if (tor_snprintf(out, end-out, "onion-key\n%s", key)<0) - goto done; - out += strlen(out); - if (family) { - if (tor_snprintf(out, end-out, "family %s\n", family)<0) - goto done; - out += strlen(out); - } - if (summary && strcmp(summary, "reject 1-65535")) { - if (tor_snprintf(out, end-out, "p %s\n", summary)<0) - goto done; - out += strlen(out); - } - *out = '\0'; /* Make sure it's nul-terminated. This should be a no-op */ + smartlist_add_asprintf(chunks, "onion-key\n%s", key); + + if (consensus_method >= MIN_METHOD_FOR_A_LINES && + !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) + smartlist_add_asprintf(chunks, "a %s:%d\n", + fmt_and_decorate_addr(&ri->ipv6_addr), + ri->ipv6_orport); + + if (family) + smartlist_add_asprintf(chunks, "family %s\n", family); + + if (summary && strcmp(summary, "reject 1-65535")) + smartlist_add_asprintf(chunks, "p %s\n", summary); + + output = smartlist_join_strings(chunks, "", 0, NULL); { - smartlist_t *lst = microdescs_parse_from_string(buf, out, 0, 1); + smartlist_t *lst = microdescs_parse_from_string(output, + output+strlen(output), 0, 1); if (smartlist_len(lst) != 1) { log_warn(LD_DIR, "We generated a microdescriptor we couldn't parse."); SMARTLIST_FOREACH(lst, microdesc_t *, md, microdesc_free(md)); @@ -3540,39 +3587,47 @@ dirvote_create_microdescriptor(const routerinfo_t *ri) } done: + tor_free(output); tor_free(key); tor_free(summary); tor_free(family); + if (chunks) { + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); + } return result; } -/** Cached space-separated string to hold */ -static char *microdesc_consensus_methods = NULL; - /** Format the appropriate vote line to describe the microdescriptor <b>md</b> * in a consensus vote document. Write it into the <b>out_len</b>-byte buffer * in <b>out</b>. Return -1 on failure and the number of characters written * on success. */ ssize_t -dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md) +dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len, + const microdesc_t *md, + int consensus_method_low, + int consensus_method_high) { + ssize_t ret = -1; char d64[BASE64_DIGEST256_LEN+1]; - if (!microdesc_consensus_methods) { - microdesc_consensus_methods = - make_consensus_method_list(MIN_METHOD_FOR_MICRODESC, - MAX_SUPPORTED_CONSENSUS_METHOD, - ","); - tor_assert(microdesc_consensus_methods); - } + char *microdesc_consensus_methods = + make_consensus_method_list(consensus_method_low, + consensus_method_high, + ","); + tor_assert(microdesc_consensus_methods); + if (digest256_to_base64(d64, md->digest)<0) - return -1; + goto out; - if (tor_snprintf(out, out_len, "m %s sha256=%s\n", + if (tor_snprintf(out_buf, out_buf_len, "m %s sha256=%s\n", microdesc_consensus_methods, d64)<0) - return -1; + goto out; + + ret = strlen(out_buf); - return strlen(out); + out: + tor_free(microdesc_consensus_methods); + return ret; } /** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with diff --git a/src/or/dirvote.h b/src/or/dirvote.h index e6f9700614..6211218641 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -19,6 +19,32 @@ /** Smallest allowable voting interval. */ #define MIN_VOTE_INTERVAL 300 +/** The highest consensus method that we currently support. */ +#define MAX_SUPPORTED_CONSENSUS_METHOD 14 + +/** Lowest consensus method that contains a 'directory-footer' marker */ +#define MIN_METHOD_FOR_FOOTER 9 + +/** Lowest consensus method that contains bandwidth weights */ +#define MIN_METHOD_FOR_BW_WEIGHTS 9 + +/** Lowest consensus method that contains consensus params */ +#define MIN_METHOD_FOR_PARAMS 7 + +/** Lowest consensus method that generates microdescriptors */ +#define MIN_METHOD_FOR_MICRODESC 8 + +/** Lowest consensus method that ensures a majority of authorities voted + * for a param. */ +#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 + +/** Lowest consensus method where microdesc consensuses omit any entry + * with no microdesc. */ +#define MIN_METHOD_FOR_MANDATORY_MICRODESC 13 + +/** Lowest consensus method that contains "a" lines. */ +#define MIN_METHOD_FOR_A_LINES 14 + void dirvote_free_all(void); /* vote manipulation */ @@ -70,10 +96,12 @@ networkstatus_t * dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert); -microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri); +microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, + int consensus_method); ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md); - + const microdesc_t *md, + int consensus_method_low, + int consensus_method_high); int vote_routerstatus_find_microdesc_hash(char *digest256_out, const vote_routerstatus_t *vrs, int method, diff --git a/src/or/dns.c b/src/or/dns.c index 3e88fad68c..78893bfbed 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -450,16 +450,17 @@ purge_expired_resolves(time_t now) if (resolve->pending_connections) { log_debug(LD_EXIT, "Closing pending connections on timed-out DNS resolve!"); - tor_fragile_assert(); while (resolve->pending_connections) { pend = resolve->pending_connections; resolve->pending_connections = pend->next; /* Connections should only be pending if they have no socket. */ tor_assert(!SOCKET_OK(pend->conn->_base.s)); pendconn = pend->conn; - connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT); - circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - connection_free(TO_CONN(pendconn)); + 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)); + } tor_free(pend); } } @@ -1091,6 +1092,13 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr, pendconn = pend->conn; /* don't pass complex things to the connection_mark_for_close macro */ assert_connection_ok(TO_CONN(pendconn),time(NULL)); + if (pendconn->_base.marked_for_close) { + /* prevent double-remove. */ + pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED; + resolve->pending_connections = pend->next; + tor_free(pend); + continue; + } tor_addr_from_ipv4h(&pendconn->_base.addr, addr); pendconn->address_ttl = ttl; diff --git a/src/or/include.am b/src/or/include.am new file mode 100644 index 0000000000..97072dce2f --- /dev/null +++ b/src/or/include.am @@ -0,0 +1,161 @@ +bin_PROGRAMS+= src/or/tor +noinst_LIBRARIES+= src/or/libtor.a + +if BUILD_NT_SERVICES +tor_platform_source=src/or/ntmain.c +else +tor_platform_source= +endif + +EXTRA_DIST+= src/or/ntmain.c src/or/or_sha1.i src/or/Makefile.nmake + +if USE_EXTERNAL_EVDNS +evdns_source= +else +evdns_source=src/or/eventdns.c +endif + +src_or_libtor_a_SOURCES = \ + src/or/buffers.c \ + src/or/circuitbuild.c \ + src/or/circuitlist.c \ + src/or/circuituse.c \ + src/or/command.c \ + src/or/config.c \ + src/or/connection.c \ + src/or/connection_edge.c \ + src/or/connection_or.c \ + src/or/control.c \ + src/or/cpuworker.c \ + src/or/directory.c \ + src/or/dirserv.c \ + src/or/dirvote.c \ + src/or/dns.c \ + src/or/dnsserv.c \ + src/or/geoip.c \ + src/or/hibernate.c \ + src/or/main.c \ + src/or/microdesc.c \ + src/or/networkstatus.c \ + src/or/nodelist.c \ + src/or/onion.c \ + src/or/transports.c \ + src/or/policies.c \ + src/or/reasons.c \ + src/or/relay.c \ + src/or/rendclient.c \ + src/or/rendcommon.c \ + src/or/rendmid.c \ + src/or/rendservice.c \ + src/or/rephist.c \ + src/or/replaycache.c \ + src/or/router.c \ + src/or/routerlist.c \ + src/or/routerparse.c \ + src/or/status.c \ + $(evdns_source) \ + $(tor_platform_source) \ + src/or/config_codedigest.c + +#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \ +# ../common/libor-event.a + + +src_or_tor_SOURCES = src/or/tor_main.c +AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or + +src/or/tor_main.o: micro-revision.i + +AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ + -DLOCALSTATEDIR="\"$(localstatedir)\"" \ + -DBINDIR="\"$(bindir)\"" + +# -L flags need to go in LDFLAGS. -l flags need to go in LDADD. +# This seems to matter nowhere but on windows, but I assure you that it +# matters a lot there, and is quite hard to debug if you forget to do it. + + +src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \ + src/common/libor-event.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ + +ORHEADERS = \ + src/or/buffers.h \ + src/or/circuitbuild.h \ + src/or/circuitlist.h \ + src/or/circuituse.h \ + src/or/command.h \ + src/or/config.h \ + src/or/connection.h \ + src/or/connection_edge.h \ + src/or/connection_or.h \ + src/or/control.h \ + src/or/cpuworker.h \ + src/or/directory.h \ + src/or/dirserv.h \ + src/or/dirvote.h \ + src/or/dns.h \ + src/or/dnsserv.h \ + src/or/eventdns.h \ + src/or/eventdns_tor.h \ + src/or/geoip.h \ + src/or/hibernate.h \ + src/or/main.h \ + src/or/microdesc.h \ + src/or/networkstatus.h \ + src/or/nodelist.h \ + src/or/ntmain.h \ + src/or/onion.h \ + src/or/or.h \ + src/or/transports.h \ + src/or/policies.h \ + src/or/reasons.h \ + src/or/relay.h \ + src/or/rendclient.h \ + src/or/rendcommon.h \ + src/or/rendmid.h \ + src/or/rendservice.h \ + src/or/rephist.h \ + src/or/replaycache.h \ + src/or/router.h \ + src/or/routerlist.h \ + src/or/routerparse.h \ + src/or/status.h + +noinst_HEADERS+= $(ORHEADERS) micro-revision.i + +src/or/config_codedigest.o: src/or/or_sha1.i + +micro-revision.i: FORCE + @rm -f micro-revision.tmp; \ + if test -d "$(top_srcdir)/.git" && \ + test -x "`which git 2>&1;true`"; then \ + HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ + echo \"$$HASH\" > micro-revision.tmp; \ + fi; \ + if test ! -f micro-revision.tmp ; then \ + if test ! -f micro-revision.i ; then \ + echo '""' > micro-revision.i; \ + fi; \ + elif test ! -f micro-revision.i || \ + test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ + mv micro-revision.tmp micro-revision.i; \ + fi; true + +src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) + $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ + (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \ + "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \ + elif test "@OPENSSL@" != none; then \ + (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \ + "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \ + else \ + rm src/or/or_sha1.i; \ + touch src/or/or_sha1.i; \ + fi + +CLEANFILES+= micro-revision.i + +FORCE: diff --git a/src/or/main.c b/src/or/main.c index 20a1e086a4..39eccd6e65 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -953,7 +953,8 @@ directory_info_has_arrived(time_t now, int from_cache) const or_options_t *options = get_options(); if (!router_have_minimum_dir_info()) { - int quiet = directory_too_idle_to_fetch_descriptors(options, now); + int quiet = from_cache || + directory_too_idle_to_fetch_descriptors(options, now); log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR, "I learned some more directory information, but not enough to " "build a circuit: %s", get_dir_info_status_string()); @@ -1546,11 +1547,15 @@ run_scheduled_events(time_t now) options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 - /* XXXXX this should take a list of ports, not just two! */ - tor_check_port_forwarding(options->PortForwardingHelper, - get_primary_dir_port(), - get_primary_or_port(), - now); + smartlist_t *ports_to_forward = get_list_of_ports_to_forward(); + if (ports_to_forward) { + tor_check_port_forwarding(options->PortForwardingHelper, + ports_to_forward, + now); + + SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp)); + smartlist_free(ports_to_forward); + } time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } @@ -1561,7 +1566,8 @@ run_scheduled_events(time_t now) /** 12. write the heartbeat message */ if (options->HeartbeatPeriod && time_to_next_heartbeat <= now) { - log_heartbeat(now); + if (time_to_next_heartbeat) /* don't log the first heartbeat */ + log_heartbeat(now); time_to_next_heartbeat = now+options->HeartbeatPeriod; } } @@ -2302,12 +2308,16 @@ tor_init(int argc, char *argv[]) { const char *version = get_version(); + log_notice(LD_GENERAL, "Tor v%s %srunning on %s with Libevent %s " + "and OpenSSL %s.", version, #ifdef USE_BUFFEREVENTS - log_notice(LD_GENERAL, "Tor v%s (with bufferevents) running on %s.", - version, get_uname()); + "(with bufferevents) ", #else - log_notice(LD_GENERAL, "Tor v%s running on %s.", version, get_uname()); + "", #endif + get_uname(), + tor_libevent_get_version_str(), + crypto_openssl_get_version_str()); log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! " "Learn how to be safe at " diff --git a/src/or/microdesc.c b/src/or/microdesc.c index 9395a9a051..3bda9cbfaf 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -323,8 +323,8 @@ microdesc_cache_reload(microdesc_cache_t *cache) } tor_free(journal_content); } - log_notice(LD_DIR, "Reloaded microdescriptor cache. Found %d descriptors.", - total); + log_info(LD_DIR, "Reloaded microdescriptor cache. Found %d descriptors.", + total); microdesc_cache_rebuild(cache, 0 /* don't force */); @@ -643,13 +643,8 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, continue; if (skip && digestmap_get(skip, rs->descriptor_digest)) continue; - if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN)) { - log_info(LD_BUG, "Found an entry in networkstatus with no " - "microdescriptor digest. (Router %s=%s at %s:%d.)", - rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN), - fmt_addr32(rs->addr), rs->or_port); + if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN)) continue; - } /* XXXX Also skip if we're a noncache and wouldn't use this router. * XXXX NM Microdesc */ @@ -658,7 +653,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, return result; } -/** Launch download requests for mircodescriptors as appropriate. +/** Launch download requests for microdescriptors as appropriate. * * Specifically, we should launch download requests if we are configured to * download mirodescriptors, and there are some microdescriptors listed the diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index fadaf90da4..0df5c4e631 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1530,13 +1530,7 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->is_bad_exit != b->is_bad_exit || a->is_bad_directory != b->is_bad_directory || a->is_hs_dir != b->is_hs_dir || - a->version_known != b->version_known || - a->version_supports_begindir != b->version_supports_begindir || - a->version_supports_extrainfo_upload != - b->version_supports_extrainfo_upload || - a->version_supports_conditional_consensus != - b->version_supports_conditional_consensus || - a->version_supports_v3_dir != b->version_supports_v3_dir; + a->version_known != b->version_known; } /** Notify controllers of any router status entries that changed between diff --git a/src/or/nodelist.c b/src/or/nodelist.c index d17850888d..b96491f5f9 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -115,19 +115,48 @@ node_get_or_create(const char *identity_digest) return node; } -/** Add <b>ri</b> to the nodelist. */ +/** Called when a node's address changes. */ +static void +node_addrs_changed(node_t *node) +{ + node->last_reachable = node->last_reachable6 = 0; + node->testing_since = node->testing_since6 = 0; + node->country = -1; +} + +/** Add <b>ri</b> to an appropriate node in the nodelist. If we replace an + * old routerinfo, and <b>ri_old_out</b> is not NULL, set *<b>ri_old_out</b> + * to the previous routerinfo. + */ node_t * -nodelist_add_routerinfo(routerinfo_t *ri) +nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out) { node_t *node; + const char *id_digest; + int had_router = 0; + tor_assert(ri); + init_nodelist(); - node = node_get_or_create(ri->cache_info.identity_digest); + id_digest = ri->cache_info.identity_digest; + node = node_get_or_create(id_digest); + + if (node->ri) { + if (!routers_have_same_or_addrs(node->ri, ri)) { + node_addrs_changed(node); + } + had_router = 1; + if (ri_old_out) + *ri_old_out = node->ri; + } else { + if (ri_old_out) + *ri_old_out = NULL; + } node->ri = ri; if (node->country == -1) node_set_country(node); - if (authdir_mode(get_options())) { + if (authdir_mode(get_options()) && !had_router) { const char *discard=NULL; uint32_t status = dirserv_router_get_status(ri, &discard); dirserv_set_node_flags_from_authoritative_status(node, status); @@ -167,7 +196,7 @@ nodelist_add_microdesc(microdesc_t *md) return node; } -/** Tell the nodelist that the current usable consensus to <b>ns</b>. +/** Tell the nodelist that the current usable consensus is <b>ns</b>. * This makes the nodelist change all of the routerstatus entries for * the nodes, drop nodes that no longer have enough info to get used, * and grab microdescriptors into nodes as appropriate. @@ -177,6 +206,7 @@ nodelist_set_consensus(networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v2(options) || authdir_mode_v3(options); + int client = !server_mode(options); init_nodelist(); if (ns->flavor == FLAV_MICRODESC) @@ -213,6 +243,11 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_bad_directory = rs->is_bad_directory; node->is_bad_exit = rs->is_bad_exit; node->is_hs_dir = rs->is_hs_dir; + node->ipv6_preferred = 0; + if (client && options->ClientPreferIPv6ORPort == 1 && + (tor_addr_is_null(&rs->ipv6_addr) == 0 || + (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) + node->ipv6_preferred = 1; } } SMARTLIST_FOREACH_END(rs); @@ -231,7 +266,8 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_valid = node->is_running = node->is_hs_dir = node->is_fast = node->is_stable = node->is_possible_guard = node->is_exit = - node->is_bad_exit = node->is_bad_directory = 0; + node->is_bad_exit = node->is_bad_directory = + node->ipv6_preferred = 0; } } } SMARTLIST_FOREACH_END(node); @@ -682,19 +718,6 @@ node_get_all_orports(const node_t *node) return sl; } -/** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>node</b> into *<b>ap_out</b>. */ -void -node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) -{ - if (node->ri) { - router_get_prim_orport(node->ri, ap_out); - } else if (node->rs) { - tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); - ap_out->port = node->rs->or_port; - } -} - /** Wrapper around node_get_prim_orport for backward compatibility. */ void @@ -718,36 +741,6 @@ node_get_prim_addr_ipv4h(const node_t *node) return 0; } -/** Copy the preferred OR port (IP address and TCP port) for - * <b>node</b> into <b>ap_out</b>. */ -void -node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) -{ - if (node->ri) { - router_get_pref_orport(node->ri, ap_out); - } else if (node->rs) { - /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private - bridges but needs fixing */ - tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); - ap_out->port = node->rs->or_port; - } -} - -/** Copy the preferred IPv6 OR port (address and TCP port) for - * <b>node</b> into *<b>ap_out</b>. */ -void -node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) -{ - if (node->ri) { - router_get_pref_ipv6_orport(node->ri, ap_out); - } else if (node->rs) { - /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private - bridges but needs fixing */ - tor_addr_make_unspec(&ap_out->addr); - ap_out->port = 0; - } -} - /** Copy a string representation of an IP address for <b>node</b> into * the <b>len</b>-byte buffer at <b>buf</b>. */ void @@ -818,3 +811,97 @@ node_get_declared_family(const node_t *node) return NULL; } +/** Return 1 if we prefer the IPv6 address and OR TCP port of + * <b>node</b>, else 0. + * + * We prefer the IPv6 address if the router has an IPv6 address and + * i) the node_t says that it prefers IPv6 + * or + * ii) the router has no IPv4 address. */ +int +node_ipv6_preferred(const node_t *node) +{ + tor_addr_port_t ipv4_addr; + node_assert_ok(node); + + if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) { + if (node->ri) + return !tor_addr_is_null(&node->ri->ipv6_addr); + if (node->md) + return !tor_addr_is_null(&node->md->ipv6_addr); + if (node->rs) + return !tor_addr_is_null(&node->rs->ipv6_addr); + } + return 0; +} + +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and + * port was copied, else return non-zero.*/ +int +node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + node_assert_ok(node); + tor_assert(ap_out); + + if (node->ri) { + if (node->ri->addr == 0 || node->ri->or_port == 0) + return -1; + tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr); + ap_out->port = node->ri->or_port; + return 0; + } + if (node->rs) { + if (node->rs->addr == 0 || node->rs->or_port == 0) + return -1; + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; + return 0; + } + return -1; +} + +/** Copy the preferred OR port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + tor_assert(ap_out); + + /* Cheap implementation of config option ClientUseIPv6 -- simply + don't prefer IPv6 when ClientUseIPv6 is not set. (See #4455 for + more on this subject.) Note that this filter is too strict since + we're hindering not only clients! Erring on the safe side + shouldn't be a problem though. XXX move this check to where + outgoing connections are made? -LN */ + if (get_options()->ClientUseIPv6 == 1 && node_ipv6_preferred(node)) + node_get_pref_ipv6_orport(node, ap_out); + else + node_get_prim_orport(node, ap_out); +} + +/** Copy the preferred IPv6 OR port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + node_assert_ok(node); + tor_assert(ap_out); + + /* We prefer the microdesc over a potential routerstatus here. They + are not being synchronised atm so there might be a chance that + they differ at some point, f.ex. when flipping + UseMicrodescriptors? -LN */ + + if (node->ri) { + tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); + ap_out->port = node->ri->ipv6_orport; + } else if (node->md) { + tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr); + ap_out->port = node->md->ipv6_orport; + } else if (node->rs) { + tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); + ap_out->port = node->rs->ipv6_orport; + } +} + diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 1e9da88d4e..fb65fa5483 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -5,17 +5,21 @@ /* See LICENSE for licensing information */ /** - * \file microdesc.h - * \brief Header file for microdesc.c. + * \file nodelist.h + * \brief Header file for nodelist.c. **/ #ifndef _TOR_NODELIST_H #define _TOR_NODELIST_H +#define node_assert_ok(n) STMT_BEGIN { \ + tor_assert((n)->ri || (n)->rs); \ + } STMT_END + node_t *node_get_mutable_by_id(const char *identity_digest); const node_t *node_get_by_id(const char *identity_digest); const node_t *node_get_by_hex_id(const char *identity_digest); -node_t *nodelist_add_routerinfo(routerinfo_t *ri); +node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); void nodelist_set_consensus(networkstatus_t *ns); @@ -38,18 +42,18 @@ int node_get_purpose(const node_t *node); int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); smartlist_t *node_get_all_orports(const node_t *node); -void node_get_prim_orport(const node_t *node, tor_addr_port_t *addr_port_out); -void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out); -void node_get_pref_ipv6_orport(const node_t *node, - tor_addr_port_t *addr_port_out); -uint32_t node_get_prim_addr_ipv4h(const node_t *node); int node_allows_single_hop_exits(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); +uint32_t node_get_prim_addr_ipv4h(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); time_t node_get_published_on(const node_t *node); const smartlist_t *node_get_declared_family(const node_t *node); +int node_ipv6_preferred(const node_t *node); +int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); smartlist_t *nodelist_get_list(void); diff --git a/src/or/or.h b/src/or/or.h index 3a53e5ed86..788179bacc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -98,6 +98,7 @@ #include "address.h" #include "compat_libevent.h" #include "ht.h" +#include "replaycache.h" /* These signals are defined to help handle_control_signal work. */ @@ -1769,8 +1770,6 @@ typedef struct { /** True if, after we have added this router, we should re-launch * tests for it. */ unsigned int needs_retest_if_added:1; - /** True if ipv6_addr:ipv6_orport is preferred. */ - unsigned int ipv6_preferred:1; /** Tor can use this router for general positions in circuits; we got it * from a directory server as usual, or we're an authority and a server @@ -1793,15 +1792,6 @@ typedef struct { * things; see notes on ROUTER_PURPOSE_* macros above. */ uint8_t purpose; - - /* The below items are used only by authdirservers for - * reachability testing. */ - - /** When was the last time we could reach this OR? */ - time_t last_reachable; - /** When did we start testing reachability for this OR? */ - time_t testing_since; - } routerinfo_t; /** Information needed to keep and cache a signed extra-info document. */ @@ -1833,6 +1823,8 @@ typedef struct routerstatus_t { uint32_t addr; /**< IPv4 address for this router. */ uint16_t or_port; /**< OR port for this router. */ uint16_t dir_port; /**< Directory port for this router. */ + tor_addr_t ipv6_addr; /**< IPv6 address for this router. */ + uint16_t ipv6_orport; /**<IPV6 OR port for this router. */ unsigned int is_authority:1; /**< True iff this router is an authority. */ unsigned int is_exit:1; /**< True iff this router is a good exit. */ unsigned int is_stable:1; /**< True iff this router stays up a long time. */ @@ -1862,16 +1854,7 @@ typedef struct routerstatus_t { * included.) We'll replace all these with a big tor_version_t or a char[] * if the number of traits we care about ever becomes incredibly big. */ unsigned int version_known:1; - /** True iff this router is a version that supports BEGIN_DIR cells. */ - unsigned int version_supports_begindir:1; - /** True iff this router is a version that supports conditional consensus - * downloads (signed by list of authorities). */ - unsigned int version_supports_conditional_consensus:1; - /** True iff this router is a version that we can post extrainfo docs to. */ - unsigned int version_supports_extrainfo_upload:1; - /** True iff this router is a version that, if it caches directory info, - * we can get v3 downloads from. */ - unsigned int version_supports_v3_dir:1; + /** True iff this router is a version that, if it caches directory info, * we can get microdescriptors from. */ unsigned int version_supports_microdesc_cache:1; @@ -1968,6 +1951,10 @@ typedef struct microdesc_t { /** As routerinfo_t.onion_pkey */ crypto_pk_t *onion_pkey; + /** As routerinfo_t.ipv6_add */ + tor_addr_t ipv6_addr; + /** As routerinfo_t.ipv6_orport */ + uint16_t ipv6_orport; /** As routerinfo_t.family */ smartlist_t *family; /** Exit policy summary */ @@ -2006,13 +1993,13 @@ typedef struct node_t { routerstatus_t *rs; /* local info: copied from routerstatus, then possibly frobbed based - * on experience. Authorities set this stuff directly. */ + * on experience. Authorities set this stuff directly. Note that + * these reflect knowledge of the primary (IPv4) OR port only. */ unsigned int is_running:1; /**< As far as we know, is this OR currently * running? */ unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR? - * (For Authdir: Have we validated this OR?) - */ + * (For Authdir: Have we validated this OR?) */ unsigned int is_fast:1; /** Do we think this is a fast OR? */ unsigned int is_stable:1; /** Do we think this is a stable OR? */ unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */ @@ -2035,8 +2022,23 @@ typedef struct node_t { /* Local info: derived. */ + /** True if the IPv6 OR port is preferred over the IPv4 OR port. */ + unsigned int ipv6_preferred:1; + /** According to the geoip db what country is this router in? */ + /* XXXprop186 what is this suppose to mean with multiple OR ports? */ country_t country; + + /* The below items are used only by authdirservers for + * reachability testing. */ + + /** When was the last time we could reach this OR? */ + time_t last_reachable; /* IPv4. */ + time_t last_reachable6; /* IPv6. */ + + /** When did we start testing reachability for this OR? */ + time_t testing_since; /* IPv4. */ + time_t testing_since6; /* IPv6. */ } node_t; /** How many times will we try to download a router's descriptor before giving @@ -2596,6 +2598,23 @@ typedef struct circuit_t { * circuit. */ #define MAX_RELAY_EARLY_CELLS_PER_CIRCUIT 8 +/** + * Describes the circuit building process in simplified terms based + * on the path bias accounting state for a circuit. Created to prevent + * overcounting due to unknown cases of circuit reuse. See Bug #6475. + */ +typedef enum { + /** This circuit is "new". It has not yet completed a first hop + * or been counted by the path bias code. */ + PATH_STATE_NEW_CIRC = 0, + /** This circuit has completed a first hop, and has been counted by + * the path bias logic. */ + PATH_STATE_DID_FIRST_HOP = 1, + /** This circuit has been completely built, and has been counted as + * successful by the path bias logic. */ + PATH_STATE_SUCCEEDED = 2, +} path_state_t; + /** An origin_circuit_t holds data necessary to build and use a circuit. */ typedef struct origin_circuit_t { @@ -2629,6 +2648,10 @@ typedef struct origin_circuit_t { * cannibalized circuits. */ unsigned int has_opened : 1; + /** Kludge to help us prevent the warn in bug #6475 and eventually + * debug why we are not seeing first hops in some cases. */ + path_state_t path_state : 2; + /** Set iff this is a hidden-service circuit which has timed out * according to our current circuit-build timeout, but which has * been kept around because it might still succeed in connecting to @@ -3008,23 +3031,46 @@ typedef struct { config_line_t *RecommendedVersions; config_line_t *RecommendedClientVersions; config_line_t *RecommendedServerVersions; - /** Whether dirservers refuse router descriptors with private IPs. */ + /** Whether dirservers allow router descriptors with private IPs. */ int DirAllowPrivateAddresses; + /** Whether routers accept EXTEND cells to routers with private IPs. */ + int ExtendAllowPrivateAddresses; char *User; /**< Name of user to run Tor as. */ char *Group; /**< Name of group to run Tor as. */ - config_line_t *ORPort; /**< Ports to listen on for OR connections. */ - config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */ + config_line_t *ORPort_lines; /**< Ports to listen on for OR connections. */ + /** Ports to listen on for SOCKS connections. */ + config_line_t *SocksPort_lines; /** Ports to listen on for transparent pf/netfilter connections. */ - config_line_t *TransPort; - config_line_t *NATDPort; /**< Ports to listen on for transparent natd + config_line_t *TransPort_lines; + config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd * connections. */ - config_line_t *ControlPort; /**< Port to listen on for control + config_line_t *ControlPort_lines; /**< Ports to listen on for control * connections. */ config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on * for control connections. */ + int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ - config_line_t *DirPort; /**< Port to listen on for directory connections. */ - config_line_t *DNSPort; /**< Port to listen on for DNS requests. */ + /** Ports to listen on for directory connections. */ + config_line_t *DirPort_lines; + config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */ + + /** @name port booleans + * + * Derived booleans: True iff there is a non-listener port on an AF_INET or + * AF_INET6 address of the given type configured in one of the _lines + * options above. + * + * @{ + */ + unsigned int ORPort_set : 1; + unsigned int SocksPort_set : 1; + unsigned int TransPort_set : 1; + unsigned int NATDPort_set : 1; + unsigned int ControlPort_set : 1; + unsigned int DirPort_set : 1; + unsigned int DNSPort_set : 1; + /**@}*/ + int AssumeReachable; /**< Whether to publish our descriptor regardless. */ int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory @@ -3268,6 +3314,7 @@ typedef struct { int AuthDirMaxServersPerAuthAddr; /**< Do not permit more than this * number of servers per IP address shared * with an authority. */ + int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */ /** If non-zero, always vote the Fast flag for any relay advertising * this amount of capacity or more. */ @@ -3434,6 +3481,13 @@ typedef struct { * over randomly chosen exits. */ int ClientRejectInternalAddresses; + /** If true, clients may connect over IPv6. XXX we don't really + enforce this -- clients _may_ set up outgoing IPv6 connections + even when this option is not set. */ + int ClientUseIPv6; + /** If true, prefer an IPv6 OR port over an IPv4 one. */ + int ClientPreferIPv6ORPort; + /** The length of time that we think a consensus should be fresh. */ int V3AuthVotingInterval; /** The length of time we think it will take to distribute votes. */ @@ -3515,8 +3569,8 @@ typedef struct { /** If true, do not enable IOCP on windows with bufferevents, even if * we think we could. */ int DisableIOCP; - /** For testing only: will go away in 0.2.3.x. */ - int _UseFilteringSSLBufferevents; + /** For testing only: will go away eventually. */ + int UseFilteringSSLBufferevents; /** Set to true if the TestingTorNetwork configuration option is set. * This is used so that options_validate() has a chance to realize that @@ -4207,12 +4261,15 @@ typedef struct rend_intro_point_t { * intro point. */ unsigned int rend_service_note_removing_intro_point_called : 1; - /** (Service side only) A digestmap recording the INTRODUCE2 cells - * this intro point's circuit has received. Each key is the digest - * of the RSA-encrypted part of a received INTRODUCE2 cell; each - * value is a pointer to the time_t at which the cell was received. - * This digestmap is used to prevent replay attacks. */ - digestmap_t *accepted_intro_rsa_parts; + /** (Service side only) A replay cache recording the RSA-encrypted parts + * of INTRODUCE2 cells this intro point's circuit has received. This is + * used to prevent replay attacks. */ + replaycache_t *accepted_intro_rsa_parts; + + /** (Service side only) Count of INTRODUCE2 cells accepted from this + * intro point. + */ + int accepted_introduce2_count; /** (Service side only) The time at which this intro point was first * published, or -1 if this intro point has not yet been diff --git a/src/or/policies.c b/src/or/policies.c index 3018803bc4..568bc88a05 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -712,7 +712,11 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port, /* no policy? accept all. */ return ADDR_POLICY_ACCEPTED; } else if (addr == NULL || tor_addr_is_null(addr)) { - tor_assert(port != 0); + if (port == 0) { + log_info(LD_BUG, "Rejecting null address with 0 port (family %d)", + addr ? tor_addr_family(addr) : -1); + return ADDR_POLICY_REJECTED; + } return compare_unknown_tor_addr_to_addr_policy(port, policy); } else if (port == 0) { return compare_known_tor_addr_to_addr_policy_noport(addr, policy); @@ -1004,8 +1008,7 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy, else addrpart = addrbuf; - result = tor_snprintf(buf, buflen, "%s%s%s %s", - (is_ip6&&format_for_desc)?"opt ":"", + result = tor_snprintf(buf, buflen, "%s%s %s", is_accept ? "accept" : "reject", (is_ip6&&format_for_desc)?"6":"", addrpart); diff --git a/src/or/relay.c b/src/or/relay.c index 3d261c2650..169286ea5d 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1175,7 +1175,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, case RELAY_COMMAND_EXTEND: { static uint64_t total_n_extend=0, total_nonearly=0; total_n_extend++; - if (conn) { + if (rh.stream_id) { log_fn(LOG_PROTOCOL_WARN, domain, "'extend' cell received for non-zero stream. Dropping."); return 0; @@ -1267,7 +1267,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if (layer_hint) { if (layer_hint->package_window + CIRCWINDOW_INCREMENT > CIRCWINDOW_START_MAX) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + /*XXXX024: Downgrade this back to LOG_PROTOCOL_WARN after a while*/ + log_fn(LOG_WARN, LD_PROTOCOL, "Bug/attack: unexpected sendme cell from exit relay. " "Closing circ."); return -END_CIRC_REASON_TORPROTOCOL; @@ -1279,7 +1280,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, } else { if (circ->package_window + CIRCWINDOW_INCREMENT > CIRCWINDOW_START_MAX) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + /*XXXX024: Downgrade this back to LOG_PROTOCOL_WARN after a while*/ + log_fn(LOG_WARN, LD_PROTOCOL, "Bug/attack: unexpected sendme cell from client. " "Closing circ."); return -END_CIRC_REASON_TORPROTOCOL; @@ -1833,7 +1835,7 @@ packed_cell_free_unchecked(packed_cell_t *cell) /** Allocate and return a new packed_cell_t. */ static INLINE packed_cell_t * -packed_cell_alloc(void) +packed_cell_new(void) { ++total_cells_allocated; return mp_pool_get(cell_pool); @@ -1862,7 +1864,7 @@ dump_cell_pool_usage(int severity) static INLINE packed_cell_t * packed_cell_copy(const cell_t *cell) { - packed_cell_t *c = packed_cell_alloc(); + packed_cell_t *c = packed_cell_new(); cell_pack(c, cell); c->next = NULL; return c; diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 6c751be27d..5b3b92e406 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -132,6 +132,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, crypt_path_t *cpath; off_t dh_offset; crypto_pk_t *intro_key = NULL; + int status = 0; tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING); tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY); @@ -161,7 +162,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc, } } - return -1; + status = -1; + goto cleanup; } /* first 20 bytes of payload are the hash of Bob's pk */ @@ -184,13 +186,16 @@ rend_client_send_introduction(origin_circuit_t *introcirc, smartlist_len(entry->parsed->intro_nodes)); if (rend_client_reextend_intro_circuit(introcirc)) { + status = -2; goto perm_err; } else { - return -1; + status = -1; + goto cleanup; } } if (crypto_pk_get_digest(intro_key, payload)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); + status = -2; goto perm_err; } @@ -202,10 +207,12 @@ rend_client_send_introduction(origin_circuit_t *introcirc, cpath->magic = CRYPT_PATH_MAGIC; if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) { log_warn(LD_BUG, "Internal error: couldn't allocate DH."); + status = -2; goto perm_err; } if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) { log_warn(LD_BUG, "Internal error: couldn't generate g^x."); + status = -2; goto perm_err; } } @@ -256,6 +263,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset, DH_KEY_LEN)<0) { log_warn(LD_BUG, "Internal error: couldn't extract g^x."); + status = -2; goto perm_err; } @@ -269,6 +277,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, PK_PKCS1_OAEP_PADDING, 0); if (r<0) { log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed."); + status = -2; goto perm_err; } @@ -288,7 +297,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc, introcirc->cpath->prev)<0) { /* introcirc is already marked for close. leave rendcirc alone. */ log_warn(LD_BUG, "Couldn't send INTRODUCE1 cell"); - return -2; + status = -2; + goto cleanup; } /* Now, we wait for an ACK or NAK on this circuit. */ @@ -299,12 +309,17 @@ rend_client_send_introduction(origin_circuit_t *introcirc, * state. */ introcirc->_base.timestamp_dirty = time(NULL); - return 0; + goto cleanup; + perm_err: if (!introcirc->_base.marked_for_close) circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL); circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL); - return -2; + cleanup: + memset(payload, 0, sizeof(payload)); + memset(tmp, 0, sizeof(tmp)); + + return status; } /** Called when a rendezvous circuit is open; sends a establish @@ -659,10 +674,17 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) time(NULL), chosen_replica) < 0) { log_warn(LD_REND, "Internal error: Computing v2 rendezvous " "descriptor ID did not succeed."); - return; + /* + * Hmm, can this write anything to descriptor_id and still fail? + * Let's clear it just to be safe. + * + * From here on, any returns should goto done which clears + * descriptor_id so we don't leave key-derived material on the stack. + */ + goto done; } if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0) - return; /* either success or failure, but we're done */ + goto done; /* either success or failure, but we're done */ } /* If we come here, there are no hidden service directories left. */ log_info(LD_REND, "Could not pick one of the responsible hidden " @@ -670,6 +692,10 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) "we already tried them all unsuccessfully."); /* Close pending connections. */ rend_client_desc_trynow(rend_query->onion_address); + + done: + memset(descriptor_id, 0, sizeof(descriptor_id)); + return; } @@ -1172,11 +1198,11 @@ rend_parse_service_authorization(const or_options_t *options, strmap_t *parsed = strmap_new(); smartlist_t *sl = smartlist_new(); rend_service_authorization_t *auth = NULL; + char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2]; + char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1]; for (line = options->HidServAuth; line; line = line->next) { char *onion_address, *descriptor_cookie; - char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2]; - char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1]; int auth_type_val = 0; auth = NULL; SMARTLIST_FOREACH(sl, char *, c, tor_free(c);); @@ -1253,6 +1279,8 @@ rend_parse_service_authorization(const or_options_t *options, } else { strmap_free(parsed, rend_service_authorization_strmap_item_free); } + memset(descriptor_cookie_tmp, 0, sizeof(descriptor_cookie_tmp)); + memset(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext)); return res; } diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 4722690c15..f6b1bf9f65 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -439,7 +439,7 @@ rend_intro_point_free(rend_intro_point_t *intro) crypto_pk_free(intro->intro_key); if (intro->accepted_intro_rsa_parts != NULL) { - digestmap_free(intro->accepted_intro_rsa_parts, _tor_free); + replaycache_free(intro->accepted_intro_rsa_parts); } tor_free(intro); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 6af4778dfc..bd8b13a762 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -7,6 +7,8 @@ * \brief The hidden-service side of rendezvous functionality. **/ +#define RENDSERVICE_PRIVATE + #include "or.h" #include "circuitbuild.h" #include "circuitlist.h" @@ -21,6 +23,7 @@ #include "router.h" #include "relay.h" #include "rephist.h" +#include "replaycache.h" #include "routerlist.h" #include "routerparse.h" @@ -28,9 +31,33 @@ static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest); static rend_intro_point_t *find_intro_point(origin_circuit_t *circ); +static extend_info_t *find_rp_for_intro( + const rend_intro_cell_t *intro, + uint8_t *need_free_out, char **err_msg_out); + static int intro_point_accepted_intro_count(rend_intro_point_t *intro); static int intro_point_should_expire_now(rend_intro_point_t *intro, time_t now); +struct rend_service_t; +static int rend_service_load_keys(struct rend_service_t *s); +static int rend_service_load_auth_keys(struct rend_service_t *s, + const char *hfname); + +static ssize_t rend_service_parse_intro_for_v0_or_v1( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out); +static ssize_t rend_service_parse_intro_for_v2( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out); +static ssize_t rend_service_parse_intro_for_v3( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out); /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. @@ -91,16 +118,12 @@ typedef struct rend_service_t { * up-to-date. */ time_t next_upload_time; /**< Scheduled next hidden service descriptor * upload time. */ - /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t - * of when they were received. Clients may send INTRODUCE1 cells - * for the same rendezvous point through two or more different - * introduction points; when they do, this digestmap keeps us from - * launching multiple simultaneous attempts to connect to the same - * rend point. */ - digestmap_t *accepted_intro_dh_parts; - /** Time at which we last removed expired values from - * accepted_intro_dh_parts. */ - time_t last_cleaned_accepted_intro_dh_parts; + /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to + * detect repeats. Clients may send INTRODUCE1 cells for the same + * rendezvous point through two or more different introduction points; + * when they do, this keeps us from launching multiple simultaneous attempts + * to connect to the same rend point. */ + replaycache_t *accepted_intro_dh_parts; } rend_service_t; /** A list of rend_service_t's for services run on this OP. @@ -135,7 +158,9 @@ rend_authorized_client_free(rend_authorized_client_t *client) return; if (client->client_key) crypto_pk_free(client->client_key); + tor_strclear(client->client_name); tor_free(client->client_name); + memset(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie)); tor_free(client); } @@ -171,7 +196,9 @@ rend_service_free(rend_service_t *service) rend_authorized_client_free(c);); smartlist_free(service->clients); } - digestmap_free(service->accepted_intro_dh_parts, _tor_free); + if (service->accepted_intro_dh_parts) { + replaycache_free(service->accepted_intro_dh_parts); + } tor_free(service); } @@ -609,231 +636,274 @@ rend_service_update_descriptor(rend_service_t *service) /** Load and/or generate private keys for all hidden services, possibly * including keys for client authorization. Return 0 on success, -1 on - * failure. - */ + * failure. */ int -rend_service_load_keys(void) +rend_service_load_all_keys(void) { - int r = 0; - char fname[512]; - char buf[1500]; - SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) { if (s->private_key) continue; log_info(LD_REND, "Loading hidden-service keys from \"%s\"", s->directory); - /* Check/create directory */ - if (check_private_dir(s->directory, CPD_CREATE, get_options()->User) < 0) + if (rend_service_load_keys(s) < 0) return -1; + } SMARTLIST_FOREACH_END(s); + + return 0; +} + +/** Load and/or generate private keys for the hidden service <b>s</b>, + * possibly including keys for client authorization. Return 0 on success, -1 + * on failure. */ +static int +rend_service_load_keys(rend_service_t *s) +{ + char fname[512]; + char buf[128]; + + /* Check/create directory */ + if (check_private_dir(s->directory, CPD_CREATE, get_options()->User) < 0) + return -1; + + /* Load key */ + if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || + strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname)) + >= sizeof(fname)) { + log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".", + s->directory); + return -1; + } + s->private_key = init_key_from_file(fname, 1, LOG_ERR); + if (!s->private_key) + return -1; + + /* Create service file */ + if (rend_get_service_id(s->private_key, s->service_id)<0) { + log_warn(LD_BUG, "Internal error: couldn't encode service ID."); + return -1; + } + if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { + log_warn(LD_BUG, "Couldn't compute hash of public key."); + return -1; + } + if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || + strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) + >= sizeof(fname)) { + log_warn(LD_CONFIG, "Directory name too long to store hostname file:" + " \"%s\".", s->directory); + return -1; + } + + tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id); + if (write_str_to_file(fname,buf,0)<0) { + log_warn(LD_CONFIG, "Could not write onion address to hostname file."); + memset(buf, 0, sizeof(buf)); + return -1; + } + memset(buf, 0, sizeof(buf)); - /* Load key */ - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".", - s->directory); + /* If client authorization is configured, load or generate keys. */ + if (s->auth_type != REND_NO_AUTH) { + if (rend_service_load_auth_keys(s, fname) < 0) return -1; + } + + return 0; +} + +/** Load and/or generate client authorization keys for the hidden service + * <b>s</b>, which stores its hostname in <b>hfname</b>. Return 0 on success, + * -1 on failure. */ +static int +rend_service_load_auth_keys(rend_service_t *s, const char *hfname) +{ + int r = 0; + char cfname[512]; + char *client_keys_str = NULL; + strmap_t *parsed_clients = strmap_new(); + FILE *cfile, *hfile; + open_file_t *open_cfile = NULL, *open_hfile = NULL; + char extended_desc_cookie[REND_DESC_COOKIE_LEN+1]; + char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1]; + char service_id[16+1]; + char buf[1500]; + + /* Load client keys and descriptor cookies, if available. */ + if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys", + s->directory)<0) { + log_warn(LD_CONFIG, "Directory name too long to store client keys " + "file: \"%s\".", s->directory); + goto err; + } + client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL); + if (client_keys_str) { + if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) { + log_warn(LD_CONFIG, "Previously stored client_keys file could not " + "be parsed."); + goto err; + } else { + log_info(LD_CONFIG, "Parsed %d previously stored client entries.", + strmap_size(parsed_clients)); } - s->private_key = init_key_from_file(fname, 1, LOG_ERR); - if (!s->private_key) - return -1; + } - /* Create service file */ - if (rend_get_service_id(s->private_key, s->service_id)<0) { - log_warn(LD_BUG, "Internal error: couldn't encode service ID."); - return -1; + /* Prepare client_keys and hostname files. */ + if (!(cfile = start_writing_to_stdio_file(cfname, + OPEN_FLAGS_REPLACE | O_TEXT, + 0600, &open_cfile))) { + log_warn(LD_CONFIG, "Could not open client_keys file %s", + escaped(cfname)); + goto err; + } + + if (!(hfile = start_writing_to_stdio_file(hfname, + OPEN_FLAGS_REPLACE | O_TEXT, + 0600, &open_hfile))) { + log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(hfname)); + goto err; + } + + /* Either use loaded keys for configured clients or generate new + * ones if a client is new. */ + SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client) { + rend_authorized_client_t *parsed = + strmap_get(parsed_clients, client->client_name); + int written; + size_t len; + /* Copy descriptor cookie from parsed entry or create new one. */ + if (parsed) { + memcpy(client->descriptor_cookie, parsed->descriptor_cookie, + REND_DESC_COOKIE_LEN); + } else { + crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN); } - if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { - log_warn(LD_BUG, "Couldn't compute hash of public key."); - return -1; + if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, + client->descriptor_cookie, + REND_DESC_COOKIE_LEN) < 0) { + log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); + goto err; } - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store hostname file:" - " \"%s\".", s->directory); - return -1; + /* Copy client key from parsed entry or create new one if required. */ + if (parsed && parsed->client_key) { + client->client_key = crypto_pk_dup_key(parsed->client_key); + } else if (s->auth_type == REND_STEALTH_AUTH) { + /* Create private key for client. */ + crypto_pk_t *prkey = NULL; + if (!(prkey = crypto_pk_new())) { + log_warn(LD_BUG,"Error constructing client key"); + goto err; + } + if (crypto_pk_generate_key(prkey)) { + log_warn(LD_BUG,"Error generating client key"); + crypto_pk_free(prkey); + goto err; + } + if (crypto_pk_check_key(prkey) <= 0) { + log_warn(LD_BUG,"Generated client key seems invalid"); + crypto_pk_free(prkey); + goto err; + } + client->client_key = prkey; } - tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id); - if (write_str_to_file(fname,buf,0)<0) { - log_warn(LD_CONFIG, "Could not write onion address to hostname file."); - return -1; + /* Add entry to client_keys file. */ + desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */ + written = tor_snprintf(buf, sizeof(buf), + "client-name %s\ndescriptor-cookie %s\n", + client->client_name, desc_cook_out); + if (written < 0) { + log_warn(LD_BUG, "Could not write client entry."); + goto err; } - - /* If client authorization is configured, load or generate keys. */ - if (s->auth_type != REND_NO_AUTH) { - char *client_keys_str = NULL; - strmap_t *parsed_clients = strmap_new(); - char cfname[512]; - FILE *cfile, *hfile; - open_file_t *open_cfile = NULL, *open_hfile = NULL; - - /* Load client keys and descriptor cookies, if available. */ - if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys", - s->directory)<0) { - log_warn(LD_CONFIG, "Directory name too long to store client keys " - "file: \"%s\".", s->directory); + if (client->client_key) { + char *client_key_out = NULL; + if (crypto_pk_write_private_key_to_string(client->client_key, + &client_key_out, &len) != 0) { + log_warn(LD_BUG, "Internal error: " + "crypto_pk_write_private_key_to_string() failed."); goto err; } - client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL); - if (client_keys_str) { - if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) { - log_warn(LD_CONFIG, "Previously stored client_keys file could not " - "be parsed."); - goto err; - } else { - log_info(LD_CONFIG, "Parsed %d previously stored client entries.", - strmap_size(parsed_clients)); - tor_free(client_keys_str); - } - } - - /* Prepare client_keys and hostname files. */ - if (!(cfile = start_writing_to_stdio_file(cfname, - OPEN_FLAGS_REPLACE | O_TEXT, - 0600, &open_cfile))) { - log_warn(LD_CONFIG, "Could not open client_keys file %s", - escaped(cfname)); + if (rend_get_service_id(client->client_key, service_id)<0) { + log_warn(LD_BUG, "Internal error: couldn't encode service ID."); + /* + * len is string length, not buffer length, but last byte is NUL + * anyway. + */ + memset(client_key_out, 0, len); + tor_free(client_key_out); goto err; } - if (!(hfile = start_writing_to_stdio_file(fname, - OPEN_FLAGS_REPLACE | O_TEXT, - 0600, &open_hfile))) { - log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(fname)); + written = tor_snprintf(buf + written, sizeof(buf) - written, + "client-key\n%s", client_key_out); + memset(client_key_out, 0, len); + tor_free(client_key_out); + if (written < 0) { + log_warn(LD_BUG, "Could not write client entry."); goto err; } + } - /* Either use loaded keys for configured clients or generate new - * ones if a client is new. */ - SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client) - { - char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1]; - char service_id[16+1]; - rend_authorized_client_t *parsed = - strmap_get(parsed_clients, client->client_name); - int written; - size_t len; - /* Copy descriptor cookie from parsed entry or create new one. */ - if (parsed) { - memcpy(client->descriptor_cookie, parsed->descriptor_cookie, - REND_DESC_COOKIE_LEN); - } else { - crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN); - } - if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, - client->descriptor_cookie, - REND_DESC_COOKIE_LEN) < 0) { - log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); - strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); - return -1; - } - /* Copy client key from parsed entry or create new one if required. */ - if (parsed && parsed->client_key) { - client->client_key = crypto_pk_dup_key(parsed->client_key); - } else if (s->auth_type == REND_STEALTH_AUTH) { - /* Create private key for client. */ - crypto_pk_t *prkey = NULL; - if (!(prkey = crypto_pk_new())) { - log_warn(LD_BUG,"Error constructing client key"); - goto err; - } - if (crypto_pk_generate_key(prkey)) { - log_warn(LD_BUG,"Error generating client key"); - crypto_pk_free(prkey); - goto err; - } - if (crypto_pk_check_key(prkey) <= 0) { - log_warn(LD_BUG,"Generated client key seems invalid"); - crypto_pk_free(prkey); - goto err; - } - client->client_key = prkey; - } - /* Add entry to client_keys file. */ - desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */ - written = tor_snprintf(buf, sizeof(buf), - "client-name %s\ndescriptor-cookie %s\n", - client->client_name, desc_cook_out); - if (written < 0) { - log_warn(LD_BUG, "Could not write client entry."); - goto err; - } - if (client->client_key) { - char *client_key_out = NULL; - crypto_pk_write_private_key_to_string(client->client_key, - &client_key_out, &len); - if (rend_get_service_id(client->client_key, service_id)<0) { - log_warn(LD_BUG, "Internal error: couldn't encode service ID."); - tor_free(client_key_out); - goto err; - } - written = tor_snprintf(buf + written, sizeof(buf) - written, - "client-key\n%s", client_key_out); - tor_free(client_key_out); - if (written < 0) { - log_warn(LD_BUG, "Could not write client entry."); - goto err; - } - } - - if (fputs(buf, cfile) < 0) { - log_warn(LD_FS, "Could not append client entry to file: %s", - strerror(errno)); - goto err; - } - - /* Add line to hostname file. */ - if (s->auth_type == REND_BASIC_AUTH) { - /* Remove == signs (newline has been removed above). */ - desc_cook_out[strlen(desc_cook_out)-2] = '\0'; - tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", - s->service_id, desc_cook_out, client->client_name); - } else { - char extended_desc_cookie[REND_DESC_COOKIE_LEN+1]; - memcpy(extended_desc_cookie, client->descriptor_cookie, - REND_DESC_COOKIE_LEN); - extended_desc_cookie[REND_DESC_COOKIE_LEN] = - ((int)s->auth_type - 1) << 4; - if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, - extended_desc_cookie, - REND_DESC_COOKIE_LEN+1) < 0) { - log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); - goto err; - } - desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and - newline. */ - tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", - service_id, desc_cook_out, client->client_name); - } + if (fputs(buf, cfile) < 0) { + log_warn(LD_FS, "Could not append client entry to file: %s", + strerror(errno)); + goto err; + } - if (fputs(buf, hfile)<0) { - log_warn(LD_FS, "Could not append host entry to file: %s", - strerror(errno)); - goto err; - } + /* Add line to hostname file. */ + if (s->auth_type == REND_BASIC_AUTH) { + /* Remove == signs (newline has been removed above). */ + desc_cook_out[strlen(desc_cook_out)-2] = '\0'; + tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", + s->service_id, desc_cook_out, client->client_name); + } else { + memcpy(extended_desc_cookie, client->descriptor_cookie, + REND_DESC_COOKIE_LEN); + extended_desc_cookie[REND_DESC_COOKIE_LEN] = + ((int)s->auth_type - 1) << 4; + if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, + extended_desc_cookie, + REND_DESC_COOKIE_LEN+1) < 0) { + log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); + goto err; } - SMARTLIST_FOREACH_END(client); + desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and + newline. */ + tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", + service_id, desc_cook_out, client->client_name); + } - goto done; - err: - r = -1; - done: - tor_free(client_keys_str); - strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); - if (r<0) { - if (open_cfile) - abort_writing_to_file(open_cfile); - if (open_hfile) - abort_writing_to_file(open_hfile); - return r; - } else { - finish_writing_to_file(open_cfile); - finish_writing_to_file(open_hfile); - } + if (fputs(buf, hfile)<0) { + log_warn(LD_FS, "Could not append host entry to file: %s", + strerror(errno)); + goto err; } - } SMARTLIST_FOREACH_END(s); + } SMARTLIST_FOREACH_END(client); + + finish_writing_to_file(open_cfile); + finish_writing_to_file(open_hfile); + + goto done; + err: + r = -1; + if (open_cfile) + abort_writing_to_file(open_cfile); + if (open_hfile) + abort_writing_to_file(open_hfile); + done: + if (client_keys_str) { + tor_strclear(client_keys_str); + tor_free(client_keys_str); + } + strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); + + memset(cfname, 0, sizeof(cfname)); + + /* Clear stack buffers that held key-derived material. */ + memset(buf, 0, sizeof(buf)); + memset(desc_cook_out, 0, sizeof(desc_cook_out)); + memset(service_id, 0, sizeof(service_id)); + memset(extended_desc_cookie, 0, sizeof(extended_desc_cookie)); + return r; } @@ -906,26 +976,6 @@ rend_check_authorization(rend_service_t *service, return 1; } -/** Remove elements from <b>service</b>'s replay cache that are old enough to - * be noticed by timestamp checking. */ -static void -clean_accepted_intro_dh_parts(rend_service_t *service, time_t now) -{ - const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL; - - service->last_cleaned_accepted_intro_dh_parts = now; - if (!service->accepted_intro_dh_parts) - return; - - DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest, - time_t *, t) { - if (*t < cutoff) { - tor_free(t); - MAP_DEL_CURRENT(digest); - } - } DIGESTMAP_FOREACH_END; -} - /** Called when <b>intro</b> will soon be removed from * <b>service</b>'s list of intro points. */ static void @@ -1033,42 +1083,55 @@ rend_service_note_removing_intro_point(rend_service_t *service, /** Respond to an INTRODUCE2 cell by launching a circuit to the chosen * rendezvous point. */ - /* XXXX024 this function sure could use some organizing. -RD */ int rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, size_t request_len) { - char *ptr, *r_cookie; - extend_info_t *extend_info = NULL; + /* Global status stuff */ + int status = 0, result; + const or_options_t *options = get_options(); + char *err_msg = NULL; + const char *stage_descr = NULL; + int reason = END_CIRC_REASON_TORPROTOCOL; + /* Service/circuit/key stuff we can learn before parsing */ + char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; + rend_service_t *service = NULL; + rend_intro_point_t *intro_point = NULL; + crypto_pk_t *intro_key = NULL; + /* Parsed cell */ + rend_intro_cell_t *parsed_req = NULL; + /* Rendezvous point */ + extend_info_t *rp = NULL; + /* + * We need to look up and construct the extend_info_t for v0 and v1, + * but all the info is in the cell and it's constructed by the parser + * for v2 and v3, so freeing it would be a double-free. Use this to + * keep track of whether we should free it. + */ + uint8_t need_rp_free = 0; + /* XXX not handled yet */ char buf[RELAY_PAYLOAD_SIZE]; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ - rend_service_t *service; - rend_intro_point_t *intro_point; - int r, i, v3_shift = 0; - size_t len, keylen; + int i; crypto_dh_t *dh = NULL; origin_circuit_t *launched = NULL; crypt_path_t *cpath = NULL; - char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char hexcookie[9]; int circ_needs_uptime; - int reason = END_CIRC_REASON_TORPROTOCOL; - crypto_pk_t *intro_key; char intro_key_digest[DIGEST_LEN]; - int auth_type; size_t auth_len = 0; char auth_data[REND_DESC_COOKIE_LEN]; - crypto_digest_t *digest = NULL; time_t now = time(NULL); char diffie_hellman_hash[DIGEST_LEN]; - time_t *access_time; - const or_options_t *options = get_options(); + time_t elapsed; + int replay; + /* Do some initial validation and logging before we parse the cell */ if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) { log_warn(LD_PROTOCOL, "Got an INTRODUCE2 over a non-introduction circuit %d.", circuit->_base.n_circ_id); - return -1; + goto err; } #ifndef NON_ANONYMOUS_MODE_ENABLED @@ -1076,218 +1139,145 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, #endif tor_assert(circuit->rend_data); + /* We'll use this in a bazillion log messages */ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); - log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.", - escaped(serviceid), circuit->_base.n_circ_id); - - /* min key length plus digest length plus nickname length */ - if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+ - DH_KEY_LEN+42) { - log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.", - circuit->_base.n_circ_id); - return -1; - } /* look up service depending on circuit. */ - service = rend_service_get_by_pk_digest( - circuit->rend_data->rend_pk_digest); + service = + rend_service_get_by_pk_digest(circuit->rend_data->rend_pk_digest); if (!service) { - log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro " + log_warn(LD_BUG, + "Internal error: Got an INTRODUCE2 cell on an intro " "circ for an unrecognized service %s.", escaped(serviceid)); - return -1; - } - - /* use intro key instead of service key. */ - intro_key = circuit->intro_key; - - /* first DIGEST_LEN bytes of request is intro or service pk digest */ - crypto_pk_get_digest(intro_key, intro_key_digest); - if (tor_memneq(intro_key_digest, request, DIGEST_LEN)) { - base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, - (char*)request, REND_SERVICE_ID_LEN); - log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).", - escaped(serviceid)); - return -1; - } - - keylen = crypto_pk_keysize(intro_key); - if (request_len < keylen+DIGEST_LEN) { - log_warn(LD_PROTOCOL, - "PK-encrypted portion of INTRODUCE2 cell was truncated."); - return -1; + goto err; } intro_point = find_intro_point(circuit); if (intro_point == NULL) { - log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ " - "(for service %s) with no corresponding rend_intro_point_t.", + log_warn(LD_BUG, + "Internal error: Got an INTRODUCE2 cell on an " + "intro circ (for service %s) with no corresponding " + "rend_intro_point_t.", escaped(serviceid)); - return -1; + goto err; } - if (!service->accepted_intro_dh_parts) - service->accepted_intro_dh_parts = digestmap_new(); + log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.", + escaped(serviceid), circuit->_base.n_circ_id); - if (!intro_point->accepted_intro_rsa_parts) - intro_point->accepted_intro_rsa_parts = digestmap_new(); + /* use intro key instead of service key. */ + intro_key = circuit->intro_key; - { - char pkpart_digest[DIGEST_LEN]; - /* Check for replay of PK-encrypted portion. */ - crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen); - access_time = digestmap_get(intro_point->accepted_intro_rsa_parts, - pkpart_digest); - if (access_time != NULL) { - log_warn(LD_REND, "Possible replay detected! We received an " - "INTRODUCE2 cell with same PK-encrypted part %d seconds ago. " - "Dropping cell.", (int)(now-*access_time)); - return -1; - } - access_time = tor_malloc(sizeof(time_t)); - *access_time = now; - digestmap_set(intro_point->accepted_intro_rsa_parts, - pkpart_digest, access_time); + tor_free(err_msg); + stage_descr = NULL; + + stage_descr = "early parsing"; + /* Early parsing pass (get pk, ciphertext); type 2 is INTRODUCE2 */ + parsed_req = + rend_service_begin_parse_intro(request, request_len, 2, &err_msg); + if (!parsed_req) { + goto log_error; + } else if (err_msg) { + log_info(LD_REND, "%s on circ %d.", err_msg, circuit->_base.n_circ_id); + tor_free(err_msg); + } + + stage_descr = "early validation"; + /* Early validation of pk/ciphertext part */ + result = rend_service_validate_intro_early(parsed_req, &err_msg); + if (result < 0) { + goto log_error; + } else if (err_msg) { + log_info(LD_REND, "%s on circ %d.", err_msg, circuit->_base.n_circ_id); + tor_free(err_msg); + } + + /* make sure service replay caches are present */ + if (!service->accepted_intro_dh_parts) { + service->accepted_intro_dh_parts = + replaycache_new(REND_REPLAY_TIME_INTERVAL, + REND_REPLAY_TIME_INTERVAL); + } + + if (!intro_point->accepted_intro_rsa_parts) { + intro_point->accepted_intro_rsa_parts = replaycache_new(0, 0); + } + + /* check for replay of PK-encrypted portion. */ + replay = replaycache_add_test_and_elapsed( + intro_point->accepted_intro_rsa_parts, + parsed_req->ciphertext, (int)parsed_req->ciphertext_len, + &elapsed); + + if (replay) { + log_warn(LD_REND, + "Possible replay detected! We received an " + "INTRODUCE2 cell with same PK-encrypted part %d " + "seconds ago. Dropping cell.", + (int)elapsed); + goto err; } - /* Next N bytes is encrypted with service key */ - note_crypto_pk_op(REND_SERVER); - r = crypto_pk_private_hybrid_decrypt( - intro_key,buf,sizeof(buf), - (char*)(request+DIGEST_LEN),request_len-DIGEST_LEN, - PK_PKCS1_OAEP_PADDING,1); - if (r<0) { - log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell."); - return -1; + stage_descr = "decryption"; + /* Now try to decrypt it */ + result = rend_service_decrypt_intro(parsed_req, intro_key, &err_msg); + if (result < 0) { + goto log_error; + } else if (err_msg) { + log_info(LD_REND, "%s on circ %d.", err_msg, circuit->_base.n_circ_id); + tor_free(err_msg); } - len = r; - if (*buf == 3) { - /* Version 3 INTRODUCE2 cell. */ - v3_shift = 1; - auth_type = buf[1]; - switch (auth_type) { - case REND_BASIC_AUTH: - /* fall through */ - case REND_STEALTH_AUTH: - auth_len = ntohs(get_uint16(buf+2)); - if (auth_len != REND_DESC_COOKIE_LEN) { - log_info(LD_REND, "Wrong auth data size %d, should be %d.", - (int)auth_len, REND_DESC_COOKIE_LEN); - return -1; - } - memcpy(auth_data, buf+4, sizeof(auth_data)); - v3_shift += 2+REND_DESC_COOKIE_LEN; - break; - case REND_NO_AUTH: - break; - default: - log_info(LD_REND, "Unknown authorization type '%d'", auth_type); - } - - /* Skip the timestamp field. We no longer use it. */ - v3_shift += 4; - } - if (*buf == 2 || *buf == 3) { - /* Version 2 INTRODUCE2 cell. */ - int klen; - extend_info = tor_malloc_zero(sizeof(extend_info_t)); - tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf+v3_shift+1)); - extend_info->port = ntohs(get_uint16(buf+v3_shift+5)); - memcpy(extend_info->identity_digest, buf+v3_shift+7, - DIGEST_LEN); - extend_info->nickname[0] = '$'; - base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1, - extend_info->identity_digest, DIGEST_LEN); - - klen = ntohs(get_uint16(buf+v3_shift+7+DIGEST_LEN)); - if ((int)len != v3_shift+7+DIGEST_LEN+2+klen+20+128) { - log_warn(LD_PROTOCOL, "Bad length %u for version %d INTRODUCE2 cell.", - (int)len, *buf); - reason = END_CIRC_REASON_TORPROTOCOL; - goto err; - } - extend_info->onion_key = - crypto_pk_asn1_decode(buf+v3_shift+7+DIGEST_LEN+2, klen); - if (!extend_info->onion_key) { - log_warn(LD_PROTOCOL, "Error decoding onion key in version %d " - "INTRODUCE2 cell.", *buf); - reason = END_CIRC_REASON_TORPROTOCOL; - goto err; - } - ptr = buf+v3_shift+7+DIGEST_LEN+2+klen; - len -= v3_shift+7+DIGEST_LEN+2+klen; - } else { - char *rp_nickname; - size_t nickname_field_len; - const node_t *node; - int version; - if (*buf == 1) { - rp_nickname = buf+1; - nickname_field_len = MAX_HEX_NICKNAME_LEN+1; - version = 1; - } else { - nickname_field_len = MAX_NICKNAME_LEN+1; - rp_nickname = buf; - version = 0; - } - ptr=memchr(rp_nickname,0,nickname_field_len); - if (!ptr || ptr == rp_nickname) { - log_warn(LD_PROTOCOL, - "Couldn't find a nul-padded nickname in INTRODUCE2 cell."); - return -1; - } - if ((version == 0 && !is_legal_nickname(rp_nickname)) || - (version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) { - log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell."); - return -1; - } - /* Okay, now we know that a nickname is at the start of the buffer. */ - ptr = rp_nickname+nickname_field_len; - len -= nickname_field_len; - len -= rp_nickname - buf; /* also remove header space used by version, if - * any */ - node = node_get_by_nickname(rp_nickname, 0); - if (!node) { - log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.", - escaped_safe_str_client(rp_nickname)); - /* XXXX Add a no-such-router reason? */ - reason = END_CIRC_REASON_TORPROTOCOL; - goto err; - } - extend_info = extend_info_from_node(node, 0); + stage_descr = "late parsing"; + /* Parse the plaintext */ + result = rend_service_parse_intro_plaintext(parsed_req, &err_msg); + if (result < 0) { + goto log_error; + } else if (err_msg) { + log_info(LD_REND, "%s on circ %d.", err_msg, circuit->_base.n_circ_id); + tor_free(err_msg); } - if (len != REND_COOKIE_LEN+DH_KEY_LEN) { - log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 cell.", (int)len); - reason = END_CIRC_REASON_TORPROTOCOL; - goto err; + stage_descr = "late validation"; + /* Validate the parsed plaintext parts */ + result = rend_service_validate_intro_late(parsed_req, &err_msg); + if (result < 0) { + goto log_error; + } else if (err_msg) { + log_info(LD_REND, "%s on circ %d.", err_msg, circuit->_base.n_circ_id); + tor_free(err_msg); } + stage_descr = NULL; + + /* Increment INTRODUCE2 counter */ + ++(intro_point->accepted_introduce2_count); + + /* Find the rendezvous point */ + rp = find_rp_for_intro(parsed_req, &need_rp_free, &err_msg); + if (!rp) + goto log_error; /* Check if we'd refuse to talk to this router */ if (options->StrictNodes && - routerset_contains_extendinfo(options->ExcludeNodes, extend_info)) { + routerset_contains_extendinfo(options->ExcludeNodes, rp)) { log_warn(LD_REND, "Client asked to rendezvous at a relay that we " "exclude, and StrictNodes is set. Refusing service."); reason = END_CIRC_REASON_INTERNAL; /* XXX might leak why we refused */ goto err; } - r_cookie = ptr; - base16_encode(hexcookie,9,r_cookie,4); - - /* Determine hash of Diffie-Hellman, part 1 to detect replays. */ - digest = crypto_digest_new(); - crypto_digest_add_bytes(digest, ptr+REND_COOKIE_LEN, DH_KEY_LEN); - crypto_digest_get_digest(digest, diffie_hellman_hash, DIGEST_LEN); - crypto_digest_free(digest); + base16_encode(hexcookie, 9, (const char *)(parsed_req->rc), 4); /* Check whether there is a past request with the same Diffie-Hellman, * part 1. */ - access_time = digestmap_get(service->accepted_intro_dh_parts, - diffie_hellman_hash); - if (access_time != NULL) { + replay = replaycache_add_test_and_elapsed( + service->accepted_intro_dh_parts, + parsed_req->dh, DH_KEY_LEN, + &elapsed); + + if (replay) { /* A Tor client will send a new INTRODUCE1 cell with the same rend * cookie and DH public key as its previous one if its intro circ * times out while in state CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT . @@ -1299,21 +1289,10 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, "INTRODUCE2 cell with same first part of " "Diffie-Hellman handshake %d seconds ago. Dropping " "cell.", - (int) (now - *access_time)); + (int) elapsed); goto err; } - /* Add request to access history, including time and hash of Diffie-Hellman, - * part 1, and possibly remove requests from the history that are older than - * one hour. */ - access_time = tor_malloc(sizeof(time_t)); - *access_time = now; - digestmap_set(service->accepted_intro_dh_parts, - diffie_hellman_hash, access_time); - if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL - < now) - clean_accepted_intro_dh_parts(service, now); - /* If the service performs client authorization, check included auth data. */ if (service->clients) { if (auth_len > 0) { @@ -1341,7 +1320,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, reason = END_CIRC_REASON_INTERNAL; goto err; } - if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, ptr+REND_COOKIE_LEN, + if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, + (char *)(parsed_req->dh), DH_KEY_LEN, keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { log_warn(LD_BUG, "Internal error: couldn't complete DH handshake"); @@ -1360,7 +1340,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; launched = circuit_launch_by_extend_info( - CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, flags); + CIRCUIT_PURPOSE_S_CONNECT_REND, rp, flags); if (launched) break; @@ -1368,7 +1348,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, if (!launched) { /* give up */ log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous " "point %s for service %s.", - safe_str_client(extend_info_describe(extend_info)), + safe_str_client(extend_info_describe(rp)), serviceid); reason = END_CIRC_REASON_CONNECTFAILED; goto err; @@ -1376,7 +1356,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, log_info(LD_REND, "Accepted intro; launching circuit to %s " "(cookie %s) for service %s.", - safe_str_client(extend_info_describe(extend_info)), + safe_str_client(extend_info_describe(rp)), hexcookie, serviceid); tor_assert(launched->build_state); /* Fill in the circuit's state. */ @@ -1384,7 +1364,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, memcpy(launched->rend_data->rend_pk_digest, circuit->rend_data->rend_pk_digest, DIGEST_LEN); - memcpy(launched->rend_data->rend_cookie, r_cookie, REND_COOKIE_LEN); + memcpy(launched->rend_data->rend_cookie, parsed_req->rc, REND_COOKIE_LEN); strlcpy(launched->rend_data->onion_address, service->service_id, sizeof(launched->rend_data->onion_address)); @@ -1402,19 +1382,878 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0) goto err; memcpy(cpath->handshake_digest, keys, DIGEST_LEN); - if (extend_info) extend_info_free(extend_info); - memset(keys, 0, sizeof(keys)); - return 0; + goto done; + + log_error: + if (!err_msg) { + if (stage_descr) { + tor_asprintf(&err_msg, + "unknown %s error for INTRODUCE2", stage_descr); + } else { + err_msg = tor_strdup("unknown error for INTRODUCE2"); + } + } + + log_warn(LD_REND, "%s on circ %d", err_msg, circuit->_base.n_circ_id); err: - memset(keys, 0, sizeof(keys)); + status = -1; if (dh) crypto_dh_free(dh); - if (launched) + if (launched) { circuit_mark_for_close(TO_CIRCUIT(launched), reason); - if (extend_info) extend_info_free(extend_info); + } + tor_free(err_msg); + + done: + memset(keys, 0, sizeof(keys)); + memset(buf, 0, sizeof(buf)); + memset(serviceid, 0, sizeof(serviceid)); + memset(hexcookie, 0, sizeof(hexcookie)); + memset(intro_key_digest, 0, sizeof(intro_key_digest)); + memset(auth_data, 0, sizeof(auth_data)); + memset(diffie_hellman_hash, 0, sizeof(diffie_hellman_hash)); + + /* Free the parsed cell */ + if (parsed_req) { + rend_service_free_intro(parsed_req); + parsed_req = NULL; + } + + /* Free rp if we must */ + if (need_rp_free) extend_info_free(rp); + + return status; +} + +/** Given a parsed and decrypted INTRODUCE2, find the rendezvous point or + * return NULL and an error string if we can't. + */ + +static extend_info_t * +find_rp_for_intro(const rend_intro_cell_t *intro, + uint8_t *need_free_out, char **err_msg_out) +{ + extend_info_t *rp = NULL; + char *err_msg = NULL; + const char *rp_nickname = NULL; + const node_t *node = NULL; + uint8_t need_free = 0; + + if (!intro || !need_free_out) { + if (err_msg_out) + err_msg = tor_strdup("Bad parameters to find_rp_for_intro()"); + + goto err; + } + + if (intro->version == 0 || intro->version == 1) { + if (intro->version == 1) rp_nickname = (const char *)(intro->u.v1.rp); + else rp_nickname = (const char *)(intro->u.v0.rp); + + node = node_get_by_nickname(rp_nickname, 0); + if (!node) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "Couldn't find router %s named in INTRODUCE2 cell", + escaped_safe_str_client(rp_nickname)); + } + + goto err; + } + + rp = extend_info_from_node(node, 0); + if (!rp) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "Could build extend_info_t for router %s named " + "in INTRODUCE2 cell", + escaped_safe_str_client(rp_nickname)); + } + + goto err; + } else { + need_free = 1; + } + } else if (intro->version == 2) { + rp = intro->u.v2.extend_info; + } else if (intro->version == 3) { + rp = intro->u.v3.extend_info; + } else { + if (err_msg_out) { + tor_asprintf(&err_msg, + "Unknown version %d in INTRODUCE2 cell", + (int)(intro->version)); + } + + goto err; + } + + goto done; + + err: + if (err_msg_out) *err_msg_out = err_msg; + else tor_free(err_msg); + + done: + if (rp && need_free_out) *need_free_out = need_free; + + return rp; +} + +/** Remove unnecessary parts from a rend_intro_cell_t - the ciphertext if + * already decrypted, the plaintext too if already parsed + */ + +void +rend_service_compact_intro(rend_intro_cell_t *request) +{ + if (!request) return; + + if ((request->plaintext && request->plaintext_len > 0) || + request->parsed) { + tor_free(request->ciphertext); + request->ciphertext_len = 0; + } + + if (request->parsed) { + tor_free(request->plaintext); + request->plaintext_len = 0; + } +} + +/** Free a parsed INTRODUCE1 or INTRODUCE2 cell that was allocated by + * rend_service_parse_intro(). + */ +void +rend_service_free_intro(rend_intro_cell_t *request) +{ + if (!request) { + log_info(LD_BUG, "rend_service_free_intro() called with NULL request!"); + return; + } + + /* Free ciphertext */ + tor_free(request->ciphertext); + request->ciphertext_len = 0; + + /* Have plaintext? */ + if (request->plaintext) { + /* Zero it out just to be safe */ + memset(request->plaintext, 0, request->plaintext_len); + tor_free(request->plaintext); + request->plaintext_len = 0; + } + + /* Have parsed plaintext? */ + if (request->parsed) { + switch (request->version) { + case 0: + case 1: + /* + * Nothing more to do; these formats have no further pointers + * in them. + */ + break; + case 2: + extend_info_free(request->u.v2.extend_info); + request->u.v2.extend_info = NULL; + break; + case 3: + if (request->u.v3.auth_data) { + memset(request->u.v3.auth_data, 0, request->u.v3.auth_len); + tor_free(request->u.v3.auth_data); + } + + extend_info_free(request->u.v3.extend_info); + request->u.v3.extend_info = NULL; + break; + default: + log_info(LD_BUG, + "rend_service_free_intro() saw unknown protocol " + "version %d.", + request->version); + } + } + + /* Zero it out to make sure sensitive stuff doesn't hang around in memory */ + memset(request, 0, sizeof(*request)); + + tor_free(request); +} + +/** Parse an INTRODUCE1 or INTRODUCE2 cell into a newly allocated + * rend_intro_cell_t structure. Free it with rend_service_free_intro() + * when finished. The type parameter should be 1 or 2 to indicate whether + * this is INTRODUCE1 or INTRODUCE2. This parses only the non-encrypted + * parts; after this, call rend_service_decrypt_intro() with a key, then + * rend_service_parse_intro_plaintext() to finish parsing. The optional + * err_msg_out parameter is set to a string suitable for log output + * if parsing fails. This function does some validation, but only + * that which depends solely on the contents of the cell and the + * key; it can be unit-tested. Further validation is done in + * rend_service_validate_intro(). + */ + +rend_intro_cell_t * +rend_service_begin_parse_intro(const uint8_t *request, + size_t request_len, + uint8_t type, + char **err_msg_out) +{ + rend_intro_cell_t *rv = NULL; + char *err_msg = NULL; + + if (!request || request_len <= 0) goto err; + if (!(type == 1 || type == 2)) goto err; + + /* First, check that the cell is long enough to be a sensible INTRODUCE */ + + /* min key length plus digest length plus nickname length */ + if (request_len < + (DIGEST_LEN + REND_COOKIE_LEN + (MAX_NICKNAME_LEN + 1) + + DH_KEY_LEN + 42)) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "got a truncated INTRODUCE%d cell", + (int)type); + } + goto err; + } + + /* Allocate a new parsed cell structure */ + rv = tor_malloc_zero(sizeof(*rv)); + + /* Set the type */ + rv->type = type; + + /* Copy in the ID */ + memcpy(rv->pk, request, DIGEST_LEN); + + /* Copy in the ciphertext */ + rv->ciphertext = tor_malloc(request_len - DIGEST_LEN); + memcpy(rv->ciphertext, request + DIGEST_LEN, request_len - DIGEST_LEN); + rv->ciphertext_len = request_len - DIGEST_LEN; + + goto done; + + err: + if (rv) rend_service_free_intro(rv); + rv = NULL; + if (err_msg_out && !err_msg) { + tor_asprintf(&err_msg, + "unknown INTRODUCE%d error", + (int)type); + } + + done: + if (err_msg_out) *err_msg_out = err_msg; + else tor_free(err_msg); + + return rv; +} + +/** Parse the version-specific parts of a v0 or v1 INTRODUCE1 or INTRODUCE2 + * cell + */ + +static ssize_t +rend_service_parse_intro_for_v0_or_v1( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out) +{ + const char *rp_nickname, *endptr; + size_t nickname_field_len, ver_specific_len; + + if (intro->version == 1) { + ver_specific_len = MAX_HEX_NICKNAME_LEN + 2; + rp_nickname = ((const char *)buf) + 1; + nickname_field_len = MAX_HEX_NICKNAME_LEN + 1; + } else if (intro->version == 0) { + ver_specific_len = MAX_NICKNAME_LEN + 1; + rp_nickname = (const char *)buf; + nickname_field_len = MAX_NICKNAME_LEN + 1; + } else { + if (err_msg_out) + tor_asprintf(err_msg_out, + "rend_service_parse_intro_for_v0_or_v1() called with " + "bad version %d on INTRODUCE%d cell (this is a bug)", + intro->version, + (int)(intro->type)); + goto err; + } + + if (plaintext_len < ver_specific_len) { + if (err_msg_out) + tor_asprintf(err_msg_out, + "short plaintext of encrypted part in v1 INTRODUCE%d " + "cell (%lu bytes, needed %lu)", + (int)(intro->type), + (unsigned long)plaintext_len, + (unsigned long)ver_specific_len); + goto err; + } + + endptr = memchr(rp_nickname, 0, nickname_field_len); + if (!endptr || endptr == rp_nickname) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "couldn't find a nul-padded nickname in " + "INTRODUCE%d cell", + (int)(intro->type)); + } + goto err; + } + + if ((intro->version == 0 && + !is_legal_nickname(rp_nickname)) || + (intro->version == 1 && + !is_legal_nickname_or_hexdigest(rp_nickname))) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "bad nickname in INTRODUCE%d cell", + (int)(intro->type)); + } + goto err; + } + + if (intro->version == 1) { + memcpy(intro->u.v1.rp, rp_nickname, endptr - rp_nickname + 1); + } else { + memcpy(intro->u.v0.rp, rp_nickname, endptr - rp_nickname + 1); + } + + return ver_specific_len; + + err: + return -1; +} + +/** Parse the version-specific parts of a v2 INTRODUCE1 or INTRODUCE2 cell + */ + +static ssize_t +rend_service_parse_intro_for_v2( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out) +{ + unsigned int klen; + extend_info_t *extend_info = NULL; + ssize_t ver_specific_len; + + /* + * We accept version 3 too so that the v3 parser can call this with + * and adjusted buffer for the latter part of a v3 cell, which is + * identical to a v2 cell. + */ + if (!(intro->version == 2 || + intro->version == 3)) { + if (err_msg_out) + tor_asprintf(err_msg_out, + "rend_service_parse_intro_for_v2() called with " + "bad version %d on INTRODUCE%d cell (this is a bug)", + intro->version, + (int)(intro->type)); + goto err; + } + + /* 7 == version, IP and port, DIGEST_LEN == id, 2 == key length */ + if (plaintext_len < 7 + DIGEST_LEN + 2) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "truncated plaintext of encrypted parted of " + "version %d INTRODUCE%d cell", + intro->version, + (int)(intro->type)); + } + + goto err; + } + + extend_info = tor_malloc_zero(sizeof(extend_info_t)); + tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf + 1)); + extend_info->port = ntohs(get_uint16(buf + 5)); + memcpy(extend_info->identity_digest, buf + 7, DIGEST_LEN); + extend_info->nickname[0] = '$'; + base16_encode(extend_info->nickname + 1, sizeof(extend_info->nickname) - 1, + extend_info->identity_digest, DIGEST_LEN); + klen = ntohs(get_uint16(buf + 7 + DIGEST_LEN)); + + /* 7 == version, IP and port, DIGEST_LEN == id, 2 == key length */ + if (plaintext_len < 7 + DIGEST_LEN + 2 + klen) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "truncated plaintext of encrypted parted of " + "version %d INTRODUCE%d cell", + intro->version, + (int)(intro->type)); + } + + goto err; + } + + extend_info->onion_key = + crypto_pk_asn1_decode((const char *)(buf + 7 + DIGEST_LEN + 2), klen); + if (!extend_info->onion_key) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "error decoding onion key in version %d " + "INTRODUCE%d cell", + intro->version, + (intro->type)); + } + + goto err; + } + + ver_specific_len = 7+DIGEST_LEN+2+klen; + + if (intro->version == 2) intro->u.v2.extend_info = extend_info; + else intro->u.v3.extend_info = extend_info; + + return ver_specific_len; + + err: + extend_info_free(extend_info); + + return -1; +} + +/** Parse the version-specific parts of a v3 INTRODUCE1 or INTRODUCE2 cell + */ + +static ssize_t +rend_service_parse_intro_for_v3( + rend_intro_cell_t *intro, + const uint8_t *buf, + size_t plaintext_len, + char **err_msg_out) +{ + ssize_t adjust, v2_ver_specific_len, ts_offset; + + /* This should only be called on v3 cells */ + if (intro->version != 3) { + if (err_msg_out) + tor_asprintf(err_msg_out, + "rend_service_parse_intro_for_v3() called with " + "bad version %d on INTRODUCE%d cell (this is a bug)", + intro->version, + (int)(intro->type)); + goto err; + } + + /* + * Check that we have at least enough to get auth_len: + * + * 1 octet for version, 1 for auth_type, 2 for auth_len + */ + if (plaintext_len < 4) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "truncated plaintext of encrypted parted of " + "version %d INTRODUCE%d cell", + intro->version, + (int)(intro->type)); + } + + goto err; + } + + /* + * The rend_client_send_introduction() function over in rendclient.c is + * broken (i.e., fails to match the spec) in such a way that we can't + * change it without breaking the protocol. Specifically, it doesn't + * emit auth_len when auth-type is REND_NO_AUTH, so everything is off + * by two bytes after that. Calculate ts_offset and do everything from + * the timestamp on relative to that to handle this dain bramage. + */ + + intro->u.v3.auth_type = buf[1]; + if (intro->u.v3.auth_type != REND_NO_AUTH) { + intro->u.v3.auth_len = ntohs(get_uint16(buf + 2)); + ts_offset = 4 + intro->u.v3.auth_len; + } else { + intro->u.v3.auth_len = 0; + ts_offset = 2; + } + + /* Check that auth len makes sense for this auth type */ + if (intro->u.v3.auth_type == REND_BASIC_AUTH || + intro->u.v3.auth_type == REND_STEALTH_AUTH) { + if (intro->u.v3.auth_len != REND_DESC_COOKIE_LEN) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "wrong auth data size %d for INTRODUCE%d cell, " + "should be %d", + (int)(intro->u.v3.auth_len), + (int)(intro->type), + REND_DESC_COOKIE_LEN); + } + + goto err; + } + } + + /* Check that we actually have everything up to the timestamp */ + if (plaintext_len < (size_t)(ts_offset)) { + if (err_msg_out) { + tor_asprintf(err_msg_out, + "truncated plaintext of encrypted parted of " + "version %d INTRODUCE%d cell", + intro->version, + (int)(intro->type)); + } + + goto err; + } + + if (intro->u.v3.auth_type != REND_NO_AUTH && + intro->u.v3.auth_len > 0) { + /* Okay, we can go ahead and copy auth_data */ + intro->u.v3.auth_data = tor_malloc(intro->u.v3.auth_len); + /* + * We know we had an auth_len field in this case, so 4 is + * always right. + */ + memcpy(intro->u.v3.auth_data, buf + 4, intro->u.v3.auth_len); + } + + /* + * Apparently we don't use the timestamp any more, but might as well copy + * over just in case we ever care about it. + */ + intro->u.v3.timestamp = ntohl(get_uint32(buf + ts_offset)); + + /* + * From here on, the format is as in v2, so we call the v2 parser with + * adjusted buffer and length. We are 4 + ts_offset octets in, but the + * v2 parser expects to skip over a version byte at the start, so we + * adjust by 3 + ts_offset. + */ + adjust = 3 + ts_offset; + + v2_ver_specific_len = + rend_service_parse_intro_for_v2(intro, + buf + adjust, plaintext_len - adjust, + err_msg_out); + + /* Success in v2 parser */ + if (v2_ver_specific_len >= 0) return v2_ver_specific_len + adjust; + /* Failure in v2 parser; it will have provided an err_msg */ + else return v2_ver_specific_len; + + err: return -1; } +/** Table of parser functions for version-specific parts of an INTRODUCE2 + * cell. + */ + +static ssize_t + (*intro_version_handlers[])( + rend_intro_cell_t *, + const uint8_t *, + size_t, + char **) = +{ rend_service_parse_intro_for_v0_or_v1, + rend_service_parse_intro_for_v0_or_v1, + rend_service_parse_intro_for_v2, + rend_service_parse_intro_for_v3 }; + +/** Decrypt the encrypted part of an INTRODUCE1 or INTRODUCE2 cell, + * return 0 if successful, or < 0 and write an error message to + * *err_msg_out if provided. + */ + +int +rend_service_decrypt_intro( + rend_intro_cell_t *intro, + crypto_pk_t *key, + char **err_msg_out) +{ + char *err_msg = NULL; + uint8_t key_digest[DIGEST_LEN]; + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; + ssize_t key_len; + uint8_t buf[RELAY_PAYLOAD_SIZE]; + int result, status = 0; + + if (!intro || !key) { + if (err_msg_out) { + err_msg = + tor_strdup("rend_service_decrypt_intro() called with bad " + "parameters"); + } + + status = -2; + goto err; + } + + /* Make sure we have ciphertext */ + if (!(intro->ciphertext) || intro->ciphertext_len <= 0) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "rend_intro_cell_t was missing ciphertext for " + "INTRODUCE%d cell", + (int)(intro->type)); + } + status = -3; + goto err; + } + + /* Check that this cell actually matches this service key */ + + /* first DIGEST_LEN bytes of request is intro or service pk digest */ + crypto_pk_get_digest(key, (char *)key_digest); + if (tor_memneq(key_digest, intro->pk, DIGEST_LEN)) { + if (err_msg_out) { + base32_encode(service_id, REND_SERVICE_ID_LEN_BASE32 + 1, + (char*)(intro->pk), REND_SERVICE_ID_LEN); + tor_asprintf(&err_msg, + "got an INTRODUCE%d cell for the wrong service (%s)", + (int)(intro->type), + escaped(service_id)); + } + + status = -4; + goto err; + } + + /* Make sure the encrypted part is long enough to decrypt */ + + key_len = crypto_pk_keysize(key); + if (intro->ciphertext_len < key_len) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "got an INTRODUCE%d cell with a truncated PK-encrypted " + "part", + (int)(intro->type)); + } + + status = -5; + goto err; + } + + /* Decrypt the encrypted part */ + + note_crypto_pk_op(REND_SERVER); + result = + crypto_pk_private_hybrid_decrypt( + key, (char *)buf, sizeof(buf), + (const char *)(intro->ciphertext), intro->ciphertext_len, + PK_PKCS1_OAEP_PADDING, 1); + if (result < 0) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "couldn't decrypt INTRODUCE%d cell", + (int)(intro->type)); + } + status = -6; + goto err; + } + intro->plaintext_len = result; + intro->plaintext = tor_malloc(intro->plaintext_len); + memcpy(intro->plaintext, buf, intro->plaintext_len); + + goto done; + + err: + if (err_msg_out && !err_msg) { + tor_asprintf(&err_msg, + "unknown INTRODUCE%d error decrypting encrypted part", + (int)(intro->type)); + } + if (status >= 0) status = -1; + + done: + if (err_msg_out) *err_msg_out = err_msg; + else tor_free(err_msg); + + /* clean up potentially sensitive material */ + memset(buf, 0, sizeof(buf)); + memset(key_digest, 0, sizeof(key_digest)); + memset(service_id, 0, sizeof(service_id)); + + return status; +} + +/** Parse the plaintext of the encrypted part of an INTRODUCE1 or + * INTRODUCE2 cell, return 0 if successful, or < 0 and write an error + * message to *err_msg_out if provided. + */ + +int +rend_service_parse_intro_plaintext( + rend_intro_cell_t *intro, + char **err_msg_out) +{ + char *err_msg = NULL; + ssize_t ver_specific_len, ver_invariant_len; + uint8_t version; + int status = 0; + + if (!intro) { + if (err_msg_out) { + err_msg = + tor_strdup("rend_service_parse_intro_plaintext() called with NULL " + "rend_intro_cell_t"); + } + + status = -2; + goto err; + } + + /* Check that we have plaintext */ + if (!(intro->plaintext) || intro->plaintext_len <= 0) { + if (err_msg_out) { + err_msg = tor_strdup("rend_intro_cell_t was missing plaintext"); + } + status = -3; + goto err; + } + + /* In all formats except v0, the first byte is a version number */ + version = intro->plaintext[0]; + + /* v0 has no version byte (stupid...), so handle it as a fallback */ + if (version > 3) version = 0; + + /* Copy the version into the parsed cell structure */ + intro->version = version; + + /* Call the version-specific parser from the table */ + ver_specific_len = + intro_version_handlers[version](intro, + intro->plaintext, intro->plaintext_len, + &err_msg); + if (ver_specific_len < 0) { + status = -4; + goto err; + } + + /** The rendezvous cookie and Diffie-Hellman stuff are version-invariant + * and at the end of the plaintext of the encrypted part of the cell. + */ + + ver_invariant_len = intro->plaintext_len - ver_specific_len; + if (ver_invariant_len < REND_COOKIE_LEN + DH_KEY_LEN) { + tor_asprintf(&err_msg, + "decrypted plaintext of INTRODUCE%d cell was truncated (%ld bytes)", + (int)(intro->type), + (long)(intro->plaintext_len)); + status = -5; + goto err; + } else if (ver_invariant_len > REND_COOKIE_LEN + DH_KEY_LEN) { + tor_asprintf(&err_msg, + "decrypted plaintext of INTRODUCE%d cell was too long (%ld bytes)", + (int)(intro->type), + (long)(intro->plaintext_len)); + status = -6; + } else { + memcpy(intro->rc, + intro->plaintext + ver_specific_len, + REND_COOKIE_LEN); + memcpy(intro->dh, + intro->plaintext + ver_specific_len + REND_COOKIE_LEN, + DH_KEY_LEN); + } + + /* Flag it as being fully parsed */ + intro->parsed = 1; + + goto done; + + err: + if (err_msg_out && !err_msg) { + tor_asprintf(&err_msg, + "unknown INTRODUCE%d error parsing encrypted part", + (int)(intro->type)); + } + if (status >= 0) status = -1; + + done: + if (err_msg_out) *err_msg_out = err_msg; + else tor_free(err_msg); + + return status; +} + +/** Do validity checks on a parsed intro cell before decryption; some of + * these are not done in rend_service_begin_parse_intro() itself because + * they depend on a lot of other state and would make it hard to unit test. + * Returns >= 0 if successful or < 0 if the intro cell is invalid, and + * optionally writes out an error message for logging. If an err_msg + * pointer is provided, it is the caller's responsibility to free any + * provided message. + */ + +int +rend_service_validate_intro_early(const rend_intro_cell_t *intro, + char **err_msg_out) +{ + int status = 0; + + if (!intro) { + if (err_msg_out) + *err_msg_out = + tor_strdup("NULL intro cell passed to " + "rend_service_validate_intro_early()"); + + status = -1; + goto err; + } + + /* TODO */ + + err: + return status; +} + +/** Do validity checks on a parsed intro cell after decryption; some of + * these are not done in rend_service_parse_intro_plaintext() itself because + * they depend on a lot of other state and would make it hard to unit test. + * Returns >= 0 if successful or < 0 if the intro cell is invalid, and + * optionally writes out an error message for logging. If an err_msg + * pointer is provided, it is the caller's responsibility to free any + * provided message. + */ + +int +rend_service_validate_intro_late(const rend_intro_cell_t *intro, + char **err_msg_out) +{ + int status = 0; + + if (!intro) { + if (err_msg_out) + *err_msg_out = + tor_strdup("NULL intro cell passed to " + "rend_service_validate_intro_late()"); + + status = -1; + goto err; + } + + if (intro->version == 3 && intro->parsed) { + if (!(intro->u.v3.auth_type == REND_NO_AUTH || + intro->u.v3.auth_type == REND_BASIC_AUTH || + intro->u.v3.auth_type == REND_STEALTH_AUTH)) { + /* This is an informative message, not an error, as in the old code */ + if (err_msg_out) + tor_asprintf(err_msg_out, + "unknown authorization type %d", + intro->u.v3.auth_type); + } + } + + err: + return status; +} + /** Called when we fail building a rendezvous circuit at some point other * than the last hop: launches a new circuit to the same rendezvous point. */ @@ -1600,8 +2439,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) this case, we might as well close the thing. */ log_info(LD_CIRC|LD_REND, "We have just finished an introduction " "circuit, but we already have enough. Closing it."); - circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_NONE); - return; + reason = END_CIRC_REASON_NONE; + goto err; } else { tor_assert(circuit->build_state->is_internal); log_info(LD_CIRC|LD_REND, "We have just finished an introduction " @@ -1622,7 +2461,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) } circuit_has_opened(circuit); - return; + goto done; } } @@ -1668,9 +2507,16 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) goto err; } - return; + goto done; + err: circuit_mark_for_close(TO_CIRCUIT(circuit), reason); + done: + memset(buf, 0, sizeof(buf)); + memset(auth, 0, sizeof(auth)); + memset(serviceid, 0, sizeof(serviceid)); + + return; } /** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a @@ -1813,9 +2659,16 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) /* Change the circuit purpose. */ circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_REND_JOINED); - return; + goto done; + err: circuit_mark_for_close(TO_CIRCUIT(circuit), reason); + done: + memset(buf, 0, sizeof(buf)); + memset(serviceid, 0, sizeof(serviceid)); + memset(hexcookie, 0, sizeof(hexcookie)); + + return; } /* @@ -2091,11 +2944,7 @@ upload_service_descriptor(rend_service_t *service) static int intro_point_accepted_intro_count(rend_intro_point_t *intro) { - if (intro->accepted_intro_rsa_parts == NULL) { - return 0; - } else { - return digestmap_size(intro->accepted_intro_rsa_parts); - } + return intro->accepted_introduce2_count; } /** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we diff --git a/src/or/rendservice.h b/src/or/rendservice.h index e5848785a8..0d6eddaee6 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -12,9 +12,67 @@ #ifndef _TOR_RENDSERVICE_H #define _TOR_RENDSERVICE_H +#include "or.h" + +typedef struct rend_intro_cell_s rend_intro_cell_t; + +#ifdef RENDSERVICE_PRIVATE + +/* This can be used for both INTRODUCE1 and INTRODUCE2 */ + +struct rend_intro_cell_s { + /* Is this an INTRODUCE1 or INTRODUCE2? (set to 1 or 2) */ + uint8_t type; + /* Public key digest */ + uint8_t pk[DIGEST_LEN]; + /* Optionally, store ciphertext here */ + uint8_t *ciphertext; + ssize_t ciphertext_len; + /* Optionally, store plaintext */ + uint8_t *plaintext; + ssize_t plaintext_len; + /* Have we parsed the plaintext? */ + uint8_t parsed; + /* intro protocol version (0, 1, 2 or 3) */ + uint8_t version; + /* Version-specific parts */ + union { + struct { + /* Rendezvous point nickname */ + uint8_t rp[20]; + } v0; + struct { + /* Rendezvous point nickname or hex-encoded key digest */ + uint8_t rp[42]; + } v1; + struct { + /* The extend_info_t struct has everything v2 uses */ + extend_info_t *extend_info; + } v2; + struct { + /* Auth type used */ + uint8_t auth_type; + /* Length of auth data */ + uint16_t auth_len; + /* Auth data */ + uint8_t *auth_data; + /* timestamp */ + uint32_t timestamp; + /* Rendezvous point's IP address/port, identity digest and onion key */ + extend_info_t *extend_info; + } v3; + } u; + /* Rendezvous cookie */ + uint8_t rc[REND_COOKIE_LEN]; + /* Diffie-Hellman data */ + uint8_t dh[DH_KEY_LEN]; +}; + +#endif + int num_rend_services(void); int rend_config_services(const or_options_t *options, int validate_only); -int rend_service_load_keys(void); +int rend_service_load_all_keys(void); void rend_services_introduce(void); void rend_consider_services_upload(time_t now); void rend_hsdir_routers_changed(void); @@ -27,6 +85,21 @@ int rend_service_intro_established(origin_circuit_t *circuit, void rend_service_rendezvous_has_opened(origin_circuit_t *circuit); int rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, size_t request_len); +void rend_service_compact_intro(rend_intro_cell_t *request); +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); +rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request, + size_t request_len, + uint8_t type, + char **err_msg_out); +int rend_service_parse_intro_plaintext(rend_intro_cell_t *intro, + char **err_msg_out); +int rend_service_validate_intro_early(const rend_intro_cell_t *intro, + char **err_msg_out); +int rend_service_validate_intro_late(const rend_intro_cell_t *intro, + char **err_msg_out); void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc); int rend_service_set_connection_addr_port(edge_connection_t *conn, origin_circuit_t *circ); diff --git a/src/or/rephist.c b/src/or/rephist.c index 720d14cf45..f9c0b5bebb 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -1136,7 +1136,7 @@ rep_hist_load_mtbf_data(time_t now) wfu_timebuf[0] = '\0'; if (format == 1) { - n = sscanf(line, "%40s %ld %lf S=%10s %8s", + n = tor_sscanf(line, "%40s %ld %lf S=%10s %8s", hexbuf, &wrl, &trw, mtbf_timebuf, mtbf_timebuf+11); if (n != 3 && n != 5) { log_warn(LD_HIST, "Couldn't scan line %s", escaped(line)); @@ -1153,7 +1153,7 @@ rep_hist_load_mtbf_data(time_t now) wfu_idx = find_next_with(lines, i+1, "+WFU "); if (mtbf_idx >= 0) { const char *mtbfline = smartlist_get(lines, mtbf_idx); - n = sscanf(mtbfline, "+MTBF %lu %lf S=%10s %8s", + n = tor_sscanf(mtbfline, "+MTBF %lu %lf S=%10s %8s", &wrl, &trw, mtbf_timebuf, mtbf_timebuf+11); if (n == 2 || n == 4) { have_mtbf = 1; @@ -1164,7 +1164,7 @@ rep_hist_load_mtbf_data(time_t now) } if (wfu_idx >= 0) { const char *wfuline = smartlist_get(lines, wfu_idx); - n = sscanf(wfuline, "+WFU %lu %lu S=%10s %8s", + n = tor_sscanf(wfuline, "+WFU %lu %lu S=%10s %8s", &wt_uptime, &total_wt_time, wfu_timebuf, wfu_timebuf+11); if (n == 2 || n == 4) { @@ -1531,7 +1531,7 @@ rep_hist_get_bandwidth_lines(void) const char *desc = NULL; size_t len; - /* opt [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */ + /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */ /* The n,n,n part above. Largest representation of a uint64_t is 20 chars * long, plus the comma. */ #define MAX_HIST_VALUE_LEN 21*NUM_TOTALS @@ -1675,7 +1675,8 @@ rep_hist_load_bwhist_state_section(bw_array_t *b, uint64_t v, mv; int i,ok,ok_m; - int have_maxima = (smartlist_len(s_values) == smartlist_len(s_maxima)); + int have_maxima = s_maxima && s_values && + (smartlist_len(s_values) == smartlist_len(s_maxima)); if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { start = s_begins - s_interval*(smartlist_len(s_values)); diff --git a/src/or/replaycache.c b/src/or/replaycache.c new file mode 100644 index 0000000000..09104a9373 --- /dev/null +++ b/src/or/replaycache.c @@ -0,0 +1,215 @@ + /* Copyright (c) 2012, The Tor Project, Inc. */ + /* See LICENSE for licensing information */ + +/* + * \file replaycache.c + * + * \brief Self-scrubbing replay cache for rendservice.c + */ + +#define REPLAYCACHE_PRIVATE + +#include "or.h" +#include "replaycache.h" + +/** Free the replaycache r and all of its entries. + */ + +void +replaycache_free(replaycache_t *r) +{ + if (!r) { + log_info(LD_BUG, "replaycache_free() called on NULL"); + return; + } + + if (r->digests_seen) digestmap_free(r->digests_seen, _tor_free); + + tor_free(r); +} + +/** Allocate a new, empty replay detection cache, where horizon is the time + * 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) +{ + replaycache_t *r = NULL; + + if (horizon < 0) { + log_info(LD_BUG, "replaycache_new() called with negative" + " horizon parameter"); + goto err; + } + + if (interval < 0) { + log_info(LD_BUG, "replaycache_new() called with negative interval" + " parameter"); + interval = 0; + } + + r = tor_malloc(sizeof(*r)); + r->scrub_interval = interval; + r->scrubbed = 0; + r->horizon = horizon; + r->digests_seen = digestmap_new(); + + err: + return r; +} + +/** See documentation for replaycache_add_and_test() + */ + +int +replaycache_add_and_test_internal( + time_t present, replaycache_t *r, const void *data, int len, + time_t *elapsed) +{ + int rv = 0; + char digest[DIGEST_LEN]; + time_t *access_time; + + /* sanity check */ + if (present <= 0 || !r || !data || len <= 0) { + log_info(LD_BUG, "replaycache_add_and_test_internal() called with stupid" + " parameters; please fix this."); + goto done; + } + + /* compute digest */ + crypto_digest(digest, (const char *)data, len); + + /* check map */ + access_time = digestmap_get(r->digests_seen, digest); + + /* seen before? */ + if (access_time != NULL) { + /* + * If it's far enough in the past, no hit. If the horizon is zero, we + * never expire. + */ + if (*access_time >= present - r->horizon || r->horizon == 0) { + /* replay cache hit, return 1 */ + rv = 1; + /* If we want to output an elapsed time, do so */ + if (elapsed) { + if (present >= *access_time) { + *elapsed = present - *access_time; + } else { + /* We shouldn't really be seeing hits from the future, but... */ + *elapsed = 0; + } + } + } + /* + * If it's ahead of the cached time, update + */ + if (*access_time < present) { + *access_time = present; + } + } else { + /* No, so no hit and update the digest map with the current time */ + access_time = tor_malloc(sizeof(*access_time)); + *access_time = present; + digestmap_set(r->digests_seen, digest, access_time); + } + + /* now scrub the cache if it's time */ + replaycache_scrub_if_needed_internal(present, r); + + done: + return rv; +} + +/** See documentation for replaycache_scrub_if_needed() + */ + +void +replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) +{ + digestmap_iter_t *itr = NULL; + const char *digest; + void *valp; + time_t *access_time; + char scrub_this; + + /* sanity check */ + if (!r || !(r->digests_seen)) { + log_info(LD_BUG, "replaycache_scrub_if_needed_internal() called with" + " stupid parameters; please fix this."); + return; + } + + /* scrub time yet? (scrubbed == 0 indicates never scrubbed before) */ + if (present - r->scrubbed < r->scrub_interval && r->scrubbed > 0) return; + + /* if we're never expiring, don't bother scrubbing */ + if (r->horizon == 0) return; + + /* okay, scrub time */ + itr = digestmap_iter_init(r->digests_seen); + while (!digestmap_iter_done(itr)) { + scrub_this = 0; + digestmap_iter_get(itr, &digest, &valp); + access_time = (time_t *)valp; + if (access_time) { + /* aged out yet? */ + if (*access_time < present - r->horizon) scrub_this = 1; + } else { + /* Buh? Get rid of it, anyway */ + log_info(LD_BUG, "replaycache_scrub_if_needed_internal() saw a NULL" + " entry in the digestmap."); + scrub_this = 1; + } + + if (scrub_this) { + /* Advance the iterator and remove this one */ + itr = digestmap_iter_next_rmv(r->digests_seen, itr); + /* Free the value removed */ + tor_free(access_time); + } else { + /* Just advance the iterator */ + itr = digestmap_iter_next(r->digests_seen, itr); + } + } + + /* update scrubbed timestamp */ + if (present > r->scrubbed) r->scrubbed = present; +} + +/** Test the buffer of length len point to by data against the replay cache r; + * the digest of the buffer will be added to the cache at the current time, + * 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, int len) +{ + return replaycache_add_and_test_internal(time(NULL), r, data, len, NULL); +} + +/** 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, int len, time_t *elapsed) +{ + return replaycache_add_and_test_internal(time(NULL), r, data, len, 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) +{ + replaycache_scrub_if_needed_internal(time(NULL), r); +} + diff --git a/src/or/replaycache.h b/src/or/replaycache.h new file mode 100644 index 0000000000..9f3107c513 --- /dev/null +++ b/src/or/replaycache.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file replaycache.h + * \brief Header file for replaycache.c. + **/ + +#ifndef _TOR_REPLAYCACHE_H +#define _TOR_REPLAYCACHE_H + +typedef struct replaycache_s replaycache_t; + +#ifdef REPLAYCACHE_PRIVATE + +struct replaycache_s { + /* Scrub interval */ + time_t scrub_interval; + /* Last scrubbed */ + time_t scrubbed; + /* + * Horizon + * (don't return true on digests in the cache but older than this) + */ + time_t horizon; + /* + * Digest map: keys are digests, values are times the digest was last seen + */ + digestmap_t *digests_seen; +}; + +#endif /* REPLAYCACHE_PRIVATE */ + +/* replaycache_t free/new */ + +void replaycache_free(replaycache_t *r); +replaycache_t * replaycache_new(time_t horizon, time_t interval); + +#ifdef REPLAYCACHE_PRIVATE + +/* + * replaycache_t internal functions: + * + * These take the time to treat as the present as an argument for easy unit + * testing. For everything else, use the wrappers below instead. + */ + +int replaycache_add_and_test_internal( + time_t present, replaycache_t *r, const void *data, int len, + time_t *elapsed); +void replaycache_scrub_if_needed_internal( + time_t present, replaycache_t *r); + +#endif /* REPLAYCACHE_PRIVATE */ + +/* + * replaycache_t methods + */ + +int replaycache_add_and_test(replaycache_t *r, const void *data, int len); +int replaycache_add_test_and_elapsed( + replaycache_t *r, const void *data, int len, time_t *elapsed); +void replaycache_scrub_if_needed(replaycache_t *r); + +#endif + diff --git a/src/or/router.c b/src/or/router.c index 352c456f1f..4e1eb2ddb2 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -27,6 +27,7 @@ #include "router.h" #include "routerlist.h" #include "routerparse.h" +#include "transports.h" /** * \file router.c @@ -671,7 +672,7 @@ init_keys(void) * we don't really need new keys yet so the descriptor doesn't * change and the old one is still fresh. */ log_info(LD_GENERAL, "Couldn't add own descriptor to directory " - "after key init: %s. This is usually not a problem.", + "after key init: %s This is usually not a problem.", m?m:"<unknown error>"); } } @@ -797,7 +798,7 @@ int check_whether_dirport_reachable(void) { const or_options_t *options = get_options(); - return !options->DirPort || + return !options->DirPort_set || options->AssumeReachable || net_is_disabled() || can_reach_dir_port; @@ -879,6 +880,21 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) return advertising ? dir_port : 0; } +/** Allocate and return a new extend_info_t that can be used to build + * a circuit to or through the router <b>r</b>. Use the primary + * address of the router unless <b>for_direct_connect</b> is true, in + * which case the preferred address is used instead. */ +static extend_info_t * +extend_info_from_router(const routerinfo_t *r) +{ + tor_addr_port_t ap; + tor_assert(r); + + router_get_prim_orport(r, &ap); + return extend_info_new(r->nickname, r->cache_info.identity_digest, + r->onion_pkey, &ap.addr, ap.port); +} + /** Some time has passed, or we just got new directory information. * See if we currently believe our ORPort or DirPort to be * unreachable. If so, launch a new test for it. @@ -920,12 +936,11 @@ consider_testing_reachability(int test_or, int test_dir) } if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) { - extend_info_t *ei; + extend_info_t *ei = extend_info_from_router(me); + /* XXX IPv6 self testing */ log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", !orport_reachable ? "reachability" : "bandwidth", me->address, me->or_port); - /* XXX IPv6 self testing IPv6 orports will need pref_addr */ - ei = extend_info_from_router(me, 0); circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); extend_info_free(ei); @@ -939,8 +954,7 @@ consider_testing_reachability(int test_or, int test_dir) /* ask myself, via tor, for my server descriptor. */ directory_initiate_command(me->address, &addr, me->or_port, me->dir_port, - 0, /* does not matter */ - 0, me->cache_info.identity_digest, + me->cache_info.identity_digest, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_GENERAL, 1, "authority.z", NULL, 0, 0); @@ -1115,7 +1129,8 @@ int server_mode(const or_options_t *options) { if (options->ClientOnly) return 0; - return (options->ORPort || options->ORListenAddress); + /* XXXX024 I believe we can kill off ORListenAddress here.*/ + return (options->ORPort_set || options->ORListenAddress); } /** Return true iff we are trying to be a non-bridge server. @@ -1538,8 +1553,9 @@ router_rebuild_descriptor(int force) ri->cache_info.published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from * main thread */ - if (options->BridgeRelay) { - /* For now, only bridges advertise an ipv6 or-address. And only one. */ + + /* For now, at most one IPv6 or-address is being advertised. */ + { const port_cfg_t *ipv6_orport = NULL; SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { if (p->type == CONN_TYPE_OR_LISTENER && @@ -1564,6 +1580,7 @@ router_rebuild_descriptor(int force) ri->ipv6_orport = ipv6_orport->port; } } + ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (crypto_pk_get_digest(ri->identity_pkey, ri->cache_info.identity_digest)<0) { @@ -2053,9 +2070,9 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, "router %s %s %d 0 %d\n" "%s" "platform %s\n" - "opt protocols Link 1 2 Circuit 1\n" + "protocols Link 1 2 Circuit 1\n" "published %s\n" - "opt fingerprint %s\n" + "fingerprint %s\n" "uptime %ld\n" "bandwidth %d %d %d\n" "%s%s%s%s" @@ -2074,15 +2091,15 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, (int) router->bandwidthrate, (int) router->bandwidthburst, (int) router->bandwidthcapacity, - has_extra_info_digest ? "opt extra-info-digest " : "", + has_extra_info_digest ? "extra-info-digest " : "", has_extra_info_digest ? extra_info_digest : "", has_extra_info_digest ? "\n" : "", - options->DownloadExtraInfo ? "opt caches-extra-info\n" : "", + options->DownloadExtraInfo ? "caches-extra-info\n" : "", onion_pkey, identity_pkey, family_line, - we_are_hibernating() ? "opt hibernating 1\n" : "", - options->HidServDirectoryV2 ? "opt hidden-service-dir\n" : "", - options->AllowSingleHopExits ? "opt allow-single-hop-exits\n" : ""); + we_are_hibernating() ? "hibernating 1\n" : "", + options->HidServDirectoryV2 ? "hidden-service-dir\n" : "", + options->AllowSingleHopExits ? "allow-single-hop-exits\n" : ""); tor_free(family_line); tor_free(onion_pkey); @@ -2193,40 +2210,24 @@ router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) ap_out->port = router->or_port; } -/** Return 1 if we prefer the IPv6 address and OR TCP port of - * <b>router</b>, else 0. - * - * We prefer the IPv6 address if the router has one and - * i) the routerinfo_t says so - * or - * ii) the router has no IPv4 address. */ +/** Return 1 if any of <b>router</b>'s addresses are <b>addr</b>. + * Otherwise return 0. */ int -router_ipv6_preferred(const routerinfo_t *router) +router_has_addr(const routerinfo_t *router, const tor_addr_t *addr) { - return (!tor_addr_is_null(&router->ipv6_addr) - && (router->ipv6_preferred || router->addr == 0)); -} - -/** Copy the preferred OR port (IP address and TCP port) for - * <b>router</b> into *<b>addr_out</b>. */ -void -router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) -{ - if (router_ipv6_preferred(router)) - router_get_pref_ipv6_orport(router, ap_out); - else - router_get_prim_orport(router, ap_out); + return + tor_addr_eq_ipv4h(addr, router->addr) || + tor_addr_eq(&router->ipv6_addr, addr); } -/** Copy the preferred IPv6 OR port (IP address and TCP port) for - * <b>router</b> into *<b>ap_out</b>. */ -void -router_get_pref_ipv6_orport(const routerinfo_t *router, - tor_addr_port_t *ap_out) +int +router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport) { - tor_assert(ap_out != NULL); - tor_addr_copy(&ap_out->addr, &router->ipv6_addr); - ap_out->port = router->ipv6_orport; + return + (tor_addr_eq_ipv4h(&orport->addr, router->addr) && + orport->port == router->or_port) || + (tor_addr_eq(&orport->addr, &router->ipv6_addr) && + orport->port == router->ipv6_orport); } /** Load the contents of <b>filename</b>, find the last line starting with @@ -2344,6 +2345,13 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } + /* Add information about the pluggable transports we support. */ + if (options->ServerTransportPlugin) { + char *pluggable_transports = pt_get_extra_info_descriptor_string(); + if (pluggable_transports) + smartlist_add(chunks, pluggable_transports); + } + if (should_record_bridge_info(options) && write_stats_to_extrainfo) { const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); if (bridge_stats) { @@ -2753,3 +2761,30 @@ router_free_all(void) } } +/** Return a smartlist of tor_addr_port_t's with all the OR ports of + <b>ri</b>. Note that freeing of the items in the list as well as + the smartlist itself is the callers responsibility. + + XXX duplicating code from node_get_all_orports(). */ +smartlist_t * +router_get_all_orports(const routerinfo_t *ri) +{ + smartlist_t *sl = smartlist_new(); + tor_assert(ri); + + if (ri->addr != 0) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&ap->addr, ri->addr); + ap->port = ri->or_port; + smartlist_add(sl, ap); + } + if (!tor_addr_is_null(&ri->ipv6_addr)) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_copy(&ap->addr, &ri->ipv6_addr); + ap->port = ri->or_port; + smartlist_add(sl, ap); + } + + return sl; +} + diff --git a/src/or/router.h b/src/or/router.h index 69805d6f2d..c43c308496 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -86,15 +86,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, crypto_pk_t *ident_key); -void router_get_prim_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); -void router_get_pref_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); -void router_get_pref_ipv6_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); -int router_ipv6_preferred(const routerinfo_t *router); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, crypto_pk_t *ident_key); +void router_get_prim_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out); +int router_has_addr(const routerinfo_t *router, const tor_addr_t *addr); +int router_has_orport(const routerinfo_t *router, + const tor_addr_port_t *orport); int is_legal_nickname(const char *s); int is_legal_nickname_or_hexdigest(const char *s); int is_legal_hexdigest(const char *s); @@ -132,6 +130,8 @@ void router_free_all(void); const char *router_purpose_to_string(uint8_t p); uint8_t router_purpose_from_string(const char *s); +smartlist_t *router_get_all_orports(const routerinfo_t *ri); + #ifdef ROUTER_PRIVATE /* Used only by router.c and test.c */ void get_platform_str(char *platform, size_t len); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 7dc430b0f8..41abc4c87a 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -996,7 +996,7 @@ router_get_my_share_of_directory_requests(double *v2_share_out, } } - if (rs->version_supports_v3_dir) { + { sl_last_total_weighted_bw = 0; router_pick_directory_server(V3_DIRINFO, pds_flags); if (sl_last_total_weighted_bw != 0) { @@ -1128,12 +1128,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags) continue; if (requireother && router_digest_is_me(node->identity)) continue; - if (type & V3_DIRINFO) { - if (!(status->version_supports_v3_dir || - router_digest_is_trusted_dir_type(node->identity, - V3_DIRINFO))) - continue; - } is_trusted = router_digest_is_trusted_dir(node->identity); if ((type & V2_DIRINFO) && !(node->rs->is_v2_dir || is_trusted)) continue; @@ -1156,7 +1150,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags) is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now; if (prefer_tunnel && - status->version_supports_begindir && (!fascistfirewall || fascist_firewall_allows_address_or(&addr, status->or_port))) smartlist_add(is_trusted ? trusted_tunnel : @@ -1344,9 +1337,11 @@ mark_all_trusteddirservers_up(void) /** Return true iff r1 and r2 have the same address and OR port. */ int -routers_have_same_or_addr(const routerinfo_t *r1, const routerinfo_t *r2) +routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2) { - return r1->addr == r2->addr && r1->or_port == r2->or_port; + return r1->addr == r2->addr && r1->or_port == r2->or_port && + tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) && + r1->ipv6_orport == r2->ipv6_orport; } /** Reset all internal variables used to count failed downloads of network @@ -2890,7 +2885,7 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri) &ri->cache_info); smartlist_add(rl->routers, ri); ri->cache_info.routerlist_index = smartlist_len(rl->routers) - 1; - nodelist_add_routerinfo(ri); + nodelist_set_routerinfo(ri, NULL); router_dir_info_changed(); #ifdef DEBUG_ROUTERLIST routerlist_assert_ok(rl); @@ -3119,8 +3114,11 @@ routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old, tor_assert(0 <= idx && idx < smartlist_len(rl->routers)); tor_assert(smartlist_get(rl->routers, idx) == ri_old); - nodelist_remove_routerinfo(ri_old); - nodelist_add_routerinfo(ri_new); + { + routerinfo_t *ri_old_tmp=NULL; + nodelist_set_routerinfo(ri_new, &ri_old_tmp); + tor_assert(ri_old == ri_old_tmp); + } router_dir_info_changed(); if (idx >= 0) { @@ -3457,11 +3455,6 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, /* Same key, and either new, or listed in the consensus. */ log_debug(LD_DIR, "Replacing entry for router %s", router_describe(router)); - if (routers_have_same_or_addr(router, old_router)) { - /* these carry over when the address and orport are unchanged. */ - router->last_reachable = old_router->last_reachable; - router->testing_since = old_router->testing_since; - } routerlist_replace(routerlist, old_router, router); if (!from_cache) { signed_desc_append_to_journal(&router->cache_info, @@ -4159,11 +4152,6 @@ add_trusted_dir_server(const char *nickname, const char *address, ent->fake_status.dir_port = ent->dir_port; ent->fake_status.or_port = ent->or_port; - if (ent->or_port) - ent->fake_status.version_supports_begindir = 1; - - ent->fake_status.version_supports_conditional_consensus = 1; - smartlist_add(trusted_dir_servers, ent); router_dir_info_changed(); return ent; @@ -4440,11 +4428,6 @@ launch_descriptor_downloads(int purpose, } } } - /* XXX should we consider having even the dir mirrors delay - * a little bit, so we don't load the authorities as much? -RD - * I don't think so. If we do, clients that want those descriptors may - * not actually find them if the caches haven't got them yet. -NM - */ if (! should_delay && n_downloadable) { int i, n_per_request; @@ -4484,9 +4467,9 @@ launch_descriptor_downloads(int purpose, rtr_plural = "s"; log_info(LD_DIR, - "Launching %d request%s for %d router%s, %d at a time", - CEIL_DIV(n_downloadable, n_per_request), - req_plural, n_downloadable, rtr_plural, n_per_request); + "Launching %d request%s for %d %s%s, %d at a time", + CEIL_DIV(n_downloadable, n_per_request), req_plural, + n_downloadable, descname, rtr_plural, n_per_request); smartlist_sort_digests(downloadable); for (i=0; i < n_downloadable; i += n_per_request) { initiate_descriptor_downloads(source, purpose, @@ -4970,7 +4953,8 @@ count_usable_descriptors(int *num_present, int *num_usable, } SMARTLIST_FOREACH_END(rs); - log_debug(LD_DIR, "%d usable, %d present.", *num_usable, *num_present); + log_debug(LD_DIR, "%d usable, %d present (%s).", *num_usable, *num_present, + md ? "microdescs" : "descs"); } /** We just fetched a new set of descriptors. Compute how far through @@ -5160,6 +5144,8 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) if (strcasecmp(r1->address, r2->address) || strcasecmp(r1->nickname, r2->nickname) || r1->or_port != r2->or_port || + !tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) || + r1->ipv6_orport != r2->ipv6_orport || r1->dir_port != r2->dir_port || r1->purpose != r2->purpose || crypto_pk_cmp_keys(r1->onion_pkey, r2->onion_pkey) || diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 2072611897..ab58b9ee5a 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -36,7 +36,7 @@ const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type, int router_get_my_share_of_directory_requests(double *v2_share_out, double *v3_share_out); void router_reset_status_download_failures(void); -int routers_have_same_or_addr(const routerinfo_t *r1, const routerinfo_t *r2); +int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2); int router_nickname_is_in_list(const routerinfo_t *router, const char *list); const routerinfo_t *routerlist_find_my_routerinfo(void); const node_t *router_find_exact_exit_enclave(const char *address, diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 970353a43c..6b94c6bfdd 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -67,6 +67,7 @@ typedef enum { K_OR_ADDRESS, K_P, K_R, + K_A, K_S, K_V, K_W, @@ -338,6 +339,7 @@ static token_rule_t extrainfo_token_table[] = { static token_rule_t rtrstatus_token_table[] = { T01("p", K_P, CONCAT_ARGS, NO_OBJ ), T1( "r", K_R, GE(7), NO_OBJ ), + T0N("a", K_A, GE(1), NO_OBJ ), T1( "s", K_S, ARGS, NO_OBJ ), T01("v", K_V, CONCAT_ARGS, NO_OBJ ), T01("w", K_W, ARGS, NO_OBJ ), @@ -522,6 +524,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = { /** List of tokens recognized in microdescriptors */ static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), + T0N("a", K_A, GE(1), NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ), @@ -962,7 +965,7 @@ router_parse_runningrouters(const char *str) /* Now that we know the signature is okay, and we have a * publication time, cache the list. */ - if (get_options()->DirPort && !authdir_mode_v1(get_options())) + if (get_options()->DirPort_set && !authdir_mode_v1(get_options())) dirserv_set_cached_directory(str, published_on, 1); r = 0; @@ -1257,6 +1260,42 @@ dump_distinct_digest_count(int severity) #endif } +/** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's + * with at least one argument (use GE(1) in setup). If found, store + * address and port number to <b>addr_out</b> and + * <b>port_out</b>. Return number of OR ports found. */ +static int +find_single_ipv6_orport(const smartlist_t *list, + tor_addr_t *addr_out, + uint16_t *port_out) +{ + int ret = 0; + tor_assert(list != NULL); + tor_assert(addr_out != NULL); + tor_assert(port_out != NULL); + + SMARTLIST_FOREACH_BEGIN(list, directory_token_t *, t) { + tor_addr_t a; + maskbits_t bits; + uint16_t port_min, port_max; + tor_assert(t->n_args >= 1); + /* XXXX Prop186 the full spec allows much more than this. */ + if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min, + &port_max) == AF_INET6 && + bits == 128 && + port_min == port_max) { + /* Okay, this is one we can understand. Use it and ignore + any potential more addresses in list. */ + tor_addr_copy(addr_out, &a); + *port_out = port_min; + ret = 1; + break; + } + } SMARTLIST_FOREACH_END(t); + + return ret; +} + /** Helper function: reads a single router entry from *<b>s</b> ... * *<b>end</b>. Mallocs a new router and returns it if all goes well, else * returns NULL. If <b>cache_copy</b> is true, duplicate the contents of @@ -1513,21 +1552,8 @@ router_parse_entry_from_string(const char *s, const char *end, { smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS); if (or_addresses) { - SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) { - tor_addr_t a; - maskbits_t bits; - uint16_t port_min, port_max; - /* XXXX Prop186 the full spec allows much more than this. */ - if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min, - &port_max) == AF_INET6 && - bits == 128 && - port_min == port_max) { - /* Okay, this is one we can understand. */ - tor_addr_copy(&router->ipv6_addr, &a); - router->ipv6_orport = port_min; - break; - } - } SMARTLIST_FOREACH_END(t); + find_single_ipv6_orport(or_addresses, &router->ipv6_addr, + &router->ipv6_orport); smartlist_free(or_addresses); } } @@ -2060,6 +2086,14 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset], 10,0,65535,NULL,NULL); + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport); + smartlist_free(a_lines); + } + } + tok = find_opt_by_keyword(tokens, K_S); if (tok && vote) { int i; @@ -2112,20 +2146,9 @@ routerstatus_parse_entry_from_string(memarea_t *area, tor_assert(tok->n_args == 1); rs->version_known = 1; if (strcmpstart(tok->args[0], "Tor ")) { - rs->version_supports_begindir = 1; - rs->version_supports_extrainfo_upload = 1; - rs->version_supports_conditional_consensus = 1; rs->version_supports_microdesc_cache = 1; rs->version_supports_optimistic_data = 1; } else { - rs->version_supports_begindir = - tor_version_as_new_as(tok->args[0], "0.2.0.1-alpha"); - rs->version_supports_extrainfo_upload = - tor_version_as_new_as(tok->args[0], "0.2.0.0-alpha-dev (r10070)"); - rs->version_supports_v3_dir = - tor_version_as_new_as(tok->args[0], "0.2.0.8-alpha"); - rs->version_supports_conditional_consensus = - tor_version_as_new_as(tok->args[0], "0.2.1.1-alpha"); rs->version_supports_microdesc_cache = tor_version_supports_microdescriptors(tok->args[0]); rs->version_supports_optimistic_data = @@ -2201,6 +2224,11 @@ routerstatus_parse_entry_from_string(memarea_t *area, escaped(tok->args[0])); goto err; } + } else { + log_info(LD_BUG, "Found an entry in networkstatus with no " + "microdescriptor digest. (Router %s=%s at %s:%d.)", + rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN), + fmt_addr32(rs->addr), rs->or_port); } } @@ -4416,6 +4444,14 @@ microdescs_parse_from_string(const char *s, const char *eos, md->onion_pkey = tok->key; tok->key = NULL; + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport); + smartlist_free(a_lines); + } + } + if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) { int i; md->family = smartlist_new(); diff --git a/src/or/transports.c b/src/or/transports.c index 4ba239562a..f2c604ce8b 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -39,13 +39,17 @@ * transport_t structs. * * When the managed proxy stops spitting METHOD lines (signified by a - * '{S,C}METHODS DONE' message) we register all the transports - * collected to the circuitbuild.c subsystem. At this point, the - * pointers to transport_t can be transformed into dangling pointers - * at any point by the circuitbuild.c subsystem, and so we replace all - * transport_t pointers with strings describing the transport names. - * We can still go from a transport name to a transport_t using the - * fact that each transport name uniquely identifies a transport_t. + * '{S,C}METHODS DONE' message) we pass copies of its transports to + * the bridge subsystem. We keep copies of the 'transport_t's on the + * managed proxy to be able to associate the proxy with its + * transports, and we pass copies to the bridge subsystem so that + * transports can be associated with bridges. + * [ XXX We should try see whether the two copies are really needed + * and maybe cut it into a single copy of the 'transport_t' shared + * between the managed proxy and the bridge subsystem. Preliminary + * analysis shows that both copies are needed with the current code + * logic, because of race conditions that can cause dangling + * pointers. ] * * <b>In even more detail, this is what happens when a SIGHUP * occurs:</b> @@ -127,6 +131,219 @@ static INLINE void free_execve_args(char **arg); protocol version. */ #define PROTO_VERSION_ONE 1 +/** A list of pluggable transports found in torrc. */ +static smartlist_t *transport_list = NULL; + +/** Returns a transport_t struct for a transport proxy supporting the + protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using + SOCKS version <b>socks_ver</b>. */ +static transport_t * +transport_new(const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver) +{ + transport_t *t = tor_malloc_zero(sizeof(transport_t)); + + tor_addr_copy(&t->addr, addr); + t->port = port; + t->name = tor_strdup(name); + t->socks_version = socks_ver; + + return t; +} + +/** Free the pluggable transport struct <b>transport</b>. */ +void +transport_free(transport_t *transport) +{ + if (!transport) + return; + + tor_free(transport->name); + tor_free(transport); +} + +/** Mark every entry of the transport list to be removed on our next call to + * sweep_transport_list unless it has first been un-marked. */ +void +mark_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH(transport_list, transport_t *, t, + t->marked_for_removal = 1); +} + +/** Remove every entry of the transport list that was marked with + * mark_transport_list if it has not subsequently been un-marked. */ +void +sweep_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, t) { + if (t->marked_for_removal) { + SMARTLIST_DEL_CURRENT(transport_list, t); + transport_free(t); + } + } SMARTLIST_FOREACH_END(t); +} + +/** Initialize the pluggable transports list to empty, creating it if + * needed. */ +static void +clear_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t)); + smartlist_clear(transport_list); +} + +/** Return a deep copy of <b>transport</b>. */ +static transport_t * +transport_copy(const transport_t *transport) +{ + transport_t *new_transport = NULL; + + tor_assert(transport); + + new_transport = tor_malloc_zero(sizeof(transport_t)); + + new_transport->socks_version = transport->socks_version; + new_transport->name = tor_strdup(transport->name); + tor_addr_copy(&new_transport->addr, &transport->addr); + new_transport->port = transport->port; + new_transport->marked_for_removal = transport->marked_for_removal; + + return new_transport; +} + +/** Returns the transport in our transport list that has the name <b>name</b>. + * Else returns NULL. */ +transport_t * +transport_get_by_name(const char *name) +{ + tor_assert(name); + + if (!transport_list) + return NULL; + + SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) { + if (!strcmp(transport->name, name)) + return transport; + } SMARTLIST_FOREACH_END(transport); + + return NULL; +} + +/** Resolve any conflicts that the insertion of transport <b>t</b> + * might cause. + * Return 0 if <b>t</b> is OK and should be registered, 1 if there is + * a transport identical to <b>t</b> already registered and -1 if + * <b>t</b> cannot be added due to conflicts. */ +static int +transport_resolve_conflicts(const transport_t *t) +{ + /* This is how we resolve transport conflicts: + + If there is already a transport with the same name and addrport, + we either have duplicate torrc lines OR we are here post-HUP and + this transport was here pre-HUP as well. In any case, mark the + old transport so that it doesn't get removed and ignore the new + one. Our caller has to free the new transport so we return '1' to + signify this. + + If there is already a transport with the same name but different + addrport: + * if it's marked for removal, it means that it either has a lower + priority than 't' in torrc (otherwise the mark would have been + cleared by the paragraph above), or it doesn't exist at all in + the post-HUP torrc. We destroy the old transport and register 't'. + * if it's *not* marked for removal, it means that it was newly + added in the post-HUP torrc or that it's of higher priority, in + this case we ignore 't'. */ + transport_t *t_tmp = transport_get_by_name(t->name); + if (t_tmp) { /* same name */ + if (tor_addr_eq(&t->addr, &t_tmp->addr) && (t->port == t_tmp->port)) { + /* same name *and* addrport */ + t_tmp->marked_for_removal = 0; + return 1; + } else { /* same name but different addrport */ + if (t_tmp->marked_for_removal) { /* marked for removal */ + log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " + "but there was already a transport marked for deletion at " + "'%s:%u'. We deleted the old transport and registered the " + "new one.", t->name, fmt_addr(&t->addr), t->port, + fmt_addr(&t_tmp->addr), t_tmp->port); + smartlist_remove(transport_list, t_tmp); + transport_free(t_tmp); + } else { /* *not* marked for removal */ + log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " + "but the same transport already exists at '%s:%u'. " + "Skipping.", t->name, fmt_addr(&t->addr), t->port, + fmt_addr(&t_tmp->addr), t_tmp->port); + return -1; + } + } + } + + return 0; +} + +/** Add transport <b>t</b> to the internal list of pluggable + * transports. + * Returns 0 if the transport was added correctly, 1 if the same + * transport was already registered (in this case the caller must + * free the transport) and -1 if there was an error. */ +static int +transport_add(transport_t *t) +{ + int r; + tor_assert(t); + + r = transport_resolve_conflicts(t); + + switch (r) { + case 0: /* should register transport */ + if (!transport_list) + transport_list = smartlist_new(); + smartlist_add(transport_list, t); + return 0; + default: /* let our caller know the return code */ + return r; + } +} + +/** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>. + * <b>name</b> is set to the name of the protocol this proxy uses. + * <b>socks_ver</b> is set to the SOCKS version of the proxy. */ +int +transport_add_from_config(const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver) +{ + transport_t *t = transport_new(addr, port, name, socks_ver); + + int r = transport_add(t); + + switch (r) { + case -1: + default: + log_notice(LD_GENERAL, "Could not add transport %s at %s:%u. Skipping.", + t->name, fmt_addr(&t->addr), t->port); + transport_free(t); + return -1; + case 1: + log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", + t->name, fmt_addr(&t->addr), t->port); + transport_free(t); /* falling */ + return 0; + case 0: + log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", + t->name, fmt_addr(&t->addr), t->port); + return 0; + } +} + /** List of unconfigured managed proxies. */ static smartlist_t *managed_proxy_list = NULL; /** Number of still unconfigured proxies. */ @@ -217,11 +434,11 @@ proxy_needs_restart(const managed_proxy_t *mp) { /* mp->transport_to_launch is populated with the names of the transports that must be launched *after* the SIGHUP. - mp->transports is populated with the names of the transports that - were launched *before* the SIGHUP. + mp->transports is populated with the transports that were + launched *before* the SIGHUP. - If the two lists contain the same strings, we don't need to - restart the proxy, since it already does what we want. */ + Check if all the transports that need to be launched are already + launched: */ tor_assert(smartlist_len(mp->transports_to_launch) > 0); tor_assert(mp->conf_state == PT_PROTO_COMPLETED); @@ -229,11 +446,11 @@ proxy_needs_restart(const managed_proxy_t *mp) if (smartlist_len(mp->transports_to_launch) != smartlist_len(mp->transports)) goto needs_restart; - SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t_t_l) { - if (!smartlist_string_isin(mp->transports, t_t_l)) + SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { + if (!smartlist_string_isin(mp->transports_to_launch, t->name)) goto needs_restart; - } SMARTLIST_FOREACH_END(t_t_l); + } SMARTLIST_FOREACH_END(t); return 0; @@ -245,6 +462,7 @@ proxy_needs_restart(const managed_proxy_t *mp) * preparations and then flag its state so that it will be relaunched * in the next tick. */ static void + proxy_prepare_for_restart(managed_proxy_t *mp) { transport_t *t_tmp = NULL; @@ -255,16 +473,17 @@ proxy_prepare_for_restart(managed_proxy_t *mp) tor_process_handle_destroy(mp->process_handle, 1); mp->process_handle = NULL; - /* destroy all its old transports. we no longer use them. */ - SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) { - t_tmp = transport_get_by_name(t_name); + /* destroy all its registered transports, since we will no longer + use them. */ + SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { + t_tmp = transport_get_by_name(t->name); if (t_tmp) t_tmp->marked_for_removal = 1; - } SMARTLIST_FOREACH_END(t_name); + } SMARTLIST_FOREACH_END(t); sweep_transport_list(); - /* free the transport names in mp->transports */ - SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name)); + /* free the transport in mp->transports */ + SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); smartlist_clear(mp->transports); /* flag it as an infant proxy so that it gets launched on next tick */ @@ -315,6 +534,7 @@ launch_managed_proxy(managed_proxy_t *mp) void pt_configure_remaining_proxies(void) { + int at_least_a_proxy_config_finished = 0; smartlist_t *tmp = smartlist_new(); log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!", @@ -352,22 +572,25 @@ pt_configure_remaining_proxies(void) if (!proxy_configuration_finished(mp)) configure_proxy(mp); + if (proxy_configuration_finished(mp)) + at_least_a_proxy_config_finished = 1; + } SMARTLIST_FOREACH_END(mp); smartlist_free(tmp); check_if_restarts_needed = 0; assert_unconfigured_count_ok(); -} -#ifdef _WIN32 + if (at_least_a_proxy_config_finished) + mark_my_descriptor_dirty("configured managed proxies"); +} /** Attempt to continue configuring managed proxy <b>mp</b>. */ static void configure_proxy(managed_proxy_t *mp) { - int pos; - char stdout_buf[200]; - smartlist_t *lines = NULL; + smartlist_t *proxy_output = NULL; + enum stream_status stream_status = 0; /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { @@ -381,28 +604,18 @@ configure_proxy(managed_proxy_t *mp) tor_assert(mp->conf_state != PT_PROTO_INFANT); tor_assert(mp->process_handle); - pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle), - stdout_buf, sizeof(stdout_buf) - 1, NULL); - if (pos < 0) { - log_notice(LD_GENERAL, "Failed to read data from managed proxy '%s'.", - mp->argv[0]); - mp->conf_state = PT_PROTO_BROKEN; + proxy_output = + tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle), + &stream_status); + if (!proxy_output) { /* failed to get input from proxy */ + if (stream_status != IO_STREAM_EAGAIN) + mp->conf_state = PT_PROTO_BROKEN; + goto done; } - if (pos == 0) /* proxy has nothing interesting to say. */ - return; - - /* End with a null even if there isn't a \r\n at the end */ - /* TODO: What if this is a partial line? */ - stdout_buf[pos] = '\0'; - - /* Split up the buffer */ - lines = smartlist_new(); - tor_split_lines(lines, stdout_buf, pos); - /* Handle lines. */ - SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { + SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) { handle_proxy_line(line, mp); if (proxy_configuration_finished(mp)) goto done; @@ -413,123 +626,56 @@ configure_proxy(managed_proxy_t *mp) if (proxy_configuration_finished(mp)) handle_finished_proxy(mp); - if (lines) - smartlist_free(lines); -} - -#else /* _WIN32 */ - -/** Attempt to continue configuring managed proxy <b>mp</b>. */ -static void -configure_proxy(managed_proxy_t *mp) -{ - enum stream_status r; - char stdout_buf[200]; - - /* if we haven't launched the proxy yet, do it now */ - if (mp->conf_state == PT_PROTO_INFANT) { - if (launch_managed_proxy(mp) < 0) { /* launch fail */ - mp->conf_state = PT_PROTO_FAILED_LAUNCH; - handle_finished_proxy(mp); - } - return; - } - - tor_assert(mp->conf_state != PT_PROTO_INFANT); - tor_assert(mp->process_handle); - - while (1) { - r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle), - stdout_buf, sizeof(stdout_buf) - 1); - - if (r == IO_STREAM_OKAY) { /* got a line; handle it! */ - handle_proxy_line((const char *)stdout_buf, mp); - } else if (r == IO_STREAM_EAGAIN) { /* check back later */ - return; - } else if (r == IO_STREAM_CLOSED || r == IO_STREAM_TERM) { /* snap! */ - log_warn(LD_GENERAL, "Our communication channel with the managed proxy " - "'%s' closed. Most probably application stopped running.", - mp->argv[0]); - mp->conf_state = PT_PROTO_BROKEN; - } else { /* unknown stream status */ - log_warn(LD_BUG, "Unknown stream status '%d' while configuring managed " - "proxy '%s'.", (int)r, mp->argv[0]); - } - - /* if the proxy finished configuring, exit the loop. */ - if (proxy_configuration_finished(mp)) { - handle_finished_proxy(mp); - return; - } + if (proxy_output) { + SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp)); + smartlist_free(proxy_output); } } -#endif /* _WIN32 */ - /** Register server managed proxy <b>mp</b> transports to state */ static void -register_server_proxy(managed_proxy_t *mp) +register_server_proxy(const managed_proxy_t *mp) { - /* After we register this proxy's transports, we switch its - mp->transports to a list containing strings of its transport - names. (See transports.h) */ - smartlist_t *sm_tmp = smartlist_new(); - tor_assert(mp->conf_state != PT_PROTO_COMPLETED); + SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { save_transport_to_state(t->name, &t->addr, t->port); log_notice(LD_GENERAL, "Registered server transport '%s' at '%s:%d'", t->name, fmt_addr(&t->addr), (int)t->port); - smartlist_add(sm_tmp, tor_strdup(t->name)); } SMARTLIST_FOREACH_END(t); - - /* Since server proxies don't register their transports in the - circuitbuild.c subsystem, it's our duty to free them when we - switch mp->transports to strings. */ - SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); - smartlist_free(mp->transports); - - mp->transports = sm_tmp; } /** Register all the transports supported by client managed proxy * <b>mp</b> to the bridge subsystem. */ static void -register_client_proxy(managed_proxy_t *mp) +register_client_proxy(const managed_proxy_t *mp) { int r; - /* After we register this proxy's transports, we switch its - mp->transports to a list containing strings of its transport - names. (See transports.h) */ - smartlist_t *sm_tmp = smartlist_new(); tor_assert(mp->conf_state != PT_PROTO_COMPLETED); + SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { - r = transport_add(t); + transport_t *transport_tmp = transport_copy(t); + r = transport_add(transport_tmp); switch (r) { case -1: log_notice(LD_GENERAL, "Could not add transport %s. Skipping.", t->name); - transport_free(t); + transport_free(transport_tmp); break; case 0: log_info(LD_GENERAL, "Succesfully registered transport %s", t->name); - smartlist_add(sm_tmp, tor_strdup(t->name)); break; case 1: log_info(LD_GENERAL, "Succesfully registered transport %s", t->name); - smartlist_add(sm_tmp, tor_strdup(t->name)); - transport_free(t); + transport_free(transport_tmp); break; } } SMARTLIST_FOREACH_END(t); - - smartlist_free(mp->transports); - mp->transports = sm_tmp; } /** Register the transports of managed proxy <b>mp</b>. */ static INLINE void -register_proxy(managed_proxy_t *mp) +register_proxy(const managed_proxy_t *mp) { if (mp->is_server) register_server_proxy(mp); @@ -542,10 +688,7 @@ static void managed_proxy_destroy(managed_proxy_t *mp, int also_terminate_process) { - if (mp->conf_state != PT_PROTO_COMPLETED) - SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); - else - SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name)); + SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); /* free the transports smartlist */ smartlist_free(mp->transports); @@ -1181,6 +1324,91 @@ pt_prepare_proxy_list_for_config_read(void) tor_assert(unconfigured_proxies_n == 0); } +/** Return a smartlist containing the ports where our pluggable + * transports are listening. */ +smartlist_t * +get_transport_proxy_ports(void) +{ + smartlist_t *sl = NULL; + + if (!managed_proxy_list) + return NULL; + + /** XXX assume that external proxy ports have been forwarded + manually */ + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + if (!mp->is_server || mp->conf_state != PT_PROTO_COMPLETED) + continue; + + if (!sl) sl = smartlist_new(); + + tor_assert(mp->transports); + SMARTLIST_FOREACH(mp->transports, const transport_t *, t, + smartlist_add_asprintf(sl, "%u:%u", t->port, t->port)); + + } SMARTLIST_FOREACH_END(mp); + + return sl; +} + +/** Return the pluggable transport string that we should display in + * our extra-info descriptor. If we shouldn't display such a string, + * or we have nothing to display, return NULL. The string is + * allocated on the heap and it's the responsibility of the caller to + * free it. */ +char * +pt_get_extra_info_descriptor_string(void) +{ + char *the_string = NULL; + smartlist_t *string_chunks = NULL; + + if (!managed_proxy_list) + return NULL; + + string_chunks = smartlist_new(); + + /* For each managed proxy, add its transports to the chunks list. */ + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + if ((!mp->is_server) || (mp->conf_state != PT_PROTO_COMPLETED)) + continue; + + tor_assert(mp->transports); + + SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { + /* If the transport proxy returned "0.0.0.0" as its address, and + * we know our external IP address, use it. Otherwise, use the + * returned address. */ + const char *addr_str = fmt_addr(&t->addr); + uint32_t external_ip_address = 0; + if (tor_addr_is_null(&t->addr) && + router_pick_published_address(get_options(), + &external_ip_address) >= 0) { + /* returned addr was 0.0.0.0 and we found our external IP + address: use it. */ + addr_str = fmt_addr32(external_ip_address); + } + + smartlist_add_asprintf(string_chunks, + "transport %s %s:%u", + t->name, addr_str, t->port); + } SMARTLIST_FOREACH_END(t); + + } SMARTLIST_FOREACH_END(mp); + + if (smartlist_len(string_chunks) == 0) { + smartlist_free(string_chunks); + return NULL; + } + + /* Join all the chunks into the final string. */ + the_string = smartlist_join_strings(string_chunks, "\n", 1, NULL); + + SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s)); + smartlist_free(string_chunks); + + return the_string; +} + /** The tor config was read. * Destroy all managed proxies that were marked by a previous call to * prepare_proxy_list_for_config_read() and are not used by the new @@ -1204,6 +1432,12 @@ sweep_proxy_list(void) void pt_free_all(void) { + if (transport_list) { + clear_transport_list(); + smartlist_free(transport_list); + transport_list = NULL; + } + if (managed_proxy_list) { /* If the proxy is in PT_PROTO_COMPLETED, it has registered its transports and it's the duty of the circuitbuild.c subsystem to diff --git a/src/or/transports.h b/src/or/transports.h index 02f159a5d6..86a2530fcb 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -11,6 +11,30 @@ #ifndef TOR_TRANSPORTS_H #define TOR_TRANSPORTS_H +/** Represents a pluggable transport used by a bridge. */ +typedef struct transport_t { + /** SOCKS version: One of PROXY_SOCKS4, PROXY_SOCKS5. */ + int socks_version; + /** Name of pluggable transport protocol */ + char *name; + /** The IP address where the transport bound and is waiting for + * connections. */ + tor_addr_t addr; + /** Port of proxy */ + uint16_t port; + /** Boolean: We are re-parsing our transport list, and we are going to remove + * this one if we don't find it in the list of configured transports. */ + unsigned marked_for_removal : 1; +} transport_t; + +void mark_transport_list(void); +void sweep_transport_list(void); +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); + +transport_t *transport_get_by_name(const char *name); + void pt_kickstart_proxy(const smartlist_t *transport_list, char **proxy_argv, int is_server); @@ -23,11 +47,15 @@ void pt_configure_remaining_proxies(void); int pt_proxies_configuration_pending(void); +char *pt_get_extra_info_descriptor_string(void); + void pt_free_all(void); void pt_prepare_proxy_list_for_config_read(void); void sweep_proxy_list(void); +smartlist_t *get_transport_proxy_ports(void); + #ifdef PT_PRIVATE /** State of the managed proxy configuration protocol. */ enum pt_proto_state { @@ -68,28 +96,7 @@ typedef struct { smartlist_t *transports_to_launch; /* The 'transports' list contains all the transports this proxy has - launched. - - Before a managed_proxy_t reaches the PT_PROTO_COMPLETED phase, - this smartlist contains a 'transport_t' for every transport it - has launched. - - When the managed_proxy_t reaches the PT_PROTO_COMPLETED phase, it - registers all its transports to the circuitbuild.c subsystem. At - that point the 'transport_t's are owned by the circuitbuild.c - subsystem. - - To avoid carrying dangling 'transport_t's in this smartlist, - right before the managed_proxy_t reaches the PT_PROTO_COMPLETED - phase we replace all 'transport_t's with strings of their - transport names. - - So, tl;dr: - When (conf_state != PT_PROTO_COMPLETED) this list carries - (transport_t *). - When (conf_state == PT_PROTO_COMPLETED) this list carries - (char *). - */ + launched. */ smartlist_t *transports; } managed_proxy_t; diff --git a/src/test/Makefile.am b/src/test/Makefile.am deleted file mode 100644 index 31a464ee7a..0000000000 --- a/src/test/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -TESTS = test - -noinst_PROGRAMS = test test-child bench - -AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ - -DLOCALSTATEDIR="\"$(localstatedir)\"" \ - -DBINDIR="\"$(bindir)\"" \ - -I"$(top_srcdir)/src/or" - -# -L flags need to go in LDFLAGS. -l flags need to go in LDADD. -# This seems to matter nowhere but on Windows, but I assure you that it -# matters a lot there, and is quite hard to debug if you forget to do it. - -test_SOURCES = \ - test.c \ - test_addr.c \ - test_containers.c \ - test_crypto.c \ - test_data.c \ - test_dir.c \ - test_microdesc.c \ - test_pt.c \ - test_util.c \ - test_config.c \ - tinytest.c - -bench_SOURCES = \ - bench.c - -test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ - @TOR_LDFLAGS_libevent@ -test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \ - ../common/libor-event.a \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ - -bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ - @TOR_LDFLAGS_libevent@ -bench_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \ - ../common/libor-event.a \ - @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ - -noinst_HEADERS = \ - tinytest.h \ - tinytest_macros.h \ - test.h - - diff --git a/src/test/include.am b/src/test/include.am new file mode 100644 index 0000000000..03fef23375 --- /dev/null +++ b/src/test/include.am @@ -0,0 +1,55 @@ +TESTS+= src/test/test + +noinst_PROGRAMS+= src/test/test src/test/test-child src/test/bench + +src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ + -DLOCALSTATEDIR="\"$(localstatedir)\"" \ + -DBINDIR="\"$(bindir)\"" \ + -I"$(top_srcdir)/src/or" + +# -L flags need to go in LDFLAGS. -l flags need to go in LDADD. +# This seems to matter nowhere but on Windows, but I assure you that it +# matters a lot there, and is quite hard to debug if you forget to do it. + +src_test_test_SOURCES = \ + src/test/test.c \ + src/test/test_addr.c \ + src/test/test_containers.c \ + src/test/test_crypto.c \ + src/test/test_data.c \ + src/test/test_dir.c \ + src/test/test_introduce.c \ + src/test/test_microdesc.c \ + src/test/test_pt.c \ + src/test/test_replay.c \ + src/test/test_util.c \ + src/test/test_config.c \ + src/test/tinytest.c + +src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) + +src_test_bench_SOURCES = \ + src/test/bench.c + +src_test_bench_CPPFLAGS= $(src_test_AM_CPPFLAGS) + +src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ + @TOR_LDFLAGS_libevent@ +src_test_test_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \ + src/common/libor-event.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ + +src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ + @TOR_LDFLAGS_libevent@ +src_test_bench_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \ + src/common/libor-event.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ + +noinst_HEADERS+= \ + src/test/tinytest.h \ + src/test/tinytest_macros.h \ + src/test/test.h + + diff --git a/src/test/test.c b/src/test/test.c index 6bf2d28d90..81172795f2 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1863,6 +1863,8 @@ extern struct testcase_t dir_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t pt_tests[]; extern struct testcase_t config_tests[]; +extern struct testcase_t introduce_tests[]; +extern struct testcase_t replaycache_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1875,6 +1877,8 @@ static struct testgroup_t testgroups[] = { { "dir/md/", microdesc_tests }, { "pt/", pt_tests }, { "config/", config_tests }, + { "replaycache/", replaycache_tests }, + { "introduce/", introduce_tests }, END_OF_GROUPS }; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 3f78ece6a0..9bf44b116b 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -150,9 +150,9 @@ test_dir_formats(void) "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" - "opt protocols Link 1 2 Circuit 1\n" + "protocols Link 1 2 Circuit 1\n" "published 1970-01-01 00:00:00\n" - "opt fingerprint ", sizeof(buf2)); + "fingerprint ", sizeof(buf2)); test_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1)); strlcat(buf2, fingerprint, sizeof(buf2)); strlcat(buf2, "\nuptime 0\n" @@ -164,7 +164,7 @@ test_dir_formats(void) strlcat(buf2, pk1_str, sizeof(buf2)); strlcat(buf2, "signing-key\n", sizeof(buf2)); strlcat(buf2, pk2_str, sizeof(buf2)); - strlcat(buf2, "opt hidden-service-dir\n", sizeof(buf2)); + strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2)); buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same * twice */ @@ -800,6 +800,7 @@ test_dir_v3_networkstatus(void) networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL, *con_md=NULL; vote_routerstatus_t *vrs; + tor_addr_t addr_ipv6; routerstatus_t *rs; char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp; smartlist_t *votes = smartlist_new(); @@ -896,6 +897,9 @@ test_dir_v3_networkstatus(void) rs->addr = 0x99009901; rs->or_port = 443; rs->dir_port = 0; + tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); + tor_addr_copy(&rs->ipv6_addr, &addr_ipv6); + rs->ipv6_orport = 4711; rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1; smartlist_add(vote->routerstatus_list, vrs); @@ -990,6 +994,8 @@ test_dir_v3_networkstatus(void) test_eq(rs->addr, 0x99009901); test_eq(rs->or_port, 443); test_eq(rs->dir_port, 0); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority." { @@ -1172,6 +1178,8 @@ test_dir_v3_networkstatus(void) test_eq(rs->addr, 0x99009901); test_eq(rs->or_port, 443); test_eq(rs->dir_port, 0); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); test_assert(!rs->is_authority); test_assert(rs->is_exit); test_assert(rs->is_fast); diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c new file mode 100644 index 0000000000..992d9cd507 --- /dev/null +++ b/src/test/test_introduce.c @@ -0,0 +1,528 @@ +/* Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "crypto.h" +#include "or.h" +#include "test.h" + +#define RENDSERVICE_PRIVATE +#include "rendservice.h" + +extern const char AUTHORITY_SIGNKEY_1[]; + +static uint8_t v0_test_plaintext[] = + /* 20 bytes of rendezvous point nickname */ + { 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* 20 bytes dummy rendezvous cookie */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 128 bytes dummy DH handshake data */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; + +static uint8_t v1_test_plaintext[] = + /* Version byte */ + { 0x01, + /* 42 bytes of dummy rendezvous point hex digest */ + 0x24, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, + 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, + 0x37, 0x30, 0x38, 0x30, 0x39, 0x30, 0x41, 0x30, + 0x42, 0x30, 0x43, 0x30, 0x44, 0x30, 0x45, 0x30, + 0x46, 0x31, 0x30, 0x31, 0x31, 0x31, 0x32, 0x31, + 0x33, 0x00, + /* 20 bytes dummy rendezvous cookie */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 128 bytes dummy DH handshake data */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; + +static uint8_t v2_test_plaintext[] = + /* Version byte */ + { 0x02, + /* 4 bytes rendezvous point's IP address */ + 0xc0, 0xa8, 0x00, 0x01, + /* 2 bytes rendezvous point's OR port */ + 0x23, 0x5a, + /* 20 bytes dummy rendezvous point's identity digest */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 2 bytes length of onion key */ + 0x00, 0x8c, + /* Onion key (140 bytes taken from live test) */ + 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1, + 0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8, + 0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4, + 0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6, + 0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61, + 0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa, + 0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11, + 0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1, + 0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d, + 0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0, + 0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f, + 0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4, + 0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31, + 0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb, + 0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f, + 0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92, + 0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02, + 0x03, 0x01, 0x00, 0x01, + /* 20 bytes dummy rendezvous cookie */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 128 bytes dummy DH handshake data */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; + +static uint8_t v3_no_auth_test_plaintext[] = + /* Version byte */ + { 0x03, + /* Auth type (0 for no auth len/auth data) */ + 0x00, + /* Timestamp */ + 0x50, 0x0b, 0xb5, 0xaa, + /* 4 bytes rendezvous point's IP address */ + 0xc0, 0xa8, 0x00, 0x01, + /* 2 bytes rendezvous point's OR port */ + 0x23, 0x5a, + /* 20 bytes dummy rendezvous point's identity digest */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 2 bytes length of onion key */ + 0x00, 0x8c, + /* Onion key (140 bytes taken from live test) */ + 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1, + 0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8, + 0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4, + 0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6, + 0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61, + 0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa, + 0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11, + 0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1, + 0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d, + 0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0, + 0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f, + 0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4, + 0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31, + 0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb, + 0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f, + 0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92, + 0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02, + 0x03, 0x01, 0x00, 0x01, + /* 20 bytes dummy rendezvous cookie */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 128 bytes dummy DH handshake data */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; + +static uint8_t v3_basic_auth_test_plaintext[] = + /* Version byte */ + { 0x03, + /* Auth type (1 for REND_BASIC_AUTH) */ + 0x01, + /* Auth len (must be 16 bytes for REND_BASIC_AUTH) */ + 0x00, 0x10, + /* Auth data (a 16-byte dummy descriptor cookie) */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /* Timestamp */ + 0x50, 0x0b, 0xb5, 0xaa, + /* 4 bytes rendezvous point's IP address */ + 0xc0, 0xa8, 0x00, 0x01, + /* 2 bytes rendezvous point's OR port */ + 0x23, 0x5a, + /* 20 bytes dummy rendezvous point's identity digest */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 2 bytes length of onion key */ + 0x00, 0x8c, + /* Onion key (140 bytes taken from live test) */ + 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1, + 0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8, + 0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4, + 0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6, + 0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61, + 0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa, + 0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11, + 0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1, + 0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d, + 0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0, + 0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f, + 0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4, + 0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31, + 0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb, + 0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f, + 0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92, + 0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02, + 0x03, 0x01, 0x00, 0x01, + /* 20 bytes dummy rendezvous cookie */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + /* 128 bytes dummy DH handshake data */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; + +static void do_decrypt_test(uint8_t *plaintext, size_t plaintext_len); +static void do_early_parse_test(uint8_t *plaintext, size_t plaintext_len); +static void do_late_parse_test(uint8_t *plaintext, size_t plaintext_len); +static void do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase); +static ssize_t make_intro_from_plaintext( + void *buf, size_t len, crypto_pk_t *key, void **cell_out); + +#define EARLY_PARSE_ONLY 1 +#define DECRYPT_ONLY 2 +#define ALL_PARSING 3 + +static void +do_early_parse_test(uint8_t *plaintext, size_t plaintext_len) +{ + do_parse_test(plaintext, plaintext_len, EARLY_PARSE_ONLY); +} + +static void +do_decrypt_test(uint8_t *plaintext, size_t plaintext_len) +{ + do_parse_test(plaintext, plaintext_len, DECRYPT_ONLY); +} + +static void +do_late_parse_test(uint8_t *plaintext, size_t plaintext_len) +{ + do_parse_test(plaintext, plaintext_len, ALL_PARSING); +} + +/** Test utility function: checks that the <b>plaintext_len</b>-byte string at + * <b>plaintext</b> is at least superficially parseable. + */ +static void +do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase) +{ + crypto_pk_t *k = NULL; + ssize_t r; + uint8_t *cell = NULL; + size_t cell_len; + rend_intro_cell_t *parsed_req = NULL; + char *err_msg = NULL; + char digest[DIGEST_LEN]; + + /* Get a key */ + k = crypto_pk_new(); + test_assert(k); + r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_1, -1); + test_assert(!r); + + /* Get digest for future comparison */ + r = crypto_pk_get_digest(k, digest); + test_assert(r >= 0); + + /* Make a cell out of it */ + r = make_intro_from_plaintext( + plaintext, plaintext_len, + k, (void **)(&cell)); + test_assert(r > 0); + test_assert(cell); + cell_len = r; + + /* Do early parsing */ + parsed_req = rend_service_begin_parse_intro(cell, cell_len, 2, &err_msg); + test_assert(parsed_req); + test_assert(!err_msg); + test_memeq(parsed_req->pk, digest, DIGEST_LEN); + test_assert(parsed_req->ciphertext); + test_assert(parsed_req->ciphertext_len > 0); + + if (phase == EARLY_PARSE_ONLY) + goto done; + + /* Do decryption */ + r = rend_service_decrypt_intro(parsed_req, k, &err_msg); + test_assert(!r); + test_assert(!err_msg); + test_assert(parsed_req->plaintext); + test_assert(parsed_req->plaintext_len > 0); + + if (phase == DECRYPT_ONLY) + goto done; + + /* Do late parsing */ + r = rend_service_parse_intro_plaintext(parsed_req, &err_msg); + test_assert(!r); + test_assert(!err_msg); + test_assert(parsed_req->parsed); + + done: + tor_free(cell); + crypto_pk_free(k); + rend_service_free_intro(parsed_req); + tor_free(err_msg); +} + +/** Given the plaintext of the encrypted part of an INTRODUCE1/2 and a key, + * construct the encrypted cell for testing. + */ + +static ssize_t +make_intro_from_plaintext( + void *buf, size_t len, crypto_pk_t *key, void **cell_out) +{ + char *cell = NULL; + ssize_t cell_len = -1, r; + /* Assemble key digest and ciphertext, then construct the cell */ + ssize_t ciphertext_size; + + if (!(buf && key && len > 0 && cell_out)) goto done; + + /* + * Figure out an upper bound on how big the ciphertext will be + * (see crypto_pk_public_hybrid_encrypt()) + */ + ciphertext_size = PKCS1_OAEP_PADDING_OVERHEAD; + ciphertext_size += crypto_pk_keysize(key); + ciphertext_size += CIPHER_KEY_LEN; + ciphertext_size += len; + + /* + * Allocate space for the cell + */ + cell = tor_malloc(DIGEST_LEN + ciphertext_size); + + /* Compute key digest (will be first DIGEST_LEN octets of cell) */ + r = crypto_pk_get_digest(key, cell); + test_assert(r >= 0); + + /* Do encryption */ + r = crypto_pk_public_hybrid_encrypt( + key, cell + DIGEST_LEN, ciphertext_size, + buf, len, + PK_PKCS1_OAEP_PADDING, 0); + test_assert(r >= 0); + + /* Figure out cell length */ + cell_len = DIGEST_LEN + r; + + /* Output the cell */ + *cell_out = cell; + + done: + return cell_len; +} + +/** Test v0 INTRODUCE2 parsing through decryption only + */ + +static void +test_introduce_decrypt_v0(void) +{ + do_decrypt_test(v0_test_plaintext, sizeof(v0_test_plaintext)); +} + +/** Test v1 INTRODUCE2 parsing through decryption only + */ + +static void +test_introduce_decrypt_v1(void) +{ + do_decrypt_test(v1_test_plaintext, sizeof(v1_test_plaintext)); +} + +/** Test v2 INTRODUCE2 parsing through decryption only + */ + +static void +test_introduce_decrypt_v2(void) +{ + do_decrypt_test(v2_test_plaintext, sizeof(v2_test_plaintext)); +} + +/** Test v3 INTRODUCE2 parsing through decryption only + */ + +static void +test_introduce_decrypt_v3(void) +{ + do_decrypt_test( + v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext)); + do_decrypt_test( + v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext)); +} + +/** Test v0 INTRODUCE2 parsing through early parsing only + */ + +static void +test_introduce_early_parse_v0(void) +{ + do_early_parse_test(v0_test_plaintext, sizeof(v0_test_plaintext)); +} + +/** Test v1 INTRODUCE2 parsing through early parsing only + */ + +static void +test_introduce_early_parse_v1(void) +{ + do_early_parse_test(v1_test_plaintext, sizeof(v1_test_plaintext)); +} + +/** Test v2 INTRODUCE2 parsing through early parsing only + */ + +static void +test_introduce_early_parse_v2(void) +{ + do_early_parse_test(v2_test_plaintext, sizeof(v2_test_plaintext)); +} + +/** Test v3 INTRODUCE2 parsing through early parsing only + */ + +static void +test_introduce_early_parse_v3(void) +{ + do_early_parse_test( + v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext)); + do_early_parse_test( + v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext)); +} + +/** Test v0 INTRODUCE2 parsing + */ + +static void +test_introduce_late_parse_v0(void) +{ + do_late_parse_test(v0_test_plaintext, sizeof(v0_test_plaintext)); +} + +/** Test v1 INTRODUCE2 parsing + */ + +static void +test_introduce_late_parse_v1(void) +{ + do_late_parse_test(v1_test_plaintext, sizeof(v1_test_plaintext)); +} + +/** Test v2 INTRODUCE2 parsing + */ + +static void +test_introduce_late_parse_v2(void) +{ + do_late_parse_test(v2_test_plaintext, sizeof(v2_test_plaintext)); +} + +/** Test v3 INTRODUCE2 parsing + */ + +static void +test_introduce_late_parse_v3(void) +{ + do_late_parse_test( + v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext)); + do_late_parse_test( + v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext)); +} + +#define INTRODUCE_LEGACY(name) \ + { #name, legacy_test_helper, 0, &legacy_setup, test_introduce_ ## name } + +struct testcase_t introduce_tests[] = { + INTRODUCE_LEGACY(early_parse_v0), + INTRODUCE_LEGACY(early_parse_v1), + INTRODUCE_LEGACY(early_parse_v2), + INTRODUCE_LEGACY(early_parse_v3), + INTRODUCE_LEGACY(decrypt_v0), + INTRODUCE_LEGACY(decrypt_v1), + INTRODUCE_LEGACY(decrypt_v2), + INTRODUCE_LEGACY(decrypt_v3), + INTRODUCE_LEGACY(late_parse_v0), + INTRODUCE_LEGACY(late_parse_v1), + INTRODUCE_LEGACY(late_parse_v2), + INTRODUCE_LEGACY(late_parse_v3), + END_OF_TESTCASES +}; + diff --git a/src/test/test_replay.c b/src/test/test_replay.c new file mode 100644 index 0000000000..b08818f06b --- /dev/null +++ b/src/test/test_replay.c @@ -0,0 +1,184 @@ +/* Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define REPLAYCACHE_PRIVATE + +#include "orconfig.h" +#include "or.h" +#include "replaycache.h" +#include "test.h" + +static const char *test_buffer = + "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed do eiusmod" + " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" + " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea" + " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate" + " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" + " occaecat cupidatat non proident, sunt in culpa qui officia deserunt" + " mollit anim id est laborum."; + +static void +test_replaycache_alloc(void) +{ + replaycache_t *r = NULL; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + if (!r) goto done; + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_miss(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + if (!r) goto done; + + result = + replaycache_add_and_test_internal(1200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_hit(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + if (!r) goto done; + + result = + replaycache_add_and_test_internal(1200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(1300, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 1); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_age(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + if (!r) goto done; + + result = + replaycache_add_and_test_internal(1200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(1300, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 1); + + result = + replaycache_add_and_test_internal(3000, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_elapsed(void) +{ + replaycache_t *r = NULL; + int result; + time_t elapsed; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + if (!r) goto done; + + result = + replaycache_add_and_test_internal(1200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(1300, r, test_buffer, + (int)strlen(test_buffer), &elapsed); + test_eq(result, 1); + test_eq(elapsed, 100); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_noexpire(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(0, 0); + test_assert(r != NULL); + if (!r) goto done; + + result = + replaycache_add_and_test_internal(1200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(1300, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 1); + + result = + replaycache_add_and_test_internal(3000, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 1); + + done: + if (r) replaycache_free(r); + + return; +} + +#define REPLAYCACHE_LEGACY(name) \ + { #name, legacy_test_helper, 0, &legacy_setup, test_replaycache_ ## name } + +struct testcase_t replaycache_tests[] = { + REPLAYCACHE_LEGACY(alloc), + REPLAYCACHE_LEGACY(miss), + REPLAYCACHE_LEGACY(hit), + REPLAYCACHE_LEGACY(age), + REPLAYCACHE_LEGACY(elapsed), + REPLAYCACHE_LEGACY(noexpire), + END_OF_TESTCASES +}; + diff --git a/src/test/test_util.c b/src/test/test_util.c index 9eca90492d..7ef4d1f78c 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -18,6 +18,19 @@ #include <tchar.h> #endif +/* XXXX this is a minimal wrapper to make the unit tests compile with the + * changed tor_timegm interface. */ +static time_t +tor_timegm_wrapper(const struct tm *tm) +{ + time_t t; + if (tor_timegm(tm, &t) < 0) + return -1; + return t; +} + +#define tor_timegm tor_timegm_wrapper + static void test_util_time(void) { @@ -1461,12 +1474,28 @@ test_util_control_formats(void) tor_free(out); } +#define test_feq(value1,value2) do { \ + double v1 = (value1), v2=(value2); \ + double tf_diff = v1-v2; \ + double tf_tolerance = ((v1+v2)/2.0)/1e8; \ + if (tf_diff<0) tf_diff=-tf_diff; \ + if (tf_tolerance<0) tf_tolerance=-tf_tolerance; \ + if (tf_diff<tf_tolerance) { \ + TT_BLATHER(("%s ~~ %s: %f ~~ %f",#value1,#value2,v1,v2)); \ + } else { \ + TT_FAIL(("%s ~~ %s: %f != %f",#value1,#value2,v1,v2)); \ + } \ + } while (0) + static void test_util_sscanf(void) { unsigned u1, u2, u3; char s1[20], s2[10], s3[10], ch; int r; + long lng1,lng2; + int int1, int2; + double d1,d2,d3,d4; /* Simple tests (malformed patterns, literal matching, ...) */ test_eq(-1, tor_sscanf("123", "%i", &r)); /* %i is not supported */ @@ -1595,6 +1624,65 @@ test_util_sscanf(void) test_eq(4, tor_sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch)); test_eq(' ', ch); + r = tor_sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2); + test_eq(r,3); + test_eq(int1, 12345); + test_eq(lng1, -67890); + test_eq(int2, -1); + +#if SIZEOF_INT == 4 + r = tor_sscanf("-2147483648. 2147483647.", "%d. %d.", &int1, &int2); + test_eq(r,2); + test_eq(int1, -2147483647-1); + test_eq(int2, 2147483647); + + r = tor_sscanf("-2147483679.", "%d.", &int1); + test_eq(r,0); + + r = tor_sscanf("2147483678.", "%d.", &int1); + test_eq(r,0); +#elif SIZEOF_INT == 8 + r = tor_sscanf("-9223372036854775808. 9223372036854775807.", + "%d. %d.", &int1, &int2); + test_eq(r,2); + test_eq(int1, -9223372036854775807-1); + test_eq(int2, 9223372036854775807); + + r = tor_sscanf("-9223372036854775809.", "%d.", &int1); + test_eq(r,0); + + r = tor_sscanf("9223372036854775808.", "%d.", &int1); + test_eq(r,0); +#endif + +#if SIZEOF_LONG == 4 + r = tor_sscanf("-2147483648. 2147483647.", "%ld. %ld.", &lng1, &lng2); + test_eq(r,2); + test_eq(lng1, -2147483647 - 1); + test_eq(lng2, 2147483647); +#elif SIZEOF_LONG == 8 + r = tor_sscanf("-9223372036854775808. 9223372036854775807.", + "%ld. %ld.", &lng1, &lng2); + test_eq(r,2); + test_eq(lng1, -9223372036854775807L - 1); + test_eq(lng2, 9223372036854775807L); + + r = tor_sscanf("-9223372036854775808. 9223372036854775808.", + "%ld. %ld.", &lng1, &lng2); + test_eq(r,1); + r = tor_sscanf("-9223372036854775809. 9223372036854775808.", + "%ld. %ld.", &lng1, &lng2); + test_eq(r,0); +#endif + + r = tor_sscanf("123.456 .000007 -900123123.2000787 00003.2", + "%lf %lf %lf %lf", &d1,&d2,&d3,&d4); + test_eq(r,4); + test_feq(d1, 123.456); + test_feq(d2, .000007); + test_feq(d3, -900123123.2000787); + test_feq(d4, 3.2); + done: ; } diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am deleted file mode 100644 index 35b0a41f53..0000000000 --- a/src/tools/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -bin_PROGRAMS = tor-resolve tor-gencert -noinst_PROGRAMS = tor-checkkey - -tor_resolve_SOURCES = tor-resolve.c -tor_resolve_LDFLAGS = -tor_resolve_LDADD = ../common/libor.a @TOR_LIB_MATH@ @TOR_LIB_WS32@ - -tor_gencert_SOURCES = tor-gencert.c -tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ -tor_gencert_LDADD = ../common/libor.a ../common/libor-crypto.a \ - @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ - -tor_checkkey_SOURCES = tor-checkkey.c -tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ -tor_checkkey_LDADD = ../common/libor.a ../common/libor-crypto.a \ - @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ - @TOR_LIB_WS32@ @TOR_LIB_GDI@ - -SUBDIRS = tor-fw-helper -DIST_SUBDIRS = tor-fw-helper - diff --git a/src/tools/include.am b/src/tools/include.am new file mode 100644 index 0000000000..7337eff163 --- /dev/null +++ b/src/tools/include.am @@ -0,0 +1,22 @@ +bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert +noinst_PROGRAMS+= src/tools/tor-checkkey + +src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c +src_tools_tor_resolve_LDFLAGS = +src_tools_tor_resolve_LDADD = src/common/libor.a @TOR_LIB_MATH@ @TOR_LIB_WS32@ + +src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c +src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ + +src_tools_tor_checkkey_SOURCES = src/tools/tor-checkkey.c +src_tools_tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_checkkey_LDADD = src/common/libor.a src/common/libor-crypto.a \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ + +include src/tools/tor-fw-helper/include.am + + diff --git a/src/tools/tor-fw-helper/Makefile.am b/src/tools/tor-fw-helper/Makefile.am deleted file mode 100644 index 393562db03..0000000000 --- a/src/tools/tor-fw-helper/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -if USE_FW_HELPER -bin_PROGRAMS = tor-fw-helper -else -bin_PROGRAMS = -endif - -tor_fw_helper_SOURCES = \ - tor-fw-helper.c \ - tor-fw-helper-natpmp.c \ - tor-fw-helper-upnp.c -noinst_HEADERS = \ - tor-fw-helper.h \ - tor-fw-helper-natpmp.h \ - tor-fw-helper-upnp.h - -if NAT_PMP -nat_pmp_ldflags = @TOR_LDFLAGS_libnatpmp@ -nat_pmp_ldadd = -lnatpmp @TOR_LIB_IPHLPAPI@ -nat_pmp_cppflags = @TOR_CPPFLAGS_libnatpmp@ -else -nat_pmp_ldflags = -nat_pmp_ldadd = -nat_pmp_cppflags = -endif - -if MINIUPNPC -miniupnpc_ldflags = @TOR_LDFLAGS_libminiupnpc@ -miniupnpc_ldadd = -lminiupnpc -lm @TOR_LIB_IPHLPAPI@ -miniupnpc_cppflags = @TOR_CPPFLAGS_libminiupnpc@ -else -miniupnpc_ldflags = -miniupnpc_ldadd = -miniupnpc_cppflags = -endif - -tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags) -tor_fw_helper_LDADD = ../../common/libor.a $(nat_pmp_ldadd) $(miniupnpc_ldadd) @TOR_LIB_WS32@ -tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags) diff --git a/src/tools/tor-fw-helper/include.am b/src/tools/tor-fw-helper/include.am new file mode 100644 index 0000000000..cb6c9cd560 --- /dev/null +++ b/src/tools/tor-fw-helper/include.am @@ -0,0 +1,36 @@ +if USE_FW_HELPER +bin_PROGRAMS+= src/tools/tor-fw-helper/tor-fw-helper +endif + +src_tools_tor_fw_helper_tor_fw_helper_SOURCES = \ + src/tools/tor-fw-helper/tor-fw-helper.c \ + src/tools/tor-fw-helper/tor-fw-helper-natpmp.c \ + src/tools/tor-fw-helper/tor-fw-helper-upnp.c +noinst_HEADERS+= \ + src/tools/tor-fw-helper/tor-fw-helper.h \ + src/tools/tor-fw-helper/tor-fw-helper-natpmp.h \ + src/tools/tor-fw-helper/tor-fw-helper-upnp.h + +if NAT_PMP +nat_pmp_ldflags = @TOR_LDFLAGS_libnatpmp@ +nat_pmp_ldadd = -lnatpmp @TOR_LIB_IPHLPAPI@ +nat_pmp_cppflags = @TOR_CPPFLAGS_libnatpmp@ +else +nat_pmp_ldflags = +nat_pmp_ldadd = +nat_pmp_cppflags = +endif + +if MINIUPNPC +miniupnpc_ldflags = @TOR_LDFLAGS_libminiupnpc@ +miniupnpc_ldadd = -lminiupnpc -lm @TOR_LIB_IPHLPAPI@ +miniupnpc_cppflags = @TOR_CPPFLAGS_libminiupnpc@ +else +miniupnpc_ldflags = +miniupnpc_ldadd = +miniupnpc_cppflags = +endif + +src_tools_tor_fw_helper_tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags) +src_tools_tor_fw_helper_tor_fw_helper_LDADD = src/common/libor.a $(nat_pmp_ldadd) $(miniupnpc_ldadd) @TOR_LIB_WS32@ +src_tools_tor_fw_helper_tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags) diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c index 0e0b385f9b..ee6d5f3434 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c +++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c @@ -60,15 +60,15 @@ tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state) state->lease = NATPMP_DEFAULT_LEASE; if (tor_fw_options->verbose) - fprintf(stdout, "V: natpmp init...\n"); + fprintf(stderr, "V: natpmp init...\n"); r = initnatpmp(&(state->natpmp), 0, 0); if (r == 0) { state->init = 1; - fprintf(stdout, "tor-fw-helper: natpmp initialized...\n"); + fprintf(stderr, "V: natpmp initialized...\n"); return r; } else { - fprintf(stderr, "tor-fw-helper: natpmp failed to initialize...\n"); + fprintf(stderr, "V: natpmp failed to initialize...\n"); return r; } } @@ -80,10 +80,10 @@ tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state) natpmp_state_t *state = (natpmp_state_t *) backend_state; int r = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: natpmp cleanup...\n"); + fprintf(stderr, "V: natpmp cleanup...\n"); r = closenatpmp(&(state->natpmp)); if (tor_fw_options->verbose) - fprintf(stdout, "V: closing natpmp socket: %d\n", r); + fprintf(stderr, "V: closing natpmp socket: %d\n", r); return r; } @@ -101,7 +101,7 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout) FD_SET(fd, &fds); r = select(fd+1, &fds, NULL, NULL, timeout); if (r == -1) { - fprintf(stdout, "V: select failed in wait_until_fd_readable: %s\n", + fprintf(stderr, "V: select failed in wait_until_fd_readable: %s\n", strerror(errno)); return -1; } @@ -110,27 +110,25 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout) return 0; } -/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b> - * using the <b>natpmp_t</b> stored in <b>backend_state</b>. */ int -tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, - void *backend_state) +tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state) { - natpmp_state_t *state = (natpmp_state_t *) backend_state; int r = 0; int x = 0; int sav_errno; + natpmp_state_t *state = (natpmp_state_t *) backend_state; struct timeval timeout; - if (tor_fw_options->verbose) - fprintf(stdout, "V: sending natpmp portmapping request...\n"); + if (is_verbose) + fprintf(stderr, "V: sending natpmp portmapping request...\n"); r = sendnewportmappingrequest(&(state->natpmp), state->protocol, - tor_fw_options->internal_port, - tor_fw_options->external_port, + internal_port, + external_port, state->lease); - if (tor_fw_options->verbose) - fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " + if (is_verbose) + fprintf(stderr, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); do { @@ -139,8 +137,8 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, if (x == -1) return -1; - if (tor_fw_options->verbose) - fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n"); + if (is_verbose) + fprintf(stderr, "V: attempting to readnatpmpreponseorretry...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = errno; @@ -163,16 +161,14 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, } if (r == NATPMP_SUCCESS) { - fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to" + fprintf(stderr, "tor-fw-helper: NAT-PMP mapped public port %hu to" " localport %hu liftime %u\n", (state->response).pnu.newportmapping.mappedpublicport, (state->response).pnu.newportmapping.privateport, (state->response).pnu.newportmapping.lifetime); } - tor_fw_options->nat_pmp_status = 1; - - return r; + return (r == NATPMP_SUCCESS) ? 0 : -1; } /** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device. @@ -189,7 +185,7 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, struct timeval timeout; r = sendpublicaddressrequest(&(state->natpmp)); - fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" + fprintf(stderr, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" " %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); do { @@ -200,12 +196,12 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, return -1; if (tor_fw_options->verbose) - fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n"); + fprintf(stderr, "V: NAT-PMP attempting to read reponse...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = errno; if (tor_fw_options->verbose) - fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned" + fprintf(stderr, "V: NAT-PMP readnatpmpresponseorretry returned" " %d\n", r); if ( r < 0 && r != NATPMP_TRYAGAIN) { @@ -223,15 +219,15 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, return r; } - fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", + fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n", inet_ntoa((state->response).pnu.publicaddress.addr)); tor_fw_options->public_ip_status = 1; if (tor_fw_options->verbose) { - fprintf(stdout, "V: result = %u\n", r); - fprintf(stdout, "V: type = %u\n", (state->response).type); - fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode); - fprintf(stdout, "V: epoch = %u\n", (state->response).epoch); + fprintf(stderr, "V: result = %u\n", r); + fprintf(stderr, "V: type = %u\n", (state->response).type); + fprintf(stderr, "V: resultcode = %u\n", (state->response).resultcode); + fprintf(stderr, "V: epoch = %u\n", (state->response).epoch); } return r; diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h index 54f541bcf4..0f97236afb 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h +++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h @@ -36,8 +36,8 @@ int tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state); int tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state); -int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, - void *backend_state); +int tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, void *backend_state); diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c index 7c104f11cd..e5c33db0b4 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c +++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c @@ -91,7 +91,7 @@ tor_upnp_init(tor_fw_options_t *options, void *backend_state) assert(options); r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data), state->lanaddr, UPNP_LANADDR_SZ); - fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r, + fprintf(stderr, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r, r==UPNP_SUCCESS?"SUCCESS":"FAILED"); freeUPNPDevlist(devlist); @@ -141,7 +141,7 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state) goto err; if (externalIPAddress[0]) { - fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", + fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n", externalIPAddress); tor_upnp_cleanup(options, state); options->public_ip_status = 1; return UPNP_ERR_SUCCESS; @@ -154,44 +154,40 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state) return UPNP_ERR_GETEXTERNALIP; } -/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b> - * and store the results in <b>backend_state</b>. */ int -tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state) +tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state) { - miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state; - int r; + int retval; char internal_port_str[6]; char external_port_str[6]; + miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state; if (!state->init) { - r = tor_upnp_init(options, state); - if (r != UPNP_ERR_SUCCESS) - return r; + fprintf(stderr, "E: %s but state is not initialized.\n", __func__); + return -1; } - if (options->verbose) - fprintf(stdout, "V: internal port: %d, external port: %d\n", - (int)options->internal_port, (int)options->external_port); + if (is_verbose) + fprintf(stderr, "V: UPnP: internal port: %u, external port: %u\n", + internal_port, external_port); tor_snprintf(internal_port_str, sizeof(internal_port_str), - "%d", (int)options->internal_port); + "%u", internal_port); tor_snprintf(external_port_str, sizeof(external_port_str), - "%d", (int)options->external_port); + "%u", external_port); - r = UPNP_AddPortMapping(state->urls.controlURL, - state->data.first.servicetype, - external_port_str, internal_port_str, + retval = UPNP_AddPortMapping(state->urls.controlURL, + state->data.first.servicetype, + external_port_str, internal_port_str, #ifdef MINIUPNPC15 - state->lanaddr, UPNP_DESC, "TCP", 0); + state->lanaddr, UPNP_DESC, "TCP", 0); #else - state->lanaddr, UPNP_DESC, "TCP", 0, 0); + state->lanaddr, UPNP_DESC, "TCP", 0, 0); #endif - if (r != UPNPCOMMAND_SUCCESS) - return UPNP_ERR_ADDPORTMAPPING; - options->upnp_status = 1; - return UPNP_ERR_SUCCESS; + return (retval == UPNP_ERR_SUCCESS) ? 0 : -1; } + #endif diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h index f037c75bab..3a061981d1 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h +++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h @@ -36,7 +36,8 @@ int tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state); int tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state); -int tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state); +int tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); #endif #endif diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c index 0510e65d11..4efe515cc4 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper.c +++ b/src/tools/tor-fw-helper/tor-fw-helper.c @@ -20,6 +20,9 @@ #include <getopt.h> #include <time.h> #include <string.h> +#include <assert.h> + +#include "container.h" #ifdef _WIN32 #include <winsock2.h> @@ -45,7 +48,7 @@ typedef struct backends_t { void *backend_state[MAX_BACKENDS]; } backends_t; -/** Initalize each backend helper with the user input stored in <b>options</b> +/** Initialize each backend helper with the user input stored in <b>options</b> * and put the results in the <b>backends</b> struct. */ static int init_backends(tor_fw_options_t *options, backends_t *backends) @@ -97,10 +100,7 @@ usage(void) " [-T|--Test]\n" " [-v|--verbose]\n" " [-g|--fetch-public-ip]\n" - " -i|--internal-or-port [TCP port]\n" - " [-e|--external-or-port [TCP port]]\n" - " [-d|--internal-dir-port [TCP port]\n" - " [-p|--external-dir-port [TCP port]]]\n"); + " [-p|--forward-port ([<external port>]:<internal port>])\n"); } /** Log commandline options to a hardcoded file <b>tor-fw-helper.log</b> in the @@ -125,7 +125,7 @@ log_commandline_options(int argc, char **argv) if (retval < 0) goto error; - retval = fprintf(stdout, "ARG: %d: %s\n", i, argv[i]); + retval = fprintf(stderr, "ARG: %d: %s\n", i, argv[i]); if (retval < 0) goto error; } @@ -152,82 +152,141 @@ tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options, int r = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_fetch_public_ip\n"); + fprintf(stderr, "V: tor_fw_fetch_public_ip\n"); for (i=0; i<backends->n_backends; ++i) { if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", + fprintf(stderr, "V: running backend_state now: %i\n", i); + fprintf(stderr, "V: size of backend state: %u\n", (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", + fprintf(stderr, "V: backend state name: %s\n", (char *)(backends->backend_ops)[i].name); } r = backends->backend_ops[i].fetch_public_ip(tor_fw_options, backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_fetch_public_ip backend %s " + fprintf(stderr, "tor-fw-helper: tor_fw_fetch_public_ip backend %s " " returned: %i\n", (char *)(backends->backend_ops)[i].name, r); } } -/** Iterate over each of the supported <b>backends</b> and attempt to add a - * port forward for the OR port stored in <b>tor_fw_options</b>. */ +/** Print a spec-conformant string to stdout describing the results of + * the TCP port forwarding operation from <b>external_port</b> to + * <b>internal_port</b>. */ static void -tor_fw_add_or_port(tor_fw_options_t *tor_fw_options, - backends_t *backends) +tor_fw_helper_report_port_fw_results(uint16_t internal_port, + uint16_t external_port, + int succeded, + const char *message) +{ + char *report_string = NULL; + + tor_asprintf(&report_string, "%s %s %u %u %s %s\n", + "tor-fw-helper", + "tcp-forward", + external_port, internal_port, + succeded ? "SUCCESS" : "FAIL", + message); + fprintf(stdout, "%s", report_string); + fflush(stdout); + tor_free(report_string); +} + +#define tor_fw_helper_report_port_fw_fail(i, e, m) \ + tor_fw_helper_report_port_fw_results((i), (e), 0, (m)) + +#define tor_fw_helper_report_port_fw_success(i, e, m) \ + tor_fw_helper_report_port_fw_results((i), (e), 1, (m)) + +/** Return a heap-allocated string containing the list of our + * backends. It can be used in log messages. Be sure to free it + * afterwards! */ +static char * +get_list_of_backends_string(backends_t *backends) { + char *backend_names = NULL; int i; - int r = 0; + smartlist_t *backend_names_sl = smartlist_new(); - if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_add_or_port\n"); + assert(backends->n_backends); - for (i=0; i<backends->n_backends; ++i) { - if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", - (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", - (const char *) backends->backend_ops[i].name); - } - r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options, - backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_add_or_port backend %s " - "returned: %i\n", (const char *) backends->backend_ops[i].name, r); - } + for (i=0; i<backends->n_backends; ++i) + smartlist_add(backend_names_sl, (char *) backends->backend_ops[i].name); + + backend_names = smartlist_join_strings(backend_names_sl, ", ", 0, NULL); + smartlist_free(backend_names_sl); + + return backend_names; } /** Iterate over each of the supported <b>backends</b> and attempt to add a - * port forward for the Dir port stored in <b>tor_fw_options</b>. */ + * port forward for the port stored in <b>tor_fw_options</b>. */ static void -tor_fw_add_dir_port(tor_fw_options_t *tor_fw_options, - backends_t *backends) +tor_fw_add_ports(tor_fw_options_t *tor_fw_options, + backends_t *backends) { int i; int r = 0; + int succeeded = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_add_dir_port\n"); + fprintf(stderr, "V: %s\n", __func__); - for (i=0; i<backends->n_backends; ++i) { - if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", - (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", - (char *)(backends->backend_ops)[i].name); + /** Loop all ports that need to be forwarded, and try to use our + * backends for each port. If a backend succeeds, break the loop, + * report success and get to the next port. If all backends fail, + * report failure for that port. */ + SMARTLIST_FOREACH_BEGIN(tor_fw_options->ports_to_forward, + port_to_forward_t *, port_to_forward) { + + succeeded = 0; + + for (i=0; i<backends->n_backends; ++i) { + if (tor_fw_options->verbose) { + fprintf(stderr, "V: running backend_state now: %i\n", i); + fprintf(stderr, "V: size of backend state: %u\n", + (int)(backends->backend_ops)[i].state_len); + fprintf(stderr, "V: backend state name: %s\n", + (const char *) backends->backend_ops[i].name); + } + + r = + backends->backend_ops[i].add_tcp_mapping(port_to_forward->internal_port, + port_to_forward->external_port, + tor_fw_options->verbose, + backends->backend_state[i]); + if (r == 0) { /* backend success */ + tor_fw_helper_report_port_fw_success(port_to_forward->internal_port, + port_to_forward->external_port, + backends->backend_ops[i].name); + succeeded = 1; + break; + } + + fprintf(stderr, "tor-fw-helper: tor_fw_add_port backend %s " + "returned: %i\n", + (const char *) backends->backend_ops[i].name, r); } - r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options, - backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_add_dir_port backend %s " - "returned: %i\n", (const char *)backends->backend_ops[i].name, r); - } + + if (!succeeded) { /* all backends failed */ + char *list_of_backends_str = get_list_of_backends_string(backends); + char *fail_msg = NULL; + tor_asprintf(&fail_msg, "All port forwarding backends (%s) failed.", + list_of_backends_str); + tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port, + port_to_forward->external_port, + fail_msg); + tor_free(list_of_backends_str); + tor_free(fail_msg); + } + + } SMARTLIST_FOREACH_END(port_to_forward); } /** Called before we make any calls to network-related functions. * (Some operating systems require their network libraries to be * initialized.) (from common/compat.c) */ static int -network_init(void) +tor_fw_helper_network_init(void) { #ifdef _WIN32 /* This silly exercise is necessary before windows will allow @@ -247,6 +306,67 @@ network_init(void) return 0; } +/** Parse the '-p' argument of tor-fw-helper. Its format is + * [<external port>]:<internal port>, and <external port> is optional. + * Return NULL if <b>arg</b> was c0rrupted. */ +static port_to_forward_t * +parse_port(const char *arg) +{ + smartlist_t *sl = smartlist_new(); + port_to_forward_t *port_to_forward = NULL; + char *port_str = NULL; + int ok; + int port; + + smartlist_split_string(sl, arg, ":", 0, 0); + if (smartlist_len(sl) != 2) + goto err; + + port_to_forward = tor_malloc(sizeof(port_to_forward_t)); + if (!port_to_forward) + goto err; + + port_str = smartlist_get(sl, 0); /* macroify ? */ + port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); + if (!ok && strlen(port_str)) /* ":1555" is valid */ + goto err; + port_to_forward->external_port = port; + + port_str = smartlist_get(sl, 1); + port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); + if (!ok) + goto err; + port_to_forward->internal_port = port; + + goto done; + + err: + tor_free(port_to_forward); + + done: + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); + + return port_to_forward; +} + +/** Report a failure of epic proportions: We didn't manage to + * initialize any port forwarding backends. */ +static void +report_full_fail(const smartlist_t *ports_to_forward) +{ + if (!ports_to_forward) + return; + + SMARTLIST_FOREACH_BEGIN(ports_to_forward, + const port_to_forward_t *, port_to_forward) { + tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port, + port_to_forward->external_port, + "All backends (NAT-PMP, UPnP) failed " + "to initialize!"); /* XXX hardcoded */ + } SMARTLIST_FOREACH_END(port_to_forward); +} + int main(int argc, char **argv) { @@ -259,22 +379,20 @@ main(int argc, char **argv) memset(&tor_fw_options, 0, sizeof(tor_fw_options)); memset(&backend_state, 0, sizeof(backend_state)); + // Parse CLI arguments. while (1) { int option_index = 0; static struct option long_options[] = { {"verbose", 0, 0, 'v'}, {"help", 0, 0, 'h'}, - {"internal-or-port", 1, 0, 'i'}, - {"external-or-port", 1, 0, 'e'}, - {"internal-dir-port", 1, 0, 'd'}, - {"external-dir-port", 1, 0, 'p'}, + {"port", 1, 0, 'p'}, {"fetch-public-ip", 0, 0, 'g'}, {"test-commandline", 0, 0, 'T'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "vhi:e:d:p:gT", + c = getopt_long(argc, argv, "vhp:gT", long_options, &option_index); if (c == -1) break; @@ -282,14 +400,31 @@ main(int argc, char **argv) switch (c) { case 'v': tor_fw_options.verbose = 1; break; case 'h': tor_fw_options.help = 1; usage(); exit(1); break; - case 'i': sscanf(optarg, "%hu", &tor_fw_options.private_or_port); - break; - case 'e': sscanf(optarg, "%hu", &tor_fw_options.public_or_port); - break; - case 'd': sscanf(optarg, "%hu", &tor_fw_options.private_dir_port); - break; - case 'p': sscanf(optarg, "%hu", &tor_fw_options.public_dir_port); + case 'p': { + port_to_forward_t *port_to_forward = parse_port(optarg); + if (!port_to_forward) { + fprintf(stderr, "E: Failed to parse '%s'.\n", optarg); + usage(); + exit(1); + } + + /* If no external port was given (it's optional), set it to be + * equal with the internal port. */ + if (!port_to_forward->external_port) { + assert(port_to_forward->internal_port); + if (tor_fw_options.verbose) + fprintf(stderr, "V: No external port was given. Setting to %u.\n", + port_to_forward->internal_port); + port_to_forward->external_port = port_to_forward->internal_port; + } + + if (!tor_fw_options.ports_to_forward) + tor_fw_options.ports_to_forward = smartlist_new(); + + smartlist_add(tor_fw_options.ports_to_forward, port_to_forward); + break; + } case 'g': tor_fw_options.fetch_public_ip = 1; break; case 'T': tor_fw_options.test_commandline = 1; break; case '?': break; @@ -297,98 +432,68 @@ main(int argc, char **argv) } } - if (tor_fw_options.verbose) { - fprintf(stderr, "V: tor-fw-helper version %s\n" - "V: We were called with the following arguments:\n" - "V: verbose = %d, help = %d, pub or port = %u, " - "priv or port = %u\n" - "V: pub dir port = %u, priv dir port = %u\n" - "V: fetch_public_ip = %u\n", - tor_fw_version, tor_fw_options.verbose, tor_fw_options.help, - tor_fw_options.private_or_port, tor_fw_options.public_or_port, - tor_fw_options.private_dir_port, tor_fw_options.public_dir_port, - tor_fw_options.fetch_public_ip); + { // Verbose output + + if (tor_fw_options.verbose) + fprintf(stderr, "V: tor-fw-helper version %s\n" + "V: We were called with the following arguments:\n" + "V: verbose = %d, help = %d, fetch_public_ip = %u\n", + tor_fw_version, tor_fw_options.verbose, tor_fw_options.help, + tor_fw_options.fetch_public_ip); + + if (tor_fw_options.verbose && tor_fw_options.ports_to_forward) { + fprintf(stderr, "V: TCP forwarding:\n"); + SMARTLIST_FOREACH(tor_fw_options.ports_to_forward, + const port_to_forward_t *, port_to_forward, + fprintf(stderr, "V: External: %u, Internal: %u\n", + port_to_forward->external_port, + port_to_forward->internal_port)); + } } if (tor_fw_options.test_commandline) { return log_commandline_options(argc, argv); } - /* At the very least, we require an ORPort; - Given a private ORPort, we can ask for a mapping that matches the port - externally. - */ - if (!tor_fw_options.private_or_port && !tor_fw_options.fetch_public_ip) { - fprintf(stderr, "E: We require an ORPort or fetch_public_ip" - " request!\n"); + // See if the user actually wants us to do something. + if (!tor_fw_options.fetch_public_ip && !tor_fw_options.ports_to_forward) { + fprintf(stderr, "E: We require a port to be forwarded or " + "fetch_public_ip request!\n"); usage(); exit(1); - } else { - /* When we only have one ORPort, internal/external are - set to be the same.*/ - if (!tor_fw_options.public_or_port && tor_fw_options.private_or_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We're setting public_or_port = " - "private_or_port.\n"); - tor_fw_options.public_or_port = tor_fw_options.private_or_port; - } - } - if (!tor_fw_options.private_dir_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We have no DirPort; no hole punching for " - "DirPorts\n"); - - } else { - /* When we only have one DirPort, internal/external are - set to be the same.*/ - if (!tor_fw_options.public_dir_port && tor_fw_options.private_dir_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We're setting public_or_port = " - "private_or_port.\n"); - - tor_fw_options.public_dir_port = tor_fw_options.private_dir_port; - } - } - - if (tor_fw_options.verbose) { - fprintf(stdout, "V: pub or port = %u, priv or port = %u\n" - "V: pub dir port = %u, priv dir port = %u\n", - tor_fw_options.private_or_port, tor_fw_options.public_or_port, - tor_fw_options.private_dir_port, - tor_fw_options.public_dir_port); } // Initialize networking - if (network_init()) + if (tor_fw_helper_network_init()) exit(1); // Initalize the various fw-helper backend helpers r = init_backends(&tor_fw_options, &backend_state); - if (r) - printf("tor-fw-helper: %i NAT traversal helper(s) loaded\n", r); - - if (tor_fw_options.fetch_public_ip) { - tor_fw_fetch_public_ip(&tor_fw_options, &backend_state); + if (!r) { // all backends failed: + // report our failure + report_full_fail(tor_fw_options.ports_to_forward); + fprintf(stderr, "tor-fw-helper: All backends failed.\n"); + exit(1); + } else { // some backends succeeded: + fprintf(stderr, "tor-fw-helper: %i NAT traversal helper(s) loaded\n", r); } - if (tor_fw_options.private_or_port) { - tor_fw_options.internal_port = tor_fw_options.private_or_port; - tor_fw_options.external_port = tor_fw_options.private_or_port; - tor_fw_add_or_port(&tor_fw_options, &backend_state); + // Forward TCP ports. + if (tor_fw_options.ports_to_forward) { + tor_fw_add_ports(&tor_fw_options, &backend_state); } - if (tor_fw_options.private_dir_port) { - tor_fw_options.internal_port = tor_fw_options.private_dir_port; - tor_fw_options.external_port = tor_fw_options.private_dir_port; - tor_fw_add_dir_port(&tor_fw_options, &backend_state); + // Fetch our public IP. + if (tor_fw_options.fetch_public_ip) { + tor_fw_fetch_public_ip(&tor_fw_options, &backend_state); } - r = (((tor_fw_options.nat_pmp_status | tor_fw_options.upnp_status) - |tor_fw_options.public_ip_status)); - if (r > 0) { - fprintf(stdout, "tor-fw-helper: SUCCESS\n"); - } else { - fprintf(stderr, "tor-fw-helper: FAILURE\n"); + // Cleanup and exit. + if (tor_fw_options.ports_to_forward) { + SMARTLIST_FOREACH(tor_fw_options.ports_to_forward, + port_to_forward_t *, port, + tor_free(port)); + smartlist_free(tor_fw_options.ports_to_forward); } exit(r); diff --git a/src/tools/tor-fw-helper/tor-fw-helper.h b/src/tools/tor-fw-helper/tor-fw-helper.h index 058afc4e09..08f94d0830 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper.h +++ b/src/tools/tor-fw-helper/tor-fw-helper.h @@ -17,24 +17,26 @@ #include <time.h> /** The current version of tor-fw-helper. */ -#define tor_fw_version "0.1" +#define tor_fw_version "0.2" /** This is an arbitrary hard limit - We currently have two (NAT-PMP and UPnP). We're likely going to add the Intel UPnP library but nothing else comes to mind at the moment. */ #define MAX_BACKENDS 23 +/** Forward traffic received in port <b>external_port</b> in the + * external side of our NAT to <b>internal_port</b> in this host. */ +typedef struct { + uint16_t external_port; + uint16_t internal_port; +} port_to_forward_t; + /** This is where we store parsed commandline options. */ typedef struct { int verbose; int help; int test_commandline; - uint16_t private_dir_port; - uint16_t private_or_port; - uint16_t public_dir_port; - uint16_t public_or_port; - uint16_t internal_port; - uint16_t external_port; + struct smartlist_t *ports_to_forward; int fetch_public_ip; int nat_pmp_status; int upnp_status; @@ -50,8 +52,8 @@ typedef struct tor_fw_backend_t { int (*init)(tor_fw_options_t *options, void *backend_state); int (*cleanup)(tor_fw_options_t *options, void *backend_state); int (*fetch_public_ip)(tor_fw_options_t *options, void *backend_state); - int (*add_tcp_mapping)(tor_fw_options_t *options, void *backend_state); + int (*add_tcp_mapping)(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); } tor_fw_backend_t; - #endif diff --git a/src/win32/Makefile.am b/src/win32/Makefile.am deleted file mode 100644 index 7f5d742481..0000000000 --- a/src/win32/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ - -EXTRA_DIST = orconfig.h - diff --git a/src/win32/include.am b/src/win32/include.am new file mode 100644 index 0000000000..dad59af3ae --- /dev/null +++ b/src/win32/include.am @@ -0,0 +1,3 @@ + +EXTRA_DIST+= src/win32/orconfig.h + diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index d780d5d73d..beebc6dbdd 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -232,7 +232,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.2.3.18-rc-dev" +#define VERSION "0.2.4.2-alpha-dev" diff --git a/tor.spec.in b/tor.spec.in deleted file mode 100644 index b452c9bb35..0000000000 --- a/tor.spec.in +++ /dev/null @@ -1,360 +0,0 @@ -## NOTE: tor.spec is autogenerated from tor.spec.in . Edit the latter, -## not the former. - -## Things that need to be edited frequently -# -# This should be incremented whenever the spec file changes, but -# can drop back to zero at a new Tor version - -%define specver 1 - -## Things users may want to change -# -# User (and group) name under which the Tor daemon runs. - -%define toruser @TORUSER@ -%define torgroup @TORGROUP@ - -## Version song and dance -# -# This should be the Tor version number, as it appears on the tarball, -# including any "pre<x>" or "rc<y>" suffix. This gets massaged to -# create the RPM version number, in a way that depends on the Tor -# numbering scheme. -%define native_version @VERSION@ - -%define version %(echo %{native_version} | sed -e 's/-/./g') - -## Define output filename -# -# This creates filenames based upon the value of target_cpu defined above - -## Release and OS identification song and dance -# -# This identifies the lineage of the spec file. This file is the -# standard one that comes with Tor; various distributions may -# have their own ideas about the right ways to do things. -%define pkgspec tor - -# This spec is intended to build and install on multiple distributions -# (someday). Detect the distribution we're building on. - -%define is_rh %(test -e /etc/redhat-release && echo 1 || echo 0) -%define is_fc %(test -e /etc/fedora-release && echo 1 || echo 0) -%define is_mdk %(test -e /etc/mandrake-release && echo 1 || echo 0) -%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) -%define is_rfl %(test -e /etc/redflag-release && echo 1 || echo 0) - -%if %{is_fc} -%define ostag %(sed -e 's/^.*release /fc/' -e 's/ .*$//' -e 's/\\./_/g' < /etc/fedora-release) -%endif - -%if %{is_rh} -%define ostag %(sed -e 's/^.*release /rh/' -e 's/ .*$//' -e 's/\\./_/g' < /etc/redhat-release) -%endif - -%if %{is_mdk} -%define ostag mdk -%endif - -%if %{is_suse} -%define ostag suse%(grep openSUSE /etc/SuSE-release | awk '{print $2}' | sed -e 's/\\./_/') -%endif - -%if %{is_rfl} -%define ostag %(sed -e 's/^.*Desktop /redflag/' -e 's/ .*$//' -e 's/\\./_/g' < /etc/redflag-release) -%endif - -# Using the build date ensures that every build really does get -# a different release number. We use this trick for CVS versions. -# For release versions, we don't want or need it. -%define is_dev_version %(echo %{native_version} | grep 'dev' > /dev/null && echo 1 || echo 0) - -%if %{is_dev_version} -%define blddate %(date -u +"%Y%m%d%H%M") -%define release %{pkgspec}.%{specver}.%{ostag}.%{blddate} -%else -%define release %{pkgspec}.%{specver}.%{ostag} -%endif - -## General-purpose macros -# -# Some systems don't have some macros. If a macro doesn't seem -# to exist on your system, add it here... - -%if %{!?__make:1}%{?__make:0} -%define __make make -%endif - -%if %{!?make:1}%{?make:0} -%define make %{__make} -%endif - -%if %{!?_localstatedir:1}%{?_localstatedir:0} -%define _localstatedir @LOCALSTATEDIR@ -%endif - -## Package information -# -Name: tor -Version: %{version} -Release: %{release} - -Summary: Anonymizing overlay network for TCP (The onion router) -URL: https://www.torproject.org/ -Group: System Environment/Daemons - -License: 3-clause BSD -Vendor: The Tor Project (https://torproject.org) -Packager: Erinn Clark <erinn@torproject.org> - -Requires: openssl >= 0.9.7, libevent >= 1.4.13 -BuildRequires: openssl-devel >= 0.9.7, libevent-devel >= 1.4.13, asciidoc - -# Fedora 16 and RHEL 5 have following conflicting packages according to rpm search -%if %{is_rh} -Conflicts: tor-core, tor-lsb, tor-upstart -%endif - -Requires(pre): /usr/bin/id, /bin/date, /bin/sh -Requires(pre): %{_sbindir}/useradd, %{_sbindir}/groupadd - -Source0: https://www.torproject.org/dist/%{name}-%{native_version}.tar.gz - -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root - -%description -Tor is a connection-based low-latency anonymous communication system. - -This package provides the "tor" program, which serves as both a client and -a relay node. Scripts will automatically create a "%{toruser}" user and -a "%{torgroup}" group, and set tor up to run as a daemon when the system -is rebooted. - -Applications connect to the local Tor proxy using the SOCKS -protocol. The tor client chooses a path through a set of relays, in -which each relay knows its predecessor and successor, but no -others. Traffic flowing down the circuit is unwrapped by a symmetric -key at each relay, which reveals the downstream relay. - -Warnings: Tor does no protocol cleaning. That means there is a danger -that application protocols and associated programs can be induced to -reveal information about the initiator. Tor depends on Privoxy or -similar protocol cleaners to solve this problem. This is alpha code, -and is even more likely than released code to have anonymity-spoiling -bugs. The present network is small -- this further reduces the -strength of the anonymity provided. Tor is not presently suitable -for high-stakes anonymity. - -%prep -%setup -q -n %{name}-%{native_version} - -%build -%if %{is_suse} -%configure --with-tor-user=%{toruser} --with-tor-group=%{torgroup} --docdir=%{_docdir}/%{name} -%else -%configure --with-tor-user=%{toruser} --with-tor-group=%{torgroup} -%endif -%make - -%install -%makeinstall - -# Install init script and control script -%__mkdir_p ${RPM_BUILD_ROOT}%{_initrddir} -%if %{is_suse} -%__install -p -m 755 contrib/suse/tor.sh ${RPM_BUILD_ROOT}%{_initrddir}/%{name} -%else -%__install -p -m 755 contrib/tor.sh ${RPM_BUILD_ROOT}%{_initrddir}/%{name} -%endif -%__install -p -m 755 contrib/torctl ${RPM_BUILD_ROOT}%{_bindir} - -# Set up config file; "sample" file implements a basic user node. -%__install -p -m 644 ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/torrc.sample ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/torrc - -# Install the logrotate control file. -%__mkdir_p -m 755 ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d -%__install -p -m 644 contrib/tor.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name} - -# Directories that don't have any preinstalled files -%__mkdir_p -m 700 ${RPM_BUILD_ROOT}%{_localstatedir}/lib/%{name} -%__mkdir_p -m 755 ${RPM_BUILD_ROOT}%{_localstatedir}/run/%{name} -%__mkdir_p -m 755 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name} -%__mkdir_p -m 700 ${RPM_BUILD_ROOT}%{_localstatedir}/tmp/%{name} - -%clean -[ "${RPM_BUILD_ROOT}" != "/" ] && rm -rf ${RPM_BUILD_ROOT} - -# These scripts are probably wrong for Mandrake or SuSE. They're certainly -# wrong for Debian, but what are you doing using RPM on Debian? - -%pre - -# If tor is already installed and running (whether installed by RPM -# or not), then kill it, but remember that it was running. -%__rm -f /%{_localstatedir}/tmp/${name}-was-running-%{version}-%{release} -if [ -f %{_initrddir}/%{name} ] && /sbin/service %{name} status ; then - /sbin/service %{name} stop - touch /%{_localstatedir}/tmp/${name}-was-running-%{version}-%{release} -fi - -# -# Create a user and group if need be -# -if [ ! -n "`/usr/bin/id -g %{torgroup} 2>/dev/null`" ]; then - # One would like to default the GID, but doing that properly would - # require thought. - %{_sbindir}/groupadd %{torgroup} 2> /dev/null -fi -if [ ! -n "`/usr/bin/id -u %{toruser} 2>/dev/null`" ]; then - # One would also like to default the UID, but doing that properly would - # also require thought. - if [ -x %{_sbindir}/nologin ]; then - %{_sbindir}/useradd -r -g %{torgroup} -d %{_localstatedir}/lib/%{name} -s %{_sbindir}/nologin %{toruser} 2> /dev/null - else - %{_sbindir}/useradd -r -g %{torgroup} -d %{_localstatedir}/lib/%{name} -s /bin/false %{toruser} 2> /dev/null - fi -fi -exit 0 - -%post - -# If this is a new installation, use chkconfig to put tor in the -# default set of runlevels. If it's an upgrade, leave the existing -# configuration alone. -if [ $1 -eq 1 ]; then - /sbin/chkconfig --add %{name} - /sbin/chkconfig %{name} on -fi - -# Older tor RPMS used a different username for the tor daemon. -# Make sure the runtime data have the right ownership. -%__chown -R %{toruser}.%{torgroup} %{_localstatedir}/{lib,log,run}/%{name} - -if [ -f /%{_localstatedir}/tmp/${name}-was-running-%{version}-%{release} ]; then - /sbin/service %{name} start - %__rm -f /%{_localstatedir}/tmp/${name}-was-running-%{version}-%{release} -fi -exit 0 - -%preun - -# If no instances of tor will be installed when we're done, make -# sure that it gets killed. We *don't* want to kill it or delete -# any of its data on uninstall if it's being upgraded to a new -# version, because the new version will actually already have -# been installed and started before the uninstall script for -# the old version is run, and we'd end up hosing it. -if [ $1 -le 0 ]; then - if [ -f %{_initrddir}/%{name} ] && /sbin/service %{name} status ; then - /sbin/service %{name} stop - fi - %/sbin/chkconfig --del %{name} - %__rm -f ${_localstatedir}/lib/%{name}/cached-directory - %__rm -f ${_localstatedir}/lib/%{name}/bw_accounting - %__rm -f ${_localstatedir}/lib/%{name}/control_auth_cookie - %__rm -f ${_localstatedir}/lib/%{name}/router.desc - %__rm -f ${_localstatedir}/lib/%{name}/fingerprint -fi -exit 0 - -%files -%defattr(-,root,root) -%if %{is_suse} -%doc INSTALL LICENSE README ChangeLog doc/HACKING doc/TODO doc/*html -%endif -%doc INSTALL LICENSE README ChangeLog doc/HACKING doc/TODO -%{_mandir}/man*/* -%{_bindir}/tor -%{_bindir}/torctl -%{_bindir}/torify -%{_bindir}/tor-resolve -%{_bindir}/tor-gencert -%if %{is_suse} -%else -%{_docdir}/* -%endif -%{_datadir}/tor/geoip -%config %{_initrddir}/%{name} -%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name} -%dir %attr(0755,root,%{torgroup}) %{_sysconfdir}/%{name}/ -%config(noreplace) %attr(0644,root,%{torgroup}) %{_sysconfdir}/%{name}/* -%attr(0700,%{toruser},%{torgroup}) %dir %{_localstatedir}/lib/%{name} -%attr(0750,%{toruser},%{torgroup}) %dir %{_localstatedir}/run/%{name} -%attr(0750,%{toruser},%{torgroup}) %dir %{_localstatedir}/log/%{name} - -%changelog -* Thu Jun 21 2012 Ondrej Mikle <ondrej.mikle@gmail.com> -- fixed to work with both rpmbuild and mock on RHEL/Fedora -- removed unnecessary files from rpm such as .git repo -- fixed build dependencies and package conflicts -- fixed creating _tor user on Fedora 17 (ancient typo) -- added/updated build instructions for RPM creation -- confirmed to build and run on EL5, EL6, Fedora 16/17, OpenSuse 12.1 - -* Fri Aug 20 2010 Erinn Clark <erinn@torproject.org> -- add conflicts for Fedora packages -- add logic for SuSE since it requires special doc handling - -* Mon Feb 22 2010 Erinn Clark <erinn@torproject.org> -- remove AUTHORS from %doc line since it no longer exists upstream -- switch maintainers - -* Fri May 01 2009 Andrew Lewman <andrew@torproject.org> -- clean up distro detection and remove dead comment blocks - -* Sun Feb 22 2009 Andrew Lewman <andrew@torproject.org> -- update the description, vendor, and packager - -* Thu Sep 11 2008 Andrew Lewman <phobos@rootme.org> -- See r16867 -- http://archives.seul.org/or/cvs/Sep-2008/msg00156.html - -* Tue Feb 27 2007 Andrew Lewman <phobos@rootme.org> -- Fix a potential race condition in how we determine the running state of tor. Found by Stefan Nordhausen. -- see OR-CVS for details - -* Fri May 26 2006 Andrew Lewman <phobos@rootme.org> -- Add in a few "SUSEisms" to make dist-rpm actually work on suse -- Turn Tor "on" via chkconfig -- Update -mcpu to -mtune to make GCC happy -- see OR-CVS for details - -* Tue Mar 28 2006 Andrew Lewman <phobos@rootme.org> -- converted to build the specified target cpu and arch -- override related rpm macros to build correctly -- see OR-CVS for details - -* Mon Jan 17 2005 John Bashinski <jbash@velvet.com> -- Take runtime user and group names from configure system. Default - user/group names are now "_tor"; blame Roger... -- Make logrotate control file a separate file in the source distribution, - rather than creating it from the spec file. -- Properly handle the order in which RPM executes scriptlets on upgrade. - The old code would kill the daemon on upgrade. -- Start the tor daemon after installation if and only if it was - running before installation. Preserve runlevel setup on upgrade. -- Package the torctl script; the init script is now a wrapper around it. - -* Tue Nov 5 2004 John Bashinski <jbash@velvet.com> -- Add skeletal support for multiple distributions -- Even more ridiculous level of macro-ization -- Modify version numbers so RPM can determine when it has a newer version -- Return to including distribution name in package release number -- Sharply trim description -- Change user/group name from "tor" to "tordmn"; "tor" is a common - given name (reported by Marius Hjelle) -- Change group to "System Environment/Daemons" (suggested by Marius Hjelle) -- Create logrotate file (suggested by Marius Hjelle) -- Make Tor run as a user proxy by default (suggested by Marius Hjelle) -- Autogenerate spec file from GNU autotools data, substituting version - and whatnot -- Be perhaps excessively paranoid with config file and directory modes -- Remove auto-start and auto-stop at installation time; there's some kind - of weird race going on, and it's arguably a bad thing anyway. - -* Mon Jun 06 2004 Nick Mathewson <nickm@freehaven.net> 0.0.7-0.std.0.1.rc2 -- Make spec file more happy with fc2 packaging - -* Sat Jan 17 2004 John Bashinski <jbash@velvet.com> -- Basic spec file; tested with Red Hat 9. |