From 383a3b8b02aedf403e66b5243aac3a2efa741039 Mon Sep 17 00:00:00 2001 From: Sami Ahmed Date: Thu, 19 Mar 2026 01:51:15 -0700 Subject: [PATCH] Modernize codebase: C++17, remove Tor v2, add embedded Tor scaffold, IBD speedups - Replace ~290 BOOST_FOREACH with C++11 range-for across 41+ files - Replace boost::assign::map_list_of with C++11 brace initialization - Remove PAIRTYPE macro (no longer needed without BOOST_FOREACH) - Guard OpenSSL locking callbacks for 3.x (no-ops in >= 1.1.0) - Fix Qt deprecated APIs for Qt6 compat (QStyleOptionViewItemV4, setResizeMode) - Add openssl_compat.h version string wrapper - Enable C++17 in makefile.unix and triangles-qt.pro - Delete 163 dead Tor v2 source files (~150K lines removed) - Add embedded Tor scaffold (tor_embedded.h/cpp) using tor_api.h - Add build-libtor.sh helper and CODEX-TOR-GUIDE.md - Update makefile.unix and .pro with USE_TOR_EMBEDDED optional flag - Fallback to external tor_process when not compiled with libtor - IBD pipeline refill: 100 -> 1000 blocks - Send/recv buffer limits: unlimited -> 100MB/32MB - Orphan block cap: unlimited -> 750 with random eviction - Socket poll: 10ms -> 1ms during IBD - Message handler sleep: 10ms -> 1ms during IBD - Stall detection timeout: 5s -> 2s Co-Authored-By: Claude Opus 4.6 --- CODEX-TOR-GUIDE.md | 225 ++ doc/embedded-tor-rebase.md | 59 + src/alert.cpp | 7 +- src/checkpoints.cpp | 77 +- src/init.cpp | 28 +- src/init.h | 2 +- src/kernel.cpp | 13 +- src/keystore.cpp | 2 +- src/main.cpp | 140 +- src/main.h | 17 +- src/makefile.unix | 39 +- src/miner.cpp | 6 +- src/net.cpp | 63 +- src/net.h | 11 +- src/openssl_compat.h | 16 + src/qt/aboutdialog.cpp | 2 - src/qt/addresstablemodel.cpp | 2 +- src/qt/coincontroldialog.cpp | 6 +- src/qt/messagepage.cpp | 6 +- src/qt/rpcconsole.cpp | 3 +- src/qt/signmessagepage.cpp | 2 - src/qt/transactiondesc.cpp | 20 +- src/qt/transactionrecord.cpp | 4 +- src/qt/triangles.cpp | 1 + src/qt/trianglesgui.cpp | 122 +- src/qt/trianglesgui.h | 7 + src/qt/verifymessagepage.cpp | 2 - src/qt/walletmodel.cpp | 8 +- src/rest.cpp | 2 +- src/rpcblockchain.cpp | 4 +- src/rpcdump.cpp | 2 +- src/rpcmining.cpp | 10 +- src/rpcnet.cpp | 6 +- src/rpcrawtransaction.cpp | 40 +- src/rpcsmessage.cpp | 2 +- src/rpcwallet.cpp | 42 +- src/script.cpp | 17 +- src/script.h | 3 +- src/smessage.cpp | 12 +- src/sync.cpp | 7 +- src/test/Checkpoints_tests.cpp | 2 - src/test/DoS_tests.cpp | 19 +- src/test/accounting_tests.cpp | 3 +- src/test/base58_tests.cpp | 10 +- src/test/getarg_tests.cpp | 3 +- src/test/multisig_tests.cpp | 17 +- src/test/rpc_tests.cpp | 1 - src/test/script_P2SH_tests.cpp | 4 - src/test/script_tests.cpp | 9 +- src/test/sigopcount_tests.cpp | 1 - src/test/transaction_tests.cpp | 8 +- src/test/util_tests.cpp | 1 - src/test/wallet_tests.cpp | 2 +- src/tor/address.c | 1692 -------- src/tor/address.h | 232 -- src/tor/addressmap.c | 1078 ----- src/tor/addressmap.h | 62 - src/tor/aes.c | 523 --- src/tor/aes.h | 29 - src/tor/anonymize.P | 1072 ----- src/tor/backtrace.c | 205 - src/tor/backtrace.h | 12 - src/tor/buffers.c | 2584 ------------ src/tor/buffers.h | 106 - src/tor/build-libtor.sh | 40 + src/tor/channel.c | 4191 -------------------- src/tor/channel.h | 489 --- src/tor/channeltls.c | 2074 ---------- src/tor/channeltls.h | 57 - src/tor/ciphers.inc | 182 - src/tor/circpathbias.c | 1538 -------- src/tor/circpathbias.h | 29 - src/tor/circuitbuild.c | 2200 ----------- src/tor/circuitbuild.h | 62 - src/tor/circuitlist.c | 1878 --------- src/tor/circuitlist.h | 82 - src/tor/circuitmux.c | 1898 --------- src/tor/circuitmux.h | 147 - src/tor/circuitmux_ewma.c | 684 ---- src/tor/circuitmux_ewma.h | 29 - src/tor/circuitstats.c | 1644 -------- src/tor/circuitstats.h | 98 - src/tor/circuituse.c | 2326 ----------- src/tor/circuituse.h | 61 - src/tor/command.c | 589 --- src/tor/command.h | 31 - src/tor/common_sha1.i | 26 - src/tor/compat.c | 3254 ---------------- src/tor/compat_libevent.c | 663 ---- src/tor/compat_libevent.h | 96 - src/tor/config.c | 6704 -------------------------------- src/tor/config.h | 144 - src/tor/config_codedigest.c | 13 - src/tor/confparse.c | 1316 ------- src/tor/confparse.h | 137 - src/tor/connection.c | 4850 ----------------------- src/tor/connection.h | 231 -- src/tor/connection_edge.c | 3119 --------------- src/tor/connection_edge.h | 143 - src/tor/connection_or.c | 2452 ------------ src/tor/connection_or.h | 100 - src/tor/container.c | 1557 -------- src/tor/container.h | 681 ---- src/tor/control.c | 5110 ------------------------ src/tor/control.h | 205 - src/tor/cpuworker.c | 718 ---- src/tor/cpuworker.h | 31 - src/tor/crypto.c | 3176 --------------- src/tor/crypto.h | 302 -- src/tor/crypto_curve25519.c | 191 - src/tor/crypto_curve25519.h | 74 - src/tor/crypto_format.c | 45 - src/tor/curve25519-donna.c | 732 ---- src/tor/di_ops.c | 222 -- src/tor/di_ops.h | 47 - src/tor/directory.c | 3894 ------------------- src/tor/directory.h | 127 - src/tor/dirserv.c | 3780 ------------------ src/tor/dirserv.h | 138 - src/tor/dirvote.c | 3778 ------------------ src/tor/dirvote.h | 148 - src/tor/dns.c | 2203 ----------- src/tor/dns.h | 32 - src/tor/dnsserv.c | 374 -- src/tor/dnsserv.h | 27 - src/tor/entrynodes.c | 2334 ----------- src/tor/entrynodes.h | 133 - src/tor/eventdns.h | 337 -- src/tor/eventdns_tor.h | 25 - src/tor/ext_orport.c | 648 --- src/tor/ext_orport.h | 42 - src/tor/fp_pair.c | 308 -- src/tor/fp_pair.h | 45 - src/tor/geoip.c | 1684 -------- src/tor/geoip.h | 69 - src/tor/hibernate.c | 1031 ----- src/tor/hibernate.h | 55 - src/tor/ht.h | 490 --- src/tor/log.c | 1316 ------- src/tor/memarea.c | 328 -- src/tor/memarea.h | 25 - src/tor/mempool.c | 628 --- src/tor/mempool.h | 65 - src/tor/microdesc.c | 833 ---- src/tor/microdesc.h | 56 - src/tor/networkstatus.c | 1871 --------- src/tor/networkstatus.h | 104 - src/tor/nodelist.c | 1557 -------- src/tor/nodelist.h | 86 - src/tor/ntmain.h | 30 - src/tor/onion.c | 1183 ------ src/tor/onion.h | 122 - src/tor/onion_fast.c | 123 - src/tor/onion_fast.h | 38 - src/tor/onion_main.c | 2908 -------------- src/tor/onion_main.h | 86 - src/tor/onion_ntor.c | 295 -- src/tor/onion_ntor.h | 63 - src/tor/onion_tap.c | 218 -- src/tor/onion_tap.h | 37 - src/tor/onion_v3.cpp | 16 +- src/tor/onion_v3.h | 1 - src/tor/or.h | 5044 ------------------------ src/tor/or_sha1.i | 167 - src/tor/orconfig.h | 12 - src/tor/orconfig_apple.h | 623 --- src/tor/orconfig_linux.h | 621 --- src/tor/orconfig_win32.h | 267 -- src/tor/policies.c | 1758 --------- src/tor/policies.h | 77 - src/tor/procmon.c | 356 -- src/tor/procmon.h | 33 - src/tor/reasons.c | 439 --- src/tor/reasons.h | 31 - src/tor/relay.c | 2870 -------------- src/tor/relay.h | 91 - src/tor/rendclient.c | 1362 ------- src/tor/rendclient.h | 57 - src/tor/rendcommon.c | 1487 ------- src/tor/rendcommon.h | 66 - src/tor/rendmid.c | 340 -- src/tor/rendmid.h | 25 - src/tor/rendservice.c | 3396 ---------------- src/tor/rendservice.h | 108 - src/tor/rephist.c | 3057 --------------- src/tor/rephist.h | 106 - src/tor/replaycache.c | 204 - src/tor/replaycache.h | 66 - src/tor/router.c | 3060 --------------- src/tor/router.h | 154 - src/tor/routerlist.c | 5145 ------------------------ src/tor/routerlist.h | 217 -- src/tor/routerparse.c | 4925 ----------------------- src/tor/routerparse.h | 86 - src/tor/routerset.c | 466 --- src/tor/routerset.h | 49 - src/tor/sandbox.c | 1510 ------- src/tor/sandbox.h | 247 -- src/tor/statefile.c | 627 --- src/tor/statefile.h | 26 - src/tor/status.c | 166 - src/tor/status.h | 10 - src/tor/strlcat.c | 70 - src/tor/strlcpy.c | 60 - src/tor/testsupport.h | 80 - src/tor/tinytest.h | 87 - src/tor/tinytest_macros.h | 184 - src/tor/tor_compat.h | 751 ---- src/tor/tor_embedded.cpp | 261 ++ src/tor/tor_embedded.h | 54 + src/tor/tor_process.cpp | 4 +- src/tor/tor_process.h | 2 +- src/tor/tor_queue.h | 568 --- src/tor/tor_util.c | 5086 ------------------------ src/tor/tor_util.h | 550 --- src/tor/torgzip.c | 527 --- src/tor/torgzip.h | 59 - src/tor/torint.h | 365 -- src/tor/torlog.h | 233 -- src/tor/tortls.c | 2822 -------------- src/tor/tortls.h | 142 - src/tor/transports.c | 1625 -------- src/tor/transports.h | 129 - src/tor/util_codedigest.c | 13 - src/tor_embed_hooks.cpp | 44 + src/tor_embed_hooks.h | 18 + src/trianglesrpc.cpp | 11 +- src/trianglesrpc.h | 4 +- src/txdb-leveldb.cpp | 12 +- src/util.cpp | 22 +- src/util.h | 3 - src/wallet.cpp | 104 +- src/wallet.h | 16 +- src/walletdb.cpp | 10 +- triangles-qt.pro | 51 +- 235 files changed, 1325 insertions(+), 151064 deletions(-) create mode 100644 CODEX-TOR-GUIDE.md create mode 100644 doc/embedded-tor-rebase.md create mode 100644 src/openssl_compat.h delete mode 100644 src/tor/address.c delete mode 100644 src/tor/address.h delete mode 100644 src/tor/addressmap.c delete mode 100644 src/tor/addressmap.h delete mode 100644 src/tor/aes.c delete mode 100644 src/tor/aes.h delete mode 100644 src/tor/anonymize.P delete mode 100644 src/tor/backtrace.c delete mode 100644 src/tor/backtrace.h delete mode 100644 src/tor/buffers.c delete mode 100644 src/tor/buffers.h create mode 100644 src/tor/build-libtor.sh delete mode 100644 src/tor/channel.c delete mode 100644 src/tor/channel.h delete mode 100644 src/tor/channeltls.c delete mode 100644 src/tor/channeltls.h delete mode 100644 src/tor/ciphers.inc delete mode 100644 src/tor/circpathbias.c delete mode 100644 src/tor/circpathbias.h delete mode 100644 src/tor/circuitbuild.c delete mode 100644 src/tor/circuitbuild.h delete mode 100644 src/tor/circuitlist.c delete mode 100644 src/tor/circuitlist.h delete mode 100644 src/tor/circuitmux.c delete mode 100644 src/tor/circuitmux.h delete mode 100644 src/tor/circuitmux_ewma.c delete mode 100644 src/tor/circuitmux_ewma.h delete mode 100644 src/tor/circuitstats.c delete mode 100644 src/tor/circuitstats.h delete mode 100644 src/tor/circuituse.c delete mode 100644 src/tor/circuituse.h delete mode 100644 src/tor/command.c delete mode 100644 src/tor/command.h delete mode 100644 src/tor/common_sha1.i delete mode 100644 src/tor/compat.c delete mode 100644 src/tor/compat_libevent.c delete mode 100644 src/tor/compat_libevent.h delete mode 100644 src/tor/config.c delete mode 100644 src/tor/config.h delete mode 100644 src/tor/config_codedigest.c delete mode 100644 src/tor/confparse.c delete mode 100644 src/tor/confparse.h delete mode 100644 src/tor/connection.c delete mode 100644 src/tor/connection.h delete mode 100644 src/tor/connection_edge.c delete mode 100644 src/tor/connection_edge.h delete mode 100644 src/tor/connection_or.c delete mode 100644 src/tor/connection_or.h delete mode 100644 src/tor/container.c delete mode 100644 src/tor/container.h delete mode 100644 src/tor/control.c delete mode 100644 src/tor/control.h delete mode 100644 src/tor/cpuworker.c delete mode 100644 src/tor/cpuworker.h delete mode 100644 src/tor/crypto.c delete mode 100644 src/tor/crypto.h delete mode 100644 src/tor/crypto_curve25519.c delete mode 100644 src/tor/crypto_curve25519.h delete mode 100644 src/tor/crypto_format.c delete mode 100644 src/tor/curve25519-donna.c delete mode 100644 src/tor/di_ops.c delete mode 100644 src/tor/di_ops.h delete mode 100644 src/tor/directory.c delete mode 100644 src/tor/directory.h delete mode 100644 src/tor/dirserv.c delete mode 100644 src/tor/dirserv.h delete mode 100644 src/tor/dirvote.c delete mode 100644 src/tor/dirvote.h delete mode 100644 src/tor/dns.c delete mode 100644 src/tor/dns.h delete mode 100644 src/tor/dnsserv.c delete mode 100644 src/tor/dnsserv.h delete mode 100644 src/tor/entrynodes.c delete mode 100644 src/tor/entrynodes.h delete mode 100644 src/tor/eventdns.h delete mode 100644 src/tor/eventdns_tor.h delete mode 100644 src/tor/ext_orport.c delete mode 100644 src/tor/ext_orport.h delete mode 100644 src/tor/fp_pair.c delete mode 100644 src/tor/fp_pair.h delete mode 100644 src/tor/geoip.c delete mode 100644 src/tor/geoip.h delete mode 100644 src/tor/hibernate.c delete mode 100644 src/tor/hibernate.h delete mode 100644 src/tor/ht.h delete mode 100644 src/tor/log.c delete mode 100644 src/tor/memarea.c delete mode 100644 src/tor/memarea.h delete mode 100644 src/tor/mempool.c delete mode 100644 src/tor/mempool.h delete mode 100644 src/tor/microdesc.c delete mode 100644 src/tor/microdesc.h delete mode 100644 src/tor/networkstatus.c delete mode 100644 src/tor/networkstatus.h delete mode 100644 src/tor/nodelist.c delete mode 100644 src/tor/nodelist.h delete mode 100644 src/tor/ntmain.h delete mode 100644 src/tor/onion.c delete mode 100644 src/tor/onion.h delete mode 100644 src/tor/onion_fast.c delete mode 100644 src/tor/onion_fast.h delete mode 100644 src/tor/onion_main.c delete mode 100644 src/tor/onion_main.h delete mode 100644 src/tor/onion_ntor.c delete mode 100644 src/tor/onion_ntor.h delete mode 100644 src/tor/onion_tap.c delete mode 100644 src/tor/onion_tap.h delete mode 100644 src/tor/or.h delete mode 100644 src/tor/or_sha1.i delete mode 100644 src/tor/orconfig.h delete mode 100644 src/tor/orconfig_apple.h delete mode 100644 src/tor/orconfig_linux.h delete mode 100644 src/tor/orconfig_win32.h delete mode 100644 src/tor/policies.c delete mode 100644 src/tor/policies.h delete mode 100644 src/tor/procmon.c delete mode 100644 src/tor/procmon.h delete mode 100644 src/tor/reasons.c delete mode 100644 src/tor/reasons.h delete mode 100644 src/tor/relay.c delete mode 100644 src/tor/relay.h delete mode 100644 src/tor/rendclient.c delete mode 100644 src/tor/rendclient.h delete mode 100644 src/tor/rendcommon.c delete mode 100644 src/tor/rendcommon.h delete mode 100644 src/tor/rendmid.c delete mode 100644 src/tor/rendmid.h delete mode 100644 src/tor/rendservice.c delete mode 100644 src/tor/rendservice.h delete mode 100644 src/tor/rephist.c delete mode 100644 src/tor/rephist.h delete mode 100644 src/tor/replaycache.c delete mode 100644 src/tor/replaycache.h delete mode 100644 src/tor/router.c delete mode 100644 src/tor/router.h delete mode 100644 src/tor/routerlist.c delete mode 100644 src/tor/routerlist.h delete mode 100644 src/tor/routerparse.c delete mode 100644 src/tor/routerparse.h delete mode 100644 src/tor/routerset.c delete mode 100644 src/tor/routerset.h delete mode 100644 src/tor/sandbox.c delete mode 100644 src/tor/sandbox.h delete mode 100644 src/tor/statefile.c delete mode 100644 src/tor/statefile.h delete mode 100644 src/tor/status.c delete mode 100644 src/tor/status.h delete mode 100644 src/tor/strlcat.c delete mode 100644 src/tor/strlcpy.c delete mode 100644 src/tor/testsupport.h delete mode 100644 src/tor/tinytest.h delete mode 100644 src/tor/tinytest_macros.h delete mode 100644 src/tor/tor_compat.h create mode 100644 src/tor/tor_embedded.cpp create mode 100644 src/tor/tor_embedded.h delete mode 100644 src/tor/tor_queue.h delete mode 100644 src/tor/tor_util.c delete mode 100644 src/tor/tor_util.h delete mode 100644 src/tor/torgzip.c delete mode 100644 src/tor/torgzip.h delete mode 100644 src/tor/torint.h delete mode 100644 src/tor/torlog.h delete mode 100644 src/tor/tortls.c delete mode 100644 src/tor/tortls.h delete mode 100644 src/tor/transports.c delete mode 100644 src/tor/transports.h delete mode 100644 src/tor/util_codedigest.c create mode 100644 src/tor_embed_hooks.cpp create mode 100644 src/tor_embed_hooks.h diff --git a/CODEX-TOR-GUIDE.md b/CODEX-TOR-GUIDE.md new file mode 100644 index 0000000..619bbf3 --- /dev/null +++ b/CODEX-TOR-GUIDE.md @@ -0,0 +1,225 @@ +# Embedded Tor Integration Guide for Triangles + +This guide explains how to compile Tor as a static library (`libtor.a`) and link +it directly into the Triangles wallet binary so that every node automatically +runs a Tor hidden service without needing an external Tor installation. + +## Architecture Overview + +``` +trianglesd / triangles-qt + ├── tor_embedded.cpp ← calls tor_run_main() in a background thread + ├── tor_process.cpp ← fallback: launches external tor binary (already works) + ├── onion_v3.cpp ← V3 onion address generation / SOCKS5 proxy logic + └── libtor.a ← static Tor library (built from official source) +``` + +When compiled with `ENABLE_TOR_EMBEDDED`, the wallet calls `tor_run_main()` from +`tor_api.h` on a dedicated thread. This gives the wallet a SOCKS5 proxy on +`127.0.0.1:19099` and a V3 hidden service on port 24112 (the P2P port). + +When compiled **without** the flag, `tor_embedded.cpp` falls back to the external +`tor_process.cpp` which searches for and launches a system `tor` binary. + +## Step 1: Add Tor as a Git Submodule + +```bash +cd /path/to/triangles +git submodule add https://gitlab.torproject.org/tpo/core/tor.git src/tor/tor-src +cd src/tor/tor-src +git checkout release-0.4.9 # latest stable branch as of 2026 +``` + +This puts the full Tor source at `src/tor/tor-src/`. +Current imported checkout in this repo: `release-0.4.9` at commit `1442ca4`. +There is also a helper build script at `src/tor/build-libtor.sh`. + +## Step 2: Build libtor.a (Linux) + +Tor uses autotools. Build it as a static library: + +```bash +cd src/tor/tor-src + +# Install Tor build dependencies +sudo apt install autoconf automake libtool pkg-config \ + libssl-dev libevent-dev zlib1g-dev + +# Generate configure script +./autogen.sh + +# Configure for static library build (disable unneeded modules) +./configure \ + --enable-static-tor \ + --disable-module-relay \ + --disable-module-dirauth \ + --disable-asciidoc \ + --disable-manpage \ + --disable-html-manual \ + --disable-unittests \ + --disable-tool-name-check \ + --with-openssl-dir=/usr \ + --with-libevent-dir=/usr \ + --with-zlib-dir=/usr \ + --prefix=/usr/local + +make -j$(nproc) +``` + +Or from the repo root: +```bash +./src/tor/build-libtor.sh +``` + +After building, the static libraries are in `src/tor/tor-src/src/`: +- `src/core/libtor-app.a` +- `src/lib/libtor-*.a` (multiple component libs) +- `src/trunnel/libor-trunnel.a` + +The header `src/feature/api/tor_api.h` provides the public C API: +```c +tor_main_configuration_t *tor_main_configuration_new(void); +int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]); +int tor_run_main(const tor_main_configuration_t *); +void tor_main_configuration_free(tor_main_configuration_t *); +``` + +## Step 3: Build Triangles with Embedded Tor + +### Linux (makefile.unix) + +```bash +cd src + +# Point to Tor's built libraries and headers +make -f makefile.unix \ + USE_TOR_EMBEDDED=1 +``` + +You may need to adjust the `-l` flags in the makefile depending on the exact +library names Tor produces. Check `src/tor/tor-src/src/` after building: + +```bash +find tor/tor-src/src -name '*.a' | sort +``` + +Common libraries to link (order matters): +``` +-ltor-app -lor -lor-ctime -lor-evloop -lor-event -lor-compress +-lor-container -lor-crypt-ops -lor-encoding -lor-err -lor-fs +-lor-intmath -lor-lock -lor-log -lor-malloc -lor-math -lor-memarea +-lor-meminfo -lor-net -lor-osinfo -lor-process -lor-sandbox +-lor-smartlist-core -lor-string -lor-term -lor-thread -lor-time +-lor-tls -lor-trace -lor-version -lor-wallclock +-lor-trunnel +``` + +### Windows (triangles-qt.pro) + +Add to `triangles-qt.pro`: +```qmake +qmake "USE_TOR_EMBEDDED=1" \ + "TOR_SOURCE_ROOT=src/tor/tor-src" +``` + +Both build systems now default to: +- source root: `src/tor/tor-src` +- include path: `src/tor/tor-src/src/feature/api` +- library paths: `src/tor/tor-src/src/core`, `src/tor/tor-src/src/lib`, `src/tor/tor-src/src/trunnel` + +Override `TOR_EMBEDDED_LIBS` if the actual Tor static library names differ on your platform/build. + +## Step 4: Wire into init.cpp + +The global hooks `StartEmbeddedTor()` and `StopEmbeddedTor()` need to be called +from `init.cpp`. Add these calls: + +### In AppInit2() (after network init, before starting node): +```cpp +#include "tor/tor_embedded.h" + +// Near the end of AppInit2, after network initialization: +if (!StartEmbeddedTor()) { + printf("WARNING: Embedded Tor failed to start. .onion connectivity unavailable.\n"); + // Non-fatal: wallet works without Tor, just no .onion +} +``` + +### In Shutdown(): +```cpp +StopEmbeddedTor(); +``` + +## Step 5: Configure SOCKS Proxy for Outbound Connections + +After Tor starts, the wallet needs to route `.onion` connections through the +SOCKS5 proxy. In `net.cpp`, after Tor is initialized: + +```cpp +// If embedded Tor is running, use its SOCKS proxy for .onion addresses +CTorEmbedded* tor = CTorEmbedded::GetInstance(); +if (tor->IsRunning()) { + // Set proxy for .onion connections + proxyType addrProxy(CService("127.0.0.1", tor->GetSocksPort()), 5); + SetNameProxy(addrProxy); +} +``` + +## Runtime Flags + +The embedded Tor respects these command-line flags: + +| Flag | Default | Description | +|------|---------|-------------| +| `-notor` | false | Disable Tor entirely | +| `-torsocks=PORT` | 19099 | SOCKS5 proxy port | +| `-torhsport=PORT` | 24112 | Hidden service virtual port | + +## File Layout After Integration + +``` +src/tor/ +├── tor-src/ ← git submodule (official Tor repo) +│ └── src/ +│ ├── core/libtor-app.a +│ ├── lib/libor-*.a +│ ├── trunnel/libor-trunnel.a +│ └── feature/api/tor_api.h +├── tor_embedded.h ← CTorEmbedded class header +├── tor_embedded.cpp ← implementation (calls tor_run_main) +├── tor_process.h ← external Tor process manager (fallback) +├── tor_process.cpp +├── onion_v3.h ← V3 onion address utilities +├── onion_v3.cpp +├── anonymize.h ← data dir helpers +├── anonymize.cpp +└── LICENSE +``` + +## Reference: How VERGE (XVG) Does It + +VERGE uses the same pattern. Their implementation is at: +- `src/torcontroller.cpp` (~100 lines) +- They use `tor_main()` (older API, pre-0.4.5) +- Git submodule at `src/tor/` pointing to `release-0.4.8` branch +- Build Tor as part of their `depends/` system + +Key difference: modern Tor (0.4.5+) uses `tor_run_main()` with a configuration +object instead of raw `tor_main(int argc, char** argv)`. + +## Troubleshooting + +**Tor fails to bootstrap**: Check firewall rules. Tor needs outbound TCP to the +Tor network (ports 80, 443, 9001, 9030). + +**Link errors with libtor**: The Tor static libraries must be linked in +dependency order. If you get undefined symbols, reorder the `-l` flags or use +`-Wl,--start-group ... -Wl,--end-group` to resolve circular deps: +``` +LIBS += -Wl,--start-group -ltor-app -lor -lor-ctime ... -Wl,--end-group +``` + +**OpenSSL version mismatch**: Both Tor and Triangles must link against the same +OpenSSL version (3.x). If Tor was built against a different OpenSSL, rebuild it +with the same `--with-openssl-dir`. diff --git a/doc/embedded-tor-rebase.md b/doc/embedded-tor-rebase.md new file mode 100644 index 0000000..6888708 --- /dev/null +++ b/doc/embedded-tor-rebase.md @@ -0,0 +1,59 @@ +# Embedded Tor Rebase Notes + +This repository currently contains a legacy Tor source snapshot under +`src/tor/`, but the wallet target does not build most of that tree. + +## Current state + +- The vendored Tor headers report `0.2.5.1-alpha-dev` in: + - `src/tor/orconfig_linux.h` + - `src/tor/orconfig_apple.h` + - `src/tor/orconfig_win32.h` +- The Qt wallet target currently builds only these Tor-related sources: + - `src/tor_embed_hooks.cpp` + - `src/tor/onion_v3.cpp` + - `src/tor/tor_process.cpp` +- This means the large legacy `src/tor/` tree is mostly dormant from the + wallet build's perspective. + +## Rebase target + +- Target upstream Tor line: `0.4.9.x` +- Imported source tree: `src/tor/tor-src` +- Imported branch: `release-0.4.9` +- Imported commit: `1442ca4` + +## Why this matters + +Attempting to "upgrade embedded Tor" by rebasing the entire old source tree in +place is unnecessarily expensive if the wallet is only relying on: + +- process management for a bundled Tor executable +- Tor v3 onion address/key handling +- a few local embedding hooks + +The migration should preserve the embedded product experience while reducing +coupling to legacy upstream Tor internals. + +## Strategy + +1. Keep the product-level embedding model. + - The wallet can still ship with Tor and launch it automatically. +2. Separate Triangles-owned glue from vendored Tor code. + - `src/tor_embed_hooks.*` now holds local process/bootstrap helpers that + previously lived under `src/tor/anonymize.*`. +3. Treat `src/tor/onion_v3.cpp` and `src/tor/tor_process.cpp` as the active + compatibility boundary. +4. Re-vendor a newer upstream Tor snapshot only after deciding whether the + product truly needs upstream Tor source in-tree or only a bundled Tor + runtime plus the wallet's own v3/onion management code. + +## Immediate next tasks + +1. Audit whether any live build target still includes legacy `src/tor/*.c` + sources beyond the current wallet target. +2. Decide whether `onion_v3.cpp` should remain wallet-owned code or be reduced + further in favor of runtime Tor control/provisioning. +3. Add build metadata recording the intended upstream Tor version and source. +4. If full upstream vendoring is still required, import a fresh `0.4.8.19` + tree side-by-side instead of trying to patch the legacy `0.2.5.1` tree. diff --git a/src/alert.cpp b/src/alert.cpp index 0b1d350..94143c7 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "alert.h" @@ -46,10 +45,10 @@ void CUnsignedAlert::SetNull() std::string CUnsignedAlert::ToString() const { std::string strSetCancel; - BOOST_FOREACH(int n, setCancel) + for (int n : setCancel) strSetCancel += strprintf("%d ", n); std::string strSetSubVer; - BOOST_FOREACH(std::string str, setSubVer) + for (std::string str : setSubVer) strSetSubVer += "\"" + str + "\" "; return strprintf( "CAlert(\n" @@ -228,7 +227,7 @@ bool CAlert::ProcessAlert(bool fThread) } // Check if this alert has been cancelled - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + for (auto& item : mapAlerts) { const CAlert& alert = item.second; if (alert.Cancels(*this)) diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 673c130..4bb98af 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -2,9 +2,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include // for 'map_list_of()' -#include - #include "checkpoints.h" #include "txdb.h" @@ -22,39 +19,37 @@ namespace Checkpoints // timestamp before) // + Contains no strange transactions // - static MapCheckpoints mapCheckpoints = - boost::assign::map_list_of - ( 0, hashGenesisBlockOfficial ) - ( 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")) - ( 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")) - ( 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")) - ( 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")) - ( 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")) - ( 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")) - ( 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")) - ( 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")) - ( 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")) - ( 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")) - ( 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")) - (2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")) - ; + static MapCheckpoints mapCheckpoints = { + { 0, hashGenesisBlockOfficial }, + { 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")}, + { 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")}, + { 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")}, + { 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")}, + { 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")}, + { 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")}, + { 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")}, + { 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")}, + { 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")}, + { 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")}, + { 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")}, + {2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")}, + }; - static MapCheckpoints mapCheckpointsTestnet = - boost::assign::map_list_of - ( 0, hashGenesisBlockTestNet ) - ( 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")) - ( 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")) - ( 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")) - ( 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")) - ( 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")) - ( 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")) - ( 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")) - ( 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")) - ( 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")) - ( 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")) - ( 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")) - (2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")) - ; + static MapCheckpoints mapCheckpointsTestnet = { + { 0, hashGenesisBlockTestNet }, + { 2000, uint256("0x0000000000b5f20078bf46ebdf1500813bb6b2cb482065aa93b89e073b2c6467")}, + { 2101, uint256("0xd4ea1ac45b63c8162a7fc8033cec441db8d532ba988202849d7831e32fe2d059")}, + { 2847, uint256("0xb5015e2835f13fd3bb6135cff9b31ac33310c9b77d694bdb592b8680d98d018e")}, + { 3589, uint256("0xb12a2ca3db4e288cada98aa2139768532bd4474c49dd7b8158031032dac08d51")}, + { 3935, uint256("0xe16290c9757d1368b8d7c35de07d4f8f70c2c9f9c785b667df0c3bff85086ca6")}, + { 5703, uint256("0x587db07bb2172ad7db72c5fabc2518262a1b27f503f99417510b2c6fafa6557b")}, + { 9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")}, + { 9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")}, + { 9002, uint256("0xa1e20fb1d44688b763690cf74d6aefe859e4cc32981f9e3f2b2ae9702bbcf249")}, + { 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")}, + { 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")}, + {2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")}, + }; bool CheckHardened(int nHeight, const uint256& hash) { @@ -76,9 +71,9 @@ namespace Checkpoints { MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); - BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) + for (auto it = checkpoints.rbegin(); it != checkpoints.rend(); ++it) { - const uint256& hash = i.second; + const uint256& hash = it->second; std::map::const_iterator t = mapBlockIndex.find(hash); if (t != mapBlockIndex.end()) return t->second; @@ -199,7 +194,7 @@ namespace Checkpoints // relay the checkpoint if (!checkpointMessage.IsNull()) { - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) checkpointMessage.RelayTo(pnode); } return true; @@ -264,9 +259,9 @@ namespace Checkpoints printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str()); } - BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints) + for (auto it = mapCheckpoints.rbegin(); it != mapCheckpoints.rend(); ++it) { - const uint256& hash = i.second; + const uint256& hash = it->second; if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain()) { if (!WriteSyncCheckpoint(hash)) @@ -331,7 +326,7 @@ namespace Checkpoints // Relay checkpoint { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) checkpoint.RelayTo(pnode); } return true; diff --git a/src/init.cpp b/src/init.cpp index c0e5670..cde43bd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -11,6 +11,8 @@ #include "ui_interface.h" #include "checkpoints.h" #include "smessage.h" +#include "openssl_compat.h" +#include "tor/tor_embedded.h" #include "tor/onion_v3.h" #include "tor/tor_process.h" #ifdef ENABLE_ZMQ @@ -156,7 +158,7 @@ void Shutdown(void* parg) SecureMsgShutdown(); ShutdownTorV3(); - StopTorProcess(); + StopEmbeddedTor(); #ifdef ENABLE_ZMQ if (pzmqNotifier) @@ -332,6 +334,9 @@ std::string HelpMessage() //" -proxy= " + _("Connect through socks proxy") + "\n" + //" -socks= " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + " -tor= " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n" + " -notor " + _("Disable Tor startup and .onion connectivity") + "\n" + + " -torsocks= " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" + + " -torhsport= " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" + //" -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + " -port= " + _("Listen for connections on (default: 24112 or testnet: 24111)") + "\n" + " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + @@ -651,7 +656,7 @@ bool AppInit2() ShrinkDebugFile(); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("Triangles version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); - printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); + printf("Using OpenSSL version %s\n", TrianglesOpenSSLVersionString()); if (!fLogTimestamps) printf("Startup time: %s\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); @@ -709,7 +714,7 @@ bool AppInit2() // Users can restrict to Tor-only with -onlynet=tor if (mapArgs.count("-onlynet")) { std::set nets; - BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) { + for (std::string snet : mapMultiArgs["-onlynet"]) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str())); @@ -761,11 +766,11 @@ bool AppInit2() // Release the old Tor initialization mutex (no longer blocking on embedded Tor) - set_initialized(); + triangles_tor_set_initialized(); if (mapArgs.count("-externalip")) { - BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { + for (string strAddr : mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str())); @@ -789,7 +794,7 @@ bool AppInit2() InitError(_("Unable to sign checkpoint, wrong checkpointkey?\n")); } - BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + for (string strDest : mapMultiArgs["-seednode"]) AddOneShot(strDest); // ********************************************************* Step 7: load blockchain @@ -971,13 +976,14 @@ bool AppInit2() uiInterface.InitMessage(_("Starting Tor...")); printf("Starting Tor process...\n"); - // Start the Tor process (finds/launches tor binary, provides SOCKS proxy) - std::string torDataPath = (GetDataDir() / "tor_data").string(); - bool torStarted = StartTorProcess(torDataPath); + bool torStarted = StartEmbeddedTor(); + std::string torDataPath = CTorEmbedded::GetInstance()->GetDataDir(); + if (torDataPath.empty()) + torDataPath = (GetDataDir() / "tor_data").string(); if (torStarted) { printf("Tor process running, SOCKS proxy at %s\n", - CTorProcess::GetInstance()->GetSocksProxy().c_str()); + CTorEmbedded::GetInstance()->GetSocksProxy().c_str()); } else { printf("WARNING: Tor not available. .onion peers will not be reachable.\n"); printf(" Clearnet connections will still work normally.\n"); @@ -1041,7 +1047,7 @@ bool AppInit2() { uiInterface.InitMessage(_("Importing blockchain data file.")); - BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + for (string strFile : mapMultiArgs["-loadblock"]) { FILE *file = fopen(strFile.c_str(), "rb"); if (file) diff --git a/src/init.h b/src/init.h index 10822e9..486966a 100644 --- a/src/init.h +++ b/src/init.h @@ -6,7 +6,7 @@ #define TRIANGLES_INIT_H #include "wallet.h" -#include +#include "tor_embed_hooks.h" extern CWallet* pwalletMain; extern std::string strWalletFileName; diff --git a/src/kernel.cpp b/src/kernel.cpp index 5b75119..55ef1e4 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -2,8 +2,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include - #include "kernel.h" #include "txdb.h" @@ -19,10 +17,9 @@ extern unsigned int nTargetSpacing; typedef std::map MapModifierCheckpoints; // Hard checkpoints of stake modifiers to ensure they are deterministic -static std::map mapStakeModifierCheckpoints = - boost::assign::map_list_of - ( 0, 0x000000000e00670b ) - ; +static std::map mapStakeModifierCheckpoints = { + { 0, 0x000000000e00670b }, +}; // Get time weight int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) @@ -80,7 +77,7 @@ static bool SelectBlockFromCandidates(vector >& vSortedBy bool fSelected = false; uint256 hashBest = 0; *pindexSelected = (const CBlockIndex*) 0; - BOOST_FOREACH(const PAIRTYPE(int64_t, uint256)& item, vSortedByTimestamp) + for (const auto& item : vSortedByTimestamp) { if (!mapBlockIndex.count(item.second)) return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str()); @@ -199,7 +196,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); pindex = pindex->pprev; } - BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks) + for (const auto& item : mapSelectedBlocks) { // 'S' indicates selected proof-of-stake blocks // 'W' indicates selected proof-of-work blocks diff --git a/src/keystore.cpp b/src/keystore.cpp index bffe860..41eb329 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -202,7 +202,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return false; fUseCrypto = true; - BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + for (KeyMap::value_type& mKey : mapKeys) { CKey key; if (!key.SetSecret(mKey.second.first, mKey.second.second)) diff --git a/src/main.cpp b/src/main.cpp index cd14eee..fb4b26e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -120,7 +120,7 @@ void UnregisterWallet(CWallet* pwalletIn) // check whether the passed transaction is from us bool static IsFromMe(CTransaction& tx) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) if (pwallet->IsFromMe(tx)) return true; return false; @@ -129,7 +129,7 @@ bool static IsFromMe(CTransaction& tx) // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) if (pwallet->GetTransaction(hashTx,wtx)) return true; return false; @@ -138,7 +138,7 @@ bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) // erases transaction with the given hash from all wallets void static EraseFromWallets(uint256 hash) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->EraseFromWallet(hash); } @@ -150,21 +150,21 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, // triangles: wallets need to refund inputs when disconnecting coinstake if (tx.IsCoinStake()) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) if (pwallet->IsFromMe(tx)) pwallet->DisableTransaction(tx); } return; } - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } // notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->SetBestChain(loc); } @@ -186,28 +186,28 @@ static bool UpdateAddressIndexSyncState(CTxDB& txdb, const CBlockIndex* pindexNe // notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } // dump all wallets void static PrintWallets(const CBlock& block) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->PrintWallet(block); } // notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->Inventory(hash); } // ask wallets to resend their transactions void ResendWalletTransactions(bool fForce) { - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + for (CWallet* pwallet : setpwalletRegistered) pwallet->ResendWalletTransactions(fForce); } @@ -245,7 +245,7 @@ bool AddOrphanTx(const CTransaction& tx) } mapOrphanTransactions[hash] = tx; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(), @@ -258,7 +258,7 @@ void static EraseOrphanTx(uint256 hash) if (!mapOrphanTransactions.count(hash)) return; const CTransaction& tx = mapOrphanTransactions[hash]; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) { mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) @@ -327,7 +327,7 @@ bool CTransaction::IsStandard() const if (nVersion > CTransaction::CURRENT_VERSION) return false; - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 @@ -340,7 +340,7 @@ bool CTransaction::IsStandard() const return false; } } - BOOST_FOREACH(const CTxOut& txout, vout) { + for (const CTxOut& txout : vout) { if (!::IsStandard(txout.scriptPubKey)) return false; if (txout.nValue == 0) @@ -421,11 +421,11 @@ unsigned int CTransaction::GetLegacySigOpCount() const { unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } - BOOST_FOREACH(const CTxOut& txout, vout) + for (const CTxOut& txout : vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } @@ -519,7 +519,7 @@ bool CTransaction::CheckTransaction() const // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) { if (vInOutPoints.count(txin.prevout)) return false; @@ -533,7 +533,7 @@ bool CTransaction::CheckTransaction() const } else { - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) if (txin.prevout.IsNull()) return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } @@ -552,7 +552,7 @@ int64_t CTransaction::GetMinFee(unsigned int nBlockSize, enum GetMinFee_mode mod // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 if (nMinFee < nBaseFee) { - BOOST_FOREACH(const CTxOut& txout, vout) + for (const CTxOut& txout : vout) if (txout.nValue < CENT) nMinFee = nBaseFee; } @@ -767,7 +767,7 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) remove(*it->second.ptx, true); } } - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); nTransactionsUpdated++; @@ -780,7 +780,7 @@ bool CTxMemPool::removeConflicts(const CTransaction &tx) { // Remove transactions which depend on inputs of tx, recursively LOCK(cs); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { + for (const CTxIn &txin : tx.vin) { std::map::iterator it = mapNextTx.find(txin.prevout); if (it != mapNextTx.end()) { const CTransaction &txConflict = *it->second.ptx; @@ -882,7 +882,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) { LOCK(mempool.cs); // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + for (CMerkleTx& tx : vtxPrev) { if (!(tx.IsCoinBase() || tx.IsCoinStake())) { @@ -1220,7 +1220,7 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) // Relinquish previous transactions' spent pointers if (!IsCoinBase()) { - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) { COutPoint prevout = txin.prevout; @@ -1627,7 +1627,7 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) } // triangles: clean up wallet after disconnecting coinstake - BOOST_FOREACH(CTransaction& tx, vtx) + for (CTransaction& tx : vtx) SyncWithWallets(tx, this, false, false); return true; @@ -1660,7 +1660,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) int64_t nValueOut = 0; int64_t nStakeReward = 0; unsigned int nSigOps = 0; - BOOST_FOREACH(CTransaction& tx, vtx) + for (CTransaction& tx : vtx) { uint256 hashTx = tx.GetHash(); @@ -1692,7 +1692,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // initial block download. CTxIndex txindexOld; if (txdb.ReadTxIndex(hashTx, txindexOld)) { - BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) + for (CDiskTxPos &pos : txindexOld.vSpent) if (pos.IsNull()) return false; } @@ -1852,7 +1852,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) // This eliminates millions of per-transaction wallet lookups during sync. if (!fIsInitialDownload) { - BOOST_FOREACH(CTransaction& tx, vtx) + for (CTransaction& tx : vtx) SyncWithWallets(tx, this, true); } @@ -1893,7 +1893,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) // Disconnect shorter branch vector vResurrect; - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + for (CBlockIndex* pindex : vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) @@ -1902,7 +1902,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); // Queue memory transactions to resurrect - BOOST_FOREACH(const CTransaction& tx, block.vtx) + for (const CTransaction& tx : block.vtx) if (!(tx.IsCoinBase() || tx.IsCoinStake())) vResurrect.push_back(tx); } @@ -1922,7 +1922,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) } // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, block.vtx) + for (const CTransaction& tx : block.vtx) vDelete.push_back(tx); } if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) @@ -1935,21 +1935,21 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) return error("Reorganize() : TxnCommit failed"); // Disconnect shorter branch - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + for (CBlockIndex* pindex : vDisconnect) if (pindex->pprev) pindex->pprev->pnext = NULL; // Connect longer branch - BOOST_FOREACH(CBlockIndex* pindex, vConnect) + for (CBlockIndex* pindex : vConnect) if (pindex->pprev) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) + for (CTransaction& tx : vResurrect) tx.AcceptToMemoryPool(txdb, false); // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) { + for (CTransaction& tx : vDelete) { mempool.remove(tx); mempool.removeConflicts(tx); } @@ -1979,7 +1979,7 @@ bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) pindexNew->pprev->pnext = pindexNew; // Delete redundant memory transactions - BOOST_FOREACH(CTransaction& tx, vtx) + for (CTransaction& tx : vtx) mempool.remove(tx); return true; @@ -2034,8 +2034,9 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } // Connect further blocks - BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) + for (auto rit = vpindexSecondary.rbegin(); rit != vpindexSecondary.rend(); ++rit) { + CBlockIndex *pindex = *rit; CBlock block; if (!block.ReadFromDisk(pindex)) { @@ -2176,7 +2177,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const if (IsCoinBase()) return true; - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) { // First try finding the previous transaction in database CTransaction txPrev; @@ -2213,7 +2214,7 @@ bool CBlock::GetCoinAge(uint64_t& nCoinAge) const nCoinAge = 0; CTxDB txdb("r"); - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) { uint64_t nTxCoinAge; if (tx.GetCoinAge(txdb, nTxCoinAge)) @@ -2369,7 +2370,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c } // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) { if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); @@ -2382,7 +2383,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set uniqueTx; - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) { uniqueTx.insert(tx.GetHash()); } @@ -2390,7 +2391,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c return DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) { nSigOps += tx.GetLegacySigOpCount(); } @@ -2447,7 +2448,7 @@ bool CBlock::AcceptBlock() return error("AcceptBlock() : block's timestamp is too early"); // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) return DoS(10, error("AcceptBlock() : contains a non-final transaction")); @@ -2497,7 +2498,7 @@ bool CBlock::AcceptBlock() if (hashBestChain == hash) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } @@ -2613,6 +2614,31 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) mapOrphanBlocks.insert(make_pair(hash, pblock2)); mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); + // Limit orphan blocks to prevent memory exhaustion + if (mapOrphanBlocks.size() > MAX_ORPHAN_BLOCKS) + { + // Evict a random orphan + uint256 randomhash = GetRandHash(); + auto it = mapOrphanBlocks.lower_bound(randomhash); + if (it == mapOrphanBlocks.end()) + it = mapOrphanBlocks.begin(); + CBlock* pblockEvict = it->second; + uint256 evictHash = it->first; + // Remove from by-prev index + for (auto range = mapOrphanBlocksByPrev.equal_range(pblockEvict->hashPrevBlock); + range.first != range.second; ++range.first) + { + if (range.first->second == pblockEvict) { + mapOrphanBlocksByPrev.erase(range.first); + break; + } + } + setStakeSeenOrphan.erase(pblockEvict->GetProofOfStake()); + delete pblockEvict; + mapOrphanBlocks.erase(evictHash); + printf("ProcessBlock: orphan eviction, %u orphans remain\n", (unsigned int)mapOrphanBlocks.size()); + } + // Ask this guy to fill in what we're missing if (pfrom) { @@ -3093,7 +3119,7 @@ string GetWarnings(string strFor) // Alerts { LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + for (auto& item : mapAlerts) { const CAlert& alert = item.second; if (alert.AppliesToMe() && alert.nPriority > nPriority) @@ -3283,7 +3309,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Relay alerts { LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) + for (auto& item : mapAlerts) item.second.RelayTo(pfrom); } @@ -3334,7 +3360,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; - BOOST_FOREACH(CAddress& addr, vAddr) + for (CAddress& addr : vAddr) { if (fShutdown) return true; @@ -3356,7 +3382,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap mapMix; - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; @@ -3450,7 +3476,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (fDebugNet || (vInv.size() != 1)) printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); - BOOST_FOREACH(const CInv& inv, vInv) + for (const CInv& inv : vInv) { if (fShutdown) return true; @@ -3603,7 +3629,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CTxDB txdb("r"); uint256 hashChainTip = 0; int nRequested = 0; - BOOST_FOREACH(const CBlock& header, vHeaders) + for (const CBlock& header : vHeaders) { if (!header.vtx.empty()) { @@ -3704,7 +3730,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } } - BOOST_FOREACH(uint256 hash, vEraseQueue) + for (uint256 hash : vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) @@ -3748,7 +3774,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (IsInitialBlockDownload()) { static int nBlocksSinceRequest = 0; - if (++nBlocksSinceRequest >= 100) + if (++nBlocksSinceRequest >= 1000) { nBlocksSinceRequest = 0; pfrom->pindexLastGetBlocksBegin = NULL; @@ -3780,7 +3806,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) int64_t nCutOff = GetTime() - (nNodeLifespan * 24 * 60 * 60); pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); - BOOST_FOREACH(const CAddress &addr, vAddr) + for (const CAddress &addr : vAddr) if(addr.nTime > nCutOff) pfrom->PushAddress(addr); } @@ -3885,7 +3911,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->setKnown.insert(alertHash); { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) alert.RelayTo(pnode); } } @@ -4100,7 +4126,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { // Periodically clear setAddrKnown to allow refresh broadcasts if (nLastRebroadcast) @@ -4125,7 +4151,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { vector vAddr; vAddr.reserve(pto->vAddrToSend.size()); - BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) + for (const CAddress& addr : pto->vAddrToSend) { // returns true if wasn't already contained in the set if (pto->setAddrKnown.insert(addr).second) @@ -4154,7 +4180,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LOCK(pto->cs_inventory); vInv.reserve(pto->vInventoryToSend.size()); vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) + for (const CInv& inv : pto->vInventoryToSend) { if (pto->setInventoryKnown.count(inv)) continue; @@ -4214,7 +4240,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (nBestHeight > nLastHeight) { nLastHeight = nBestHeight; nLastBlockReceived = GetTime(); - } else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) { + } else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 2) { if (GetTime() - nLastStallLog >= 10) { // log every 10s max printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n", nBestHeight, (int)(GetTime() - nLastBlockReceived), diff --git a/src/main.h b/src/main.h index e862eca..b57dd4f 100644 --- a/src/main.h +++ b/src/main.h @@ -34,6 +34,7 @@ static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; +static const unsigned int MAX_ORPHAN_BLOCKS = 750; static const unsigned int MAX_INV_SZ = 50000; static const int64_t MIN_TX_FEE = (1 * CENT) / 100; static const int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100; @@ -489,7 +490,7 @@ public: nBlockTime = GetAdjustedTime(); if ((int64_t)nLockTime < ((int64_t)nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; - BOOST_FOREACH(const CTxIn& txin, vin) + for (const CTxIn& txin : vin) if (!txin.IsFinal()) return false; return true; @@ -567,7 +568,7 @@ public: int64_t GetValueOut() const { int64_t nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) + for (const CTxOut& txout : vout) { nValueOut += txout.nValue; if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) @@ -955,7 +956,7 @@ public: int64_t GetMaxTransactionTime() const { int64_t maxTransactionTime = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) maxTransactionTime = std::max(maxTransactionTime, (int64_t)tx.nTime); return maxTransactionTime; } @@ -963,7 +964,7 @@ public: uint256 BuildMerkleTree() const { vMerkleTree.clear(); - BOOST_FOREACH(const CTransaction& tx, vtx) + for (const CTransaction& tx : vtx) vMerkleTree.push_back(tx.GetHash()); int j = 0; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) @@ -999,7 +1000,7 @@ public: { if (nIndex == -1) return 0; - BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + for (const uint256& otherside : vMerkleBranch) { if (nIndex & 1) hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); @@ -1518,7 +1519,7 @@ public: // Retrace how far back it was in the sender's branch int nDistance = 0; int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) + for (const uint256& hash : vHave) { std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) @@ -1537,7 +1538,7 @@ public: CBlockIndex* GetBlockIndex() { // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) + for (const uint256& hash : vHave) { std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) @@ -1553,7 +1554,7 @@ public: uint256 GetBlockHash() { // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) + for (const uint256& hash : vHave) { std::map::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) diff --git a/src/makefile.unix b/src/makefile.unix index 151aeec..0648289 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -107,7 +107,7 @@ endif # CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only # adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work. -xCXXFLAGS=-O2 $(EXT_OPTIONS) -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \ +xCXXFLAGS=-O2 -std=c++17 $(EXT_OPTIONS) -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \ $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS) # LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only @@ -171,7 +171,24 @@ OBJS= \ obj/scrypt-x86_64.o \ obj/smessage.o \ obj/onion_v3.o \ - obj/tor_process.o + obj/tor_process.o \ + obj/tor_embed_hooks.o \ + obj/tor_embedded.o + +# Embedded Tor support (optional) +# Build with: make -f makefile.unix USE_TOR_EMBEDDED=1 TOR_LIB_PATH=/path/to/libtor +# Requires libtor.a built from official Tor source (see CODEX-TOR-GUIDE.md) +TOR_SOURCE_ROOT ?= tor/tor-src +TOR_INCLUDE_PATH ?= $(TOR_SOURCE_ROOT)/src/feature/api +TOR_LIB_PATH ?= $(TOR_SOURCE_ROOT)/src/core $(TOR_SOURCE_ROOT)/src/lib $(TOR_SOURCE_ROOT)/src/trunnel +TOR_EMBEDDED_LIBS ?= -ltor-app -lor -lor-ctime -lor-event -lor-trunnel +ifdef USE_TOR_EMBEDDED + DEFS += -DENABLE_TOR_EMBEDDED + DEFS += $(addprefix -I,$(TOR_INCLUDE_PATH)) + LIBS += $(addprefix -L,$(TOR_LIB_PATH)) + LIBS += -Wl,--start-group $(TOR_EMBEDDED_LIBS) -Wl,--end-group + LIBS += -levent -levent_pthreads -lssl -lcrypto -lz -lm -lpthread +endif # ZMQ support (optional) # Build with: make -f makefile.unix USE_ZMQ=1 @@ -226,14 +243,7 @@ obj/%.o: %.c -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) -obj/%.o: tor/%.c - $(CC) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< - @cp $(@:%.o=%.d) $(@:%.o=%.P); \ - sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ - -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ - rm -f $(@:%.o=%.d) - -obj/anonymize.o: tor/anonymize.cpp +obj/tor_embed_hooks.o: tor_embed_hooks.cpp $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ @@ -254,6 +264,13 @@ obj/tor_process.o: tor/tor_process.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) +obj/tor_embedded.o: tor/tor_embedded.cpp + $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< + @cp $(@:%.o=%.d) $(@:%.o=%.P); \ + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ + rm -f $(@:%.o=%.d) + obj/net_bootstrap.o: net_bootstrap.cpp $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ @@ -261,7 +278,7 @@ obj/net_bootstrap.o: net_bootstrap.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) -trianglesd: $(OBJS:obj/%=obj/%) obj/anonymize.o +trianglesd: $(OBJS:obj/%=obj/%) $(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS) TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)) diff --git a/src/miner.cpp b/src/miner.cpp index b551348..36c2abf 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -72,7 +72,7 @@ public: { printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb); - BOOST_FOREACH(uint256 hash, setDependsOn) + for (uint256 hash : setDependsOn) printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); } }; @@ -188,7 +188,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) double dPriority = 0; int64_t nTotalIn = 0; bool fMissingInputs = false; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) { // Read prev transaction CTransaction txPrev; @@ -335,7 +335,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) uint256 hash = tx.GetHash(); if (mapDependers.count(hash)) { - BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) + for (COrphan* porphan : mapDependers[hash]) { if (!porphan->setDependsOn.empty()) { diff --git a/src/net.cpp b/src/net.cpp index 1fdba38..84e2480 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -213,7 +213,7 @@ bool RecvLine(SOCKET hSocket, string& strLine) void static AdvertizeLocal() { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { if (pnode->fSuccessfullyConnected) { @@ -458,7 +458,7 @@ CNode* FindNode(const CNetAddr& ip) { { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if ((CNetAddr)pnode->addr == ip) return (pnode); } @@ -468,7 +468,7 @@ CNode* FindNode(const CNetAddr& ip) CNode* FindNode(std::string addrName) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->addrName == addrName) return (pnode); return NULL; @@ -478,7 +478,7 @@ CNode* FindNode(const CService& addr) { { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if ((CService)pnode->addr == addr) return (pnode); } @@ -823,7 +823,7 @@ void ThreadSocketHandler2(void* parg) LOCK(cs_vNodes); // Disconnect unused nodes vector vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) { if (pnode->fDisconnect || (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) @@ -847,7 +847,7 @@ void ThreadSocketHandler2(void* parg) // Delete disconnected nodes list vNodesDisconnectedCopy = vNodesDisconnected; - BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) + for (CNode* pnode : vNodesDisconnectedCopy) { // wait until threads are done using it if (pnode->GetRefCount() <= 0) @@ -890,7 +890,7 @@ void ThreadSocketHandler2(void* parg) // struct timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = IsInitialBlockDownload() ? 10000 : 50000; // faster polling during IBD + timeout.tv_usec = IsInitialBlockDownload() ? 1000 : 50000; // 1ms during IBD, 50ms normal fd_set fdsetRecv; fd_set fdsetSend; @@ -901,14 +901,14 @@ void ThreadSocketHandler2(void* parg) SOCKET hSocketMax = 0; bool have_fds = false; - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { + for (SOCKET hListenSocket : vhListenSocket) { FD_SET(hListenSocket, &fdsetRecv); hSocketMax = max(hSocketMax, hListenSocket); have_fds = true; } { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { if (pnode->hSocket == INVALID_SOCKET) continue; @@ -948,7 +948,7 @@ void ThreadSocketHandler2(void* parg) // // Accept new connections // - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + for (SOCKET hListenSocket : vhListenSocket) if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) { #ifdef USE_IPV6 @@ -967,7 +967,7 @@ void ThreadSocketHandler2(void* parg) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->fInbound) nInbound++; } @@ -1007,10 +1007,10 @@ void ThreadSocketHandler2(void* parg) { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->AddRef(); } - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) { if (fShutdown) return; @@ -1100,7 +1100,7 @@ void ThreadSocketHandler2(void* parg) } { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->Release(); } @@ -1439,7 +1439,7 @@ void ThreadDNSAddressSeed2(void* parg) vector vaddr; if (LookupHost(strDNSSeed[seed_idx], vaddr)) { - BOOST_FOREACH(CNetAddr& ip, vaddr) + for (CNetAddr& ip : vaddr) { CAddress addr(CService(ip, GetDefaultPort())); addr.nTime = GetTime() - 3*24*60*60; // 3 days ago @@ -1541,7 +1541,7 @@ void ThreadOpenConnections2(void* parg) for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) + for (string strAddr : mapMultiArgs["-connect"]) { CAddress addr; OpenNetworkConnection(addr, NULL, strAddr.c_str()); @@ -1581,7 +1581,7 @@ void ThreadOpenConnections2(void* parg) { LOCK(cs_vNodes); bool fNoOutbound = true; - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (!pnode->fInbound) { fNoOutbound = false; break; } } if (fNoOutbound && (GetTime() - nStart > 30) && !fTestNet) @@ -1611,7 +1611,7 @@ void ThreadOpenConnections2(void* parg) set > setConnected; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (!pnode->fInbound) { setConnected.insert(pnode->addr.GetGroup()); nOutbound++; @@ -1688,7 +1688,7 @@ void ThreadOpenAddedConnections2(void* parg) if (HaveNameProxy()) { while(!fShutdown) { - BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) { + for (string& strAddNode : mapMultiArgs["-addnode"]) { CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); @@ -1702,7 +1702,7 @@ void ThreadOpenAddedConnections2(void* parg) } vector > vservAddressesToAdd(0); - BOOST_FOREACH(string& strAddNode, mapMultiArgs["-addnode"]) + for (string& strAddNode : mapMultiArgs["-addnode"]) { vector vservNode(0); if(Lookup(strAddNode.c_str(), vservNode, GetDefaultPort(), fNameLookup, 0)) @@ -1710,7 +1710,7 @@ void ThreadOpenAddedConnections2(void* parg) vservAddressesToAdd.push_back(vservNode); { LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH(CService& serv, vservNode) + for (CService& serv : vservNode) setservAddNodeAddresses.insert(serv); } } @@ -1722,9 +1722,9 @@ void ThreadOpenAddedConnections2(void* parg) // (keeping in mind that addnode entries can have many IPs if fNameLookup) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) for (vector >::iterator it = vservConnectAddresses.begin(); it != vservConnectAddresses.end(); it++) - BOOST_FOREACH(CService& addrNode, *(it)) + for (CService& addrNode : *(it)) if (pnode->addr == addrNode) { it = vservConnectAddresses.erase(it); @@ -1732,7 +1732,7 @@ void ThreadOpenAddedConnections2(void* parg) break; } } - BOOST_FOREACH(vector& vserv, vservConnectAddresses) + for (vector& vserv : vservConnectAddresses) { CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(CAddress(*(vserv.begin())), &grant); @@ -1822,7 +1822,7 @@ void ThreadMessageHandler2(void* parg) { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->AddRef(); } @@ -1830,7 +1830,7 @@ void ThreadMessageHandler2(void* parg) CNode* pnodeTrickle = NULL; if (!vNodesCopy.empty()) pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) { // Receive messages { @@ -1854,7 +1854,7 @@ void ThreadMessageHandler2(void* parg) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->Release(); } @@ -1872,7 +1872,7 @@ void ThreadMessageHandler2(void* parg) // Reduce vnThreadsRunning so StopNode has permission to exit while // we're sleeping, but we must always check fShutdown after doing this. vnThreadsRunning[THREAD_MESSAGEHANDLER]--; - MilliSleep(IsInitialBlockDownload() ? 10 : 100); + MilliSleep(IsInitialBlockDownload() ? 1 : 100); if (fRequestShutdown) StartShutdown(); vnThreadsRunning[THREAD_MESSAGEHANDLER]++; @@ -2027,7 +2027,7 @@ static void run_tor() { // which starts an external Tor binary with SOCKS5 proxy and v3 hidden service. // The old embedded Tor v2 code was removed (incompatible with OpenSSL 3.x). printf("Tor v3 mode: using managed Tor process via SOCKS5 proxy.\n"); - set_initialized(); + triangles_tor_set_initialized(); } @@ -2192,10 +2192,10 @@ public: ~CNetCleanup() { // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->hSocket != INVALID_SOCKET) closesocket(pnode->hSocket); - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + for (SOCKET hListenSocket : vhListenSocket) if (hListenSocket != INVALID_SOCKET) if (closesocket(hListenSocket) == SOCKET_ERROR) printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); @@ -2235,4 +2235,3 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt RelayInventory(inv); } - diff --git a/src/net.h b/src/net.h index b274c66..056bcf7 100644 --- a/src/net.h +++ b/src/net.h @@ -7,7 +7,6 @@ #include #include -#include #include #ifndef WIN32 @@ -27,8 +26,8 @@ extern int nBestHeight; -inline unsigned int ReceiveFloodSize() { return (unsigned int)-1; } -inline unsigned int SendBufferSize() { return (unsigned int)-1; } +inline unsigned int ReceiveFloodSize() { return 100 * 1024 * 1024; } // 100 MB +inline unsigned int SendBufferSize() { return 32 * 1024 * 1024; } // 32 MB void AddOneShot(std::string strDest); bool RecvLine(SOCKET hSocket, std::string& strLine); @@ -356,7 +355,7 @@ public: unsigned int GetTotalRecvSize() { unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + for (const CNetMessage &msg : vRecvMsg) total += msg.vRecv.size() + 24; return total; } @@ -368,7 +367,7 @@ public: void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; - BOOST_FOREACH(CNetMessage &msg, vRecvMsg) + for (CNetMessage &msg : vRecvMsg) msg.SetVersion(nVersionIn); } @@ -742,7 +741,7 @@ inline void RelayInventory(const CInv& inv) // Put on lists to offer to the other nodes { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) pnode->PushInventory(inv); } } diff --git a/src/openssl_compat.h b/src/openssl_compat.h new file mode 100644 index 0000000..92f2b02 --- /dev/null +++ b/src/openssl_compat.h @@ -0,0 +1,16 @@ +#ifndef TRIANGLES_OPENSSL_COMPAT_H +#define TRIANGLES_OPENSSL_COMPAT_H + +#include +#include + +static inline const char* TrianglesOpenSSLVersionString() +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + return OpenSSL_version(OPENSSL_VERSION); +#else + return SSLeay_version(SSLEAY_VERSION); +#endif +} + +#endif // TRIANGLES_OPENSSL_COMPAT_H diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp index 35dde93..b088fde 100644 --- a/src/qt/aboutdialog.cpp +++ b/src/qt/aboutdialog.cpp @@ -1,8 +1,6 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" -#include - #include "clientmodel.h" #include "dialog_move_handler.h" #include "version.h" diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index fee4afd..1649303 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -61,7 +61,7 @@ public: cachedAddressTable.clear(); { LOCK(wallet->cs_wallet); - BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& item, wallet->mapAddressBook) + for (const auto& item : wallet->mapAddressBook) { const CTrianglesAddress& address = item.first; const std::string& strName = item.second; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 370bd84..28e6208 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -506,7 +506,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) coinControl->ListSelected(vCoinControl); model->getOutputs(vCoinControl, vOutputs); - BOOST_FOREACH(const COutput& out, vOutputs) + for (const COutput& out : vOutputs) { // Quantity nQuantity++; @@ -647,7 +647,7 @@ void CoinControlDialog::updateView() map > mapCoins; model->listCoins(mapCoins); - BOOST_FOREACH(PAIRTYPE(QString, vector) coins, mapCoins) + for (auto coins : mapCoins) { QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem(); QString sWalletAddress = coins.first; @@ -679,7 +679,7 @@ void CoinControlDialog::updateView() double dPrioritySum = 0; int nChildren = 0; int nInputSum = 0; - BOOST_FOREACH(const COutput& out, coins.second) + for (const COutput& out : coins.second) { int nInputSize = 148; // 180 if uncompressed public key nSum += out.tx->vout[out.i].nValue; diff --git a/src/qt/messagepage.cpp b/src/qt/messagepage.cpp index e05c91f..b37a346 100644 --- a/src/qt/messagepage.cpp +++ b/src/qt/messagepage.cpp @@ -31,7 +31,7 @@ protected: void MessageViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - QStyleOptionViewItemV4 optionV4 = option; + QStyleOptionViewItem optionV4 = option; initStyleOption(&optionV4, index); QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style(); @@ -62,7 +62,7 @@ void MessageViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o QSize MessageViewDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const { - QStyleOptionViewItemV4 options = option; + QStyleOptionViewItem options = option; initStyleOption(&options, index); QTextDocument doc; @@ -159,7 +159,7 @@ void MessagePage::setModel(MessageModel *model) // Set column widths ui->tableView->horizontalHeader()->resizeSection(MessageModel::Type, 100); ui->tableView->horizontalHeader()->resizeSection(MessageModel::Label, 100); - ui->tableView->horizontalHeader()->setResizeMode(MessageModel::Label, QHeaderView::Stretch); + ui->tableView->horizontalHeader()->setSectionResizeMode(MessageModel::Label, QHeaderView::Stretch); ui->tableView->horizontalHeader()->resizeSection(MessageModel::FromAddress, 320); ui->tableView->horizontalHeader()->resizeSection(MessageModel::ToAddress, 320); ui->tableView->horizontalHeader()->resizeSection(MessageModel::SentDateTime, 170); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 07b4c48..ee53b04 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -5,6 +5,7 @@ #include "trianglesrpc.h" #include "guiutil.h" #include "dialog_move_handler.h" +#include "openssl_compat.h" #include #include @@ -207,7 +208,7 @@ RPCConsole::RPCConsole(QWidget *parent) : connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); // set OpenSSL version label - ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); + ui->openSSLVersion->setText(TrianglesOpenSSLVersionString()); startExecutor(); diff --git a/src/qt/signmessagepage.cpp b/src/qt/signmessagepage.cpp index 0a7daa7..dbc41b3 100644 --- a/src/qt/signmessagepage.cpp +++ b/src/qt/signmessagepage.cpp @@ -14,8 +14,6 @@ #include #include -#include - SignMessagePage::SignMessagePage(QWidget *parent) : QWidget(parent), ui(new Ui::SignMessagePage), diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e2a4acd..221c8ea 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -77,7 +77,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) if (nNet > 0) { // Credit - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) { if (wallet->IsMine(txout)) { @@ -125,7 +125,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // Coinbase // int64_t nUnmatured = 0; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) nUnmatured += wallet->GetCredit(txout); strHTML += "" + tr("Credit") + ": "; if (wtx.IsInMainChain()) @@ -144,11 +144,11 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) else { bool fAllFromMe = true; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + for (const CTxIn& txin : wtx.vin) fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe) @@ -156,7 +156,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // // Debit // - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) { if (wallet->IsMine(txout)) continue; @@ -196,10 +196,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // // Mixed debit transaction // - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + for (const CTxIn& txin : wtx.vin) if (wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + TrianglesUnits::formatWithUnit(TrianglesUnits::TRI, -wallet->GetDebit(txin)) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) if (wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + TrianglesUnits::formatWithUnit(TrianglesUnits::TRI, wallet->GetCredit(txout)) + "
"; } @@ -234,10 +234,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) if (fDebug) { strHTML += "

" + tr("Debug information") + "

"; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + for (const CTxIn& txin : wtx.vin) if(wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + TrianglesUnits::formatWithUnit(TrianglesUnits::TRI, -wallet->GetDebit(txin)) + "
"; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) if(wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + TrianglesUnits::formatWithUnit(TrianglesUnits::TRI, wallet->GetCredit(txout)) + "
"; @@ -251,7 +251,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) { LOCK(wallet->cs_wallet); - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + for (const CTxIn& txin : wtx.vin) { COutPoint prevout = txin.prevout; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index eb181ea..73982e2 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -87,11 +87,11 @@ QList TransactionRecord::decomposeTransaction(const CWallet * else { bool fAllFromMe = true; - BOOST_FOREACH(const CTxIn& txin, wtx.vin) + for (const CTxIn& txin : wtx.vin) fAllFromMe = fAllFromMe && wallet->IsMine(txin); bool fAllToMe = true; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) fAllToMe = fAllToMe && wallet->IsMine(txout); if (fAllFromMe && fAllToMe) diff --git a/src/qt/triangles.cpp b/src/qt/triangles.cpp index 47261f6..217bb11 100644 --- a/src/qt/triangles.cpp +++ b/src/qt/triangles.cpp @@ -219,6 +219,7 @@ int main(int argc, char *argv[]) if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(true); + InitMessage(_("Preparing interface...")); TrianglesGUI window; guiref = &window; if(AppInit2()) diff --git a/src/qt/trianglesgui.cpp b/src/qt/trianglesgui.cpp index c51ba83..094bfad 100644 --- a/src/qt/trianglesgui.cpp +++ b/src/qt/trianglesgui.cpp @@ -76,9 +76,10 @@ #include #include #include -#include +#include #include #include +#include //#include #include @@ -297,19 +298,15 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent): receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); - sendCoinsPage = new SendCoinsDialog(this); - messagePage = new MessagePage(this); - signMessagePage = new SignMessagePage(this); - verifyMessagePage = new VerifyMessagePage(this); + sendCoinsPage = 0; + messagePage = 0; + signMessagePage = 0; + verifyMessagePage = 0; centralWidget = ui->stackedWidget; centralWidget->addWidget(overviewPage); centralWidget->addWidget(transactionsPage); centralWidget->addWidget(addressBookPage); centralWidget->addWidget(receiveCoinsPage); - centralWidget->addWidget(sendCoinsPage); - centralWidget->addWidget(messagePage); - centralWidget->addWidget(signMessagePage); - centralWidget->addWidget(verifyMessagePage); QSizeGrip* grip = new QSizeGrip(this); grip->setStyleSheet("width: 6px; height: 6px; image: url(:/res/icons/handle.png);"); @@ -337,6 +334,10 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent): updateStakingIcon(); } + QTimer *timerShutdown = new QTimer(this); + connect(timerShutdown, SIGNAL(timeout()), this, SLOT(detectShutdown())); + timerShutdown->start(200); + // Progress bar and label for blocks download progressBarLabel = ui->label_synchronization; progressBarLabel->setVisible(false); @@ -359,8 +360,7 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent): // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); - rpcConsole = new RPCConsole(this); - connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + connect(openRPCConsoleAction, SIGNAL(triggered()), this, SLOT(openRPCConsole())); // Clicking on "Verify Message" in the address book sends you to the verify message tab connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); @@ -479,7 +479,7 @@ void TrianglesGUI::createActions(bool fIsTestnet) openRPCConsoleAction = new QAction(QIcon(":/menu_16/debug"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); - connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(quitAction, SIGNAL(triggered()), this, SLOT(requestShutdown())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked())); @@ -563,7 +563,8 @@ void TrianglesGUI::setClientModel(ClientModel *clientModel) // Receive and report messages from network/worker thread connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); - rpcConsole->setClientModel(clientModel); + if (rpcConsole) + rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); } @@ -582,9 +583,12 @@ void TrianglesGUI::setWalletModel(WalletModel *walletModel) overviewPage->setModel(walletModel); addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); - sendCoinsPage->setModel(walletModel); - signMessagePage->setModel(walletModel); - verifyMessagePage->setModel(walletModel); + if (sendCoinsPage) + sendCoinsPage->setModel(walletModel); + if (signMessagePage) + signMessagePage->setModel(walletModel); + if (verifyMessagePage) + verifyMessagePage->setModel(walletModel); setEncryptionStatus(walletModel->getEncryptionStatus()); connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); @@ -610,7 +614,8 @@ void TrianglesGUI::setMessageModel(MessageModel *messageModel) connect(messageModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); // Put transaction list in tabs - messagePage->setModel(messageModel); + if (messagePage) + messagePage->setModel(messageModel); // Balloon pop-up for new message connect(messageModel, SIGNAL(rowsInserted(QModelIndex,int,int)), @@ -626,6 +631,60 @@ void TrianglesGUI::ensureMessageModel() setMessageModel(new MessageModel(pwalletMain, walletModel, this)); } +void TrianglesGUI::ensureSendCoinsPage() +{ + if (sendCoinsPage) + return; + + sendCoinsPage = new SendCoinsDialog(this); + if (walletModel) + sendCoinsPage->setModel(walletModel); + centralWidget->addWidget(sendCoinsPage); +} + +void TrianglesGUI::ensureMessagePage() +{ + if (messagePage) + return; + + messagePage = new MessagePage(this); + if (messageModel) + messagePage->setModel(messageModel); + centralWidget->addWidget(messagePage); +} + +void TrianglesGUI::ensureSignMessagePage() +{ + if (signMessagePage) + return; + + signMessagePage = new SignMessagePage(this); + if (walletModel) + signMessagePage->setModel(walletModel); + centralWidget->addWidget(signMessagePage); +} + +void TrianglesGUI::ensureVerifyMessagePage() +{ + if (verifyMessagePage) + return; + + verifyMessagePage = new VerifyMessagePage(this); + if (walletModel) + verifyMessagePage->setModel(walletModel); + centralWidget->addWidget(verifyMessagePage); +} + +void TrianglesGUI::ensureRPCConsole() +{ + if (rpcConsole) + return; + + rpcConsole = new RPCConsole(this); + if (clientModel) + rpcConsole->setClientModel(clientModel); +} + void TrianglesGUI::createTrayIcon() { #ifndef Q_OS_MAC @@ -698,7 +757,8 @@ void TrianglesGUI::restoreWindowGeometry() QSize size = settings.value("nWindowSize", QSize(850, 550)).toSize(); if (!pos.x() && !pos.y()) { - QRect screen = QApplication::desktop()->screenGeometry(); + QScreen *screenObject = QGuiApplication::primaryScreen(); + QRect screen = screenObject ? screenObject->availableGeometry() : QRect(QPoint(0, 0), size); pos.setX((screen.width()-size.width())/2); pos.setY((screen.height()-size.height())/2); } @@ -923,11 +983,17 @@ void TrianglesGUI::closeEvent(QCloseEvent *event) } } #endif - // Actually closing - quit the application - QApplication::quit(); + // Actually closing - request a full core shutdown before leaving the UI loop. + StartShutdown(); + event->accept(); QMainWindow::closeEvent(event); } +void TrianglesGUI::requestShutdown() +{ + StartShutdown(); +} + void TrianglesGUI::askFee(qint64 nFeeRequired, bool *payFee) { QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " @@ -1070,6 +1136,8 @@ void TrianglesGUI::gotoReceiveCoinsPage() void TrianglesGUI::gotoSendCoinsPage() { + ensureSendCoinsPage(); + sendCoinsAction->setChecked(true); centralWidget->setCurrentWidget(sendCoinsPage); @@ -1079,6 +1147,7 @@ void TrianglesGUI::gotoSendCoinsPage() void TrianglesGUI::gotoMessagePage() { + ensureMessagePage(); ensureMessageModel(); messageAction->setChecked(true); @@ -1091,6 +1160,8 @@ void TrianglesGUI::gotoMessagePage() void TrianglesGUI::gotoSignMessageTab(QString addr) { + ensureSignMessagePage(); + centralWidget->setCurrentWidget(signMessagePage); exportAction->setEnabled(false); @@ -1105,6 +1176,8 @@ void TrianglesGUI::gotoSignMessageTab(QString addr) void TrianglesGUI::gotoVerifyMessageTab(QString addr) { + ensureVerifyMessagePage(); + centralWidget->setCurrentWidget(verifyMessagePage); exportAction->setEnabled(false); @@ -1128,6 +1201,7 @@ void TrianglesGUI::dropEvent(QDropEvent *event) { if(event->mimeData()->hasUrls()) { + ensureSendCoinsPage(); int nValidUrisFound = 0; QList uris = event->mimeData()->urls(); foreach(const QUrl &uri, uris) @@ -1209,6 +1283,7 @@ void TrianglesGUI::updateMask() void TrianglesGUI::handleURI(QString strURI) { // URI has to be valid + ensureSendCoinsPage(); if (sendCoinsPage->handleURI(strURI)) { showNormalIfMinimized(); @@ -1265,6 +1340,12 @@ void TrianglesGUI::menuFileRequested() } } +void TrianglesGUI::openRPCConsole() +{ + ensureRPCConsole(); + rpcConsole->show(); +} + void TrianglesGUI::menuOperationsRequested() { QMenu menu(this); @@ -1564,5 +1645,6 @@ void TrianglesGUI::detectShutdown() void TrianglesGUI::on_bHelp_clicked() { + ensureRPCConsole(); rpcConsole->show(); } diff --git a/src/qt/trianglesgui.h b/src/qt/trianglesgui.h index 4c519e7..8512e64 100644 --- a/src/qt/trianglesgui.h +++ b/src/qt/trianglesgui.h @@ -79,6 +79,11 @@ protected: private: void updateMask(); + void ensureSendCoinsPage(); + void ensureMessagePage(); + void ensureSignMessagePage(); + void ensureVerifyMessagePage(); + void ensureRPCConsole(); private: Ui::MainWindow *ui; @@ -181,6 +186,8 @@ public slots: private slots: void ensureMessageModel(); + void openRPCConsole(); + void requestShutdown(); void menuFileRequested(); void menuOperationsRequested(); diff --git a/src/qt/verifymessagepage.cpp b/src/qt/verifymessagepage.cpp index f92af22..d994e03 100644 --- a/src/qt/verifymessagepage.cpp +++ b/src/qt/verifymessagepage.cpp @@ -14,8 +14,6 @@ #include #include -#include - VerifyMessagePage::VerifyMessagePage(QWidget *parent) : QWidget(parent), ui(new Ui::VerifyMessagePage), diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index eb0b56d..68ee438 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -168,7 +168,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList vCoins; wallet->AvailableCoins(vCoins, true, coinControl); - BOOST_FOREACH(const COutput& out, vCoins) + for (const COutput& out : vCoins) nBalance += out.tx->vout[out.i].nValue; if(total > nBalance) @@ -451,7 +451,7 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const // returns a list of COutputs from COutPoints void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) { - BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) + for (const COutPoint& outpoint : vOutpoints) { if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); @@ -469,7 +469,7 @@ void WalletModel::listCoins(std::map >& mapCoins) std::vector vLockedCoins; // add locked coins - BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) + for (const COutPoint& outpoint : vLockedCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); @@ -478,7 +478,7 @@ void WalletModel::listCoins(std::map >& mapCoins) vCoins.push_back(out); } - BOOST_FOREACH(const COutput& out, vCoins) + for (const COutput& out : vCoins) { COutput cout = out; diff --git a/src/rest.cpp b/src/rest.cpp index ac9d343..a7aeede 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -393,7 +393,7 @@ static bool HandleMempool(string& strReply, int& nStatus) vector vtxid; mempool.queryHashes(vtxid); Array a; - BOOST_FOREACH(const uint256& hash, vtxid) + for (const uint256& hash : vtxid) a.push_back(hash.ToString()); strReply = write_string(Value(a), false) + "\n"; nStatus = HTTP_OK; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 7bc56cb..a16cab2 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -128,7 +128,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPri result.push_back(Pair("modifier", strprintf("%016"PRIx64, blockindex->nStakeModifier))); result.push_back(Pair("modifierchecksum", strprintf("%08x", blockindex->nStakeModifierChecksum))); Array txinfo; - BOOST_FOREACH (const CTransaction& tx, block.vtx) + for (const CTransaction& tx : block.vtx) { if (fPrintTransactionDetail) { @@ -211,7 +211,7 @@ Value getrawmempool(const Array& params, bool fHelp) mempool.queryHashes(vtxid); Array a; - BOOST_FOREACH(const uint256& hash, vtxid) + for (const uint256& hash : vtxid) a.push_back(hash.ToString()); return a; diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index bfe5526..e77cdaa 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -63,7 +63,7 @@ std::string static EncodeDumpTime(int64_t nTime) { std::string static EncodeDumpString(const std::string &str) { std::stringstream ret; - BOOST_FOREACH(unsigned char c, str) { + for (unsigned char c : str) { if (c <= 32 || c >= 128 || c == '%') { ret << '%' << HexStr(&c, &c + 1); } else { diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 3026309..583a73d 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -144,7 +144,7 @@ Value getworkex(const Array& params, bool fHelp) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) + for (CBlock* pblock : vNewBlock) delete pblock; vNewBlock.clear(); } @@ -191,7 +191,7 @@ Value getworkex(const Array& params, bool fHelp) Array merkle_arr; - BOOST_FOREACH(uint256 merkleh, merkle) { + for (uint256 merkleh : merkle) { merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh))); } @@ -278,7 +278,7 @@ Value getwork(const Array& params, bool fHelp) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); - BOOST_FOREACH(CBlock* pblock, vNewBlock) + for (CBlock* pblock : vNewBlock) delete pblock; vNewBlock.clear(); } @@ -442,7 +442,7 @@ Value getblocktemplate(const Array& params, bool fHelp) map setTxIndex; int i = 0; CTxDB txdb("r"); - BOOST_FOREACH (CTransaction& tx, pblock->vtx) + for (CTransaction& tx : pblock->vtx) { uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; @@ -466,7 +466,7 @@ Value getblocktemplate(const Array& params, bool fHelp) entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); Array deps; - BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) + for (MapPrevTx::value_type& inp : mapInputs) { if (setTxIndex.count(inp.first)) deps.push_back(setTxIndex[inp.first]); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index ce11e37..4f1cfa3 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -51,7 +51,7 @@ static void CopyNodeStats(std::vector& vstats) LOCK(cs_vNodes); vstats.reserve(vNodes.size()); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { CNodeStats stats; pnode->copyStats(stats); vstats.push_back(stats); @@ -70,7 +70,7 @@ Value getpeerinfo(const Array& params, bool fHelp) Array ret; - BOOST_FOREACH(const CNodeStats& stats, vstats) { + for (const CNodeStats& stats : vstats) { Object obj; obj.push_back(Pair("addr", stats.addrName)); @@ -140,7 +140,7 @@ Value sendalert(const Array& params, bool fHelp) // Relay alert { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) alert.RelayTo(pnode); } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index a474c35..6e73452 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -3,8 +3,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include - #include "base58.h" #include "trianglesrpc.h" #include "txdb.h" @@ -15,7 +13,6 @@ using namespace std; using namespace boost; -using namespace boost::assign; using namespace json_spirit; void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex) @@ -39,7 +36,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeH out.push_back(Pair("type", GetTxnOutputType(type))); Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) + for (const CTxDestination& addr : addresses) a.push_back(CTrianglesAddress(addr).ToString()); out.push_back(Pair("addresses", a)); } @@ -51,7 +48,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) entry.push_back(Pair("time", (boost::int64_t)tx.nTime)); entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); Array vin; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) { Object in; if (tx.IsCoinBase()) @@ -162,7 +159,7 @@ Value listunspent(const Array& params, bool fHelp) if (params.size() > 2) { Array inputs = params[2].get_array(); - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { CTrianglesAddress address(input.get_str()); if (!address.IsValid()) @@ -176,7 +173,7 @@ Value listunspent(const Array& params, bool fHelp) Array results; vector vecOutputs; pwalletMain->AvailableCoins(vecOutputs, false); - BOOST_FOREACH(const COutput& out, vecOutputs) + for (const COutput& out : vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; @@ -231,7 +228,7 @@ Value createrawtransaction(const Array& params, bool fHelp) CTransaction rawTx; - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { const Object& o = input.get_obj(); @@ -254,7 +251,7 @@ Value createrawtransaction(const Array& params, bool fHelp) } set setAddress; - BOOST_FOREACH(const Pair& s, sendTo) + for (const Pair& s : sendTo) { CTrianglesAddress address(s.name_); if (!address.IsValid()) @@ -382,7 +379,7 @@ Value signrawtransaction(const Array& params, bool fHelp) tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); // Copy results into mapPrevOut: - BOOST_FOREACH(const CTxIn& txin, tempTx.vin) + for (const CTxIn& txin : tempTx.vin) { const uint256& prevHash = txin.prevout.hash; if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n) @@ -394,7 +391,7 @@ Value signrawtransaction(const Array& params, bool fHelp) if (params.size() > 1 && params[1].type() != null_type) { Array prevTxs = params[1].get_array(); - BOOST_FOREACH(Value& p, prevTxs) + for (Value& p : prevTxs) { if (p.type() != obj_type) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); @@ -442,7 +439,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { fGivenKeys = true; Array keys = params[2].get_array(); - BOOST_FOREACH(Value k, keys) + for (Value k : keys) { CTrianglesSecret vchSecret; bool fGood = vchSecret.SetString(k.get_str()); @@ -463,15 +460,14 @@ Value signrawtransaction(const Array& params, bool fHelp) int nHashType = SIGHASH_ALL; if (params.size() > 3 && params[3].type() != null_type) { - static map mapSigHashValues = - boost::assign::map_list_of - (string("ALL"), int(SIGHASH_ALL)) - (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) - (string("NONE"), int(SIGHASH_NONE)) - (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) - (string("SINGLE"), int(SIGHASH_SINGLE)) - (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) - ; + static map mapSigHashValues = { + {"ALL", int(SIGHASH_ALL)}, + {"ALL|ANYONECANPAY", int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, + {"NONE", int(SIGHASH_NONE)}, + {"NONE|ANYONECANPAY", int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)}, + {"SINGLE", int(SIGHASH_SINGLE)}, + {"SINGLE|ANYONECANPAY", int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, + }; string strHashType = params[3].get_str(); if (mapSigHashValues.count(strHashType)) nHashType = mapSigHashValues[strHashType]; @@ -498,7 +494,7 @@ Value signrawtransaction(const Array& params, bool fHelp) SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) + for (const CTransaction& txv : txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } diff --git a/src/rpcsmessage.cpp b/src/rpcsmessage.cpp index 497f91e..206f680 100644 --- a/src/rpcsmessage.cpp +++ b/src/rpcsmessage.cpp @@ -301,7 +301,7 @@ Value smsglocalkeys(const Array& params, bool fHelp) if (mode == "wallet") { uint32_t nKeys = 0; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& entry, pwalletMain->mapAddressBook) + for (const auto& entry : pwalletMain->mapAddressBook) { if (!IsMine(*pwalletMain, entry.first)) continue; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index de42157..b3192a4 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -60,7 +60,7 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair("txid", wtx.GetHash().GetHex())); entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); - BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) + for (const auto& item : wtx.mapValue) entry.push_back(Pair(item.first, item.second)); } @@ -211,7 +211,7 @@ CTrianglesAddress GetAccountAddress(string strAccount, bool bForceNew=false) ++it) { const CWalletTx& wtx = (*it).second; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) if (txout.scriptPubKey == scriptPubKey) bKeyUsed = true; } @@ -309,7 +309,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) // Find all addresses that have the given account Array ret; - BOOST_FOREACH(const PAIRTYPE(CTrianglesAddress, string)& item, pwalletMain->mapAddressBook) + for (const auto& item : pwalletMain->mapAddressBook) { const CTrianglesAddress& address = item.first; const string& strName = item.second; @@ -366,10 +366,10 @@ Value listaddressgroupings(const Array& params, bool fHelp) Array jsonGroupings; map balances = pwalletMain->GetAddressBalances(); - BOOST_FOREACH(set grouping, pwalletMain->GetAddressGroupings()) + for (set grouping : pwalletMain->GetAddressGroupings()) { Array jsonGrouping; - BOOST_FOREACH(CTxDestination address, grouping) + for (CTxDestination address : grouping) { Array addressInfo; addressInfo.push_back(CTrianglesAddress(address).ToString()); @@ -487,7 +487,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) if (txout.scriptPubKey == scriptPubKey) if (wtx.GetDepthInMainChain() >= nMinDepth) nAmount += txout.nValue; @@ -499,7 +499,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) void GetAccountAddresses(string strAccount, set& setAddress) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook) + for (const auto& item : pwalletMain->mapAddressBook) { const CTxDestination& address = item.first; const string& strName = item.second; @@ -535,7 +535,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) @@ -613,10 +613,10 @@ Value getbalance(const Array& params, bool fHelp) wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); if (wtx.GetDepthInMainChain() >= nMinDepth && wtx.GetBlocksToMaturity() == 0) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) + for (const auto& r : listReceived) nBalance += r.second; } - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent) + for (const auto& r : listSent) nBalance -= r.second; nBalance -= allFee; } @@ -754,7 +754,7 @@ Value sendmany(const Array& params, bool fHelp) vector > vecSend; int64_t totalAmount = 0; - BOOST_FOREACH(const Pair& s, sendTo) + for (const Pair& s : sendTo) { CTrianglesAddress address(s.name_); if (!address.IsValid()) @@ -925,7 +925,7 @@ Value ListReceived(const Array& params, bool fByAccounts) if (nDepth < nMinDepth) continue; - BOOST_FOREACH(const CTxOut& txout, wtx.vout) + for (const CTxOut& txout : wtx.vout) { CTxDestination address; if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address)) @@ -940,7 +940,7 @@ Value ListReceived(const Array& params, bool fByAccounts) // Reply Array ret; map mapAccountTally; - BOOST_FOREACH(const PAIRTYPE(CTrianglesAddress, string)& item, pwalletMain->mapAddressBook) + for (const auto& item : pwalletMain->mapAddressBook) { const CTrianglesAddress& address = item.first; const string& strAccount = item.second; @@ -1044,7 +1044,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Sent if ((!wtx.IsCoinStake()) && (!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) + for (const auto& s : listSent) { Object entry; entry.push_back(Pair("account", strSentAccount)); @@ -1062,7 +1062,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { bool stop = false; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) + for (const auto& r : listReceived) { string account; if (pwalletMain->mapAddressBook.count(r.first)) @@ -1191,7 +1191,7 @@ Value listaccounts(const Array& params, bool fHelp) nMinDepth = params[0].get_int(); map mapAccountBalances; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) { + for (const auto& entry : pwalletMain->mapAddressBook) { if (IsMine(*pwalletMain, entry.first)) // This address belongs to me mapAccountBalances[entry.second] = 0; } @@ -1208,11 +1208,11 @@ Value listaccounts(const Array& params, bool fHelp) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) + for (const auto& s : listSent) mapAccountBalances[strSentAccount] -= s.second; if (nDepth >= nMinDepth && wtx.GetBlocksToMaturity() == 0) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) + for (const auto& r : listReceived) if (pwalletMain->mapAddressBook.count(r.first)) mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second; else @@ -1222,11 +1222,11 @@ Value listaccounts(const Array& params, bool fHelp) list acentries; CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - BOOST_FOREACH(const CAccountingEntry& entry, acentries) + for (const CAccountingEntry& entry : acentries) mapAccountBalances[entry.strAccount] += entry.nCreditDebit; Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) { + for (const auto& accountBalance : mapAccountBalances) { ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); } return ret; @@ -1614,7 +1614,7 @@ public: obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); Array a; - BOOST_FOREACH(const CTxDestination& addr, addresses) + for (const CTxDestination& addr : addresses) a.push_back(CTrianglesAddress(addr).ToString()); obj.push_back(Pair("addresses", a)); if (whichType == TX_MULTISIG) diff --git a/src/script.cpp b/src/script.cpp index 22bdc95..ddc8d44 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -3,7 +3,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include #include @@ -1331,7 +1330,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector& pubkeys, const CKeyStore& keystore) { unsigned int nResult = 0; - BOOST_FOREACH(const valtype& pubkey, pubkeys) + for (const valtype& pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); if (keystore.HaveKey(keyID)) @@ -1637,7 +1636,7 @@ public: std::vector vDest; int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { - BOOST_FOREACH(const CTxDestination &dest, vDest) + for (const CTxDestination &dest : vDest) boost::apply_visitor(*this, dest); } } @@ -1792,7 +1791,7 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig static CScript PushAll(const vector& values) { CScript result; - BOOST_FOREACH(const valtype& v, values) + for (const valtype& v : values) result << v; return result; } @@ -1803,12 +1802,12 @@ static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, u { // Combine all the signatures we've got: set allsigs; - BOOST_FOREACH(const valtype& v, sigs1) + for (const valtype& v : sigs1) { if (!v.empty()) allsigs.insert(v); } - BOOST_FOREACH(const valtype& v, sigs2) + for (const valtype& v : sigs2) { if (!v.empty()) allsigs.insert(v); @@ -1819,7 +1818,7 @@ static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, u unsigned int nSigsRequired = vSolutions.front()[0]; unsigned int nPubKeys = vSolutions.size()-2; map sigs; - BOOST_FOREACH(const valtype& sig, allsigs) + for (const valtype& sig : allsigs) { for (unsigned int i = 0; i < nPubKeys; i++) { @@ -2031,7 +2030,7 @@ void CScript::SetMultisig(int nRequired, const std::vector& keys) this->clear(); *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) + for (const CKey& key : keys) *this << key.GetPubKey(); *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; } diff --git a/src/script.h b/src/script.h index ac7b387..22d3b45 100644 --- a/src/script.h +++ b/src/script.h @@ -11,7 +11,6 @@ #include -#include #include #include "keystore.h" @@ -218,7 +217,7 @@ inline std::string ValueString(const std::vector& vch) inline std::string StackString(const std::vector >& vStack) { std::string str; - BOOST_FOREACH(const std::vector& vch, vStack) + for (const std::vector& vch : vStack) { if (!str.empty()) str += " "; diff --git a/src/smessage.cpp b/src/smessage.cpp index ac89d8a..d62dc02 100644 --- a/src/smessage.cpp +++ b/src/smessage.cpp @@ -670,7 +670,7 @@ void ThreadSecureMsg(void* parg) printf("Lock on bucket %"PRId64" for peer %u timed out.\n", it->first, nPeerId); // -- look through the nodes for the peer that locked this bucket LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { if (pnode->smsgData.nPeerId != nPeerId) continue; @@ -961,7 +961,7 @@ int SecureMsgAddWalletAddresses() printf("SecureMsgAddWalletAddresses()\n"); uint32_t nAdded = 0; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& entry, pwalletMain->mapAddressBook) + for (const auto& entry : pwalletMain->mapAddressBook) { if (!IsMine(*pwalletMain, entry.first)) continue; @@ -1255,7 +1255,7 @@ bool SecureMsgEnable() // -- ping each peer, don't know which have messaging enabled { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { pnode->PushMessage("smsgPing"); pnode->PushMessage("smsgPong"); // Send pong as have missed initial ping sent by peer when it connected @@ -1291,7 +1291,7 @@ bool SecureMsgDisable() // -- tell each smsg enabled peer that this node is disabling { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + for (CNode* pnode : vNodes) { if (!pnode->smsgData.fEnabled) continue; @@ -1947,7 +1947,7 @@ static bool ScanBlock(CBlock& block, CTxDB& txdb, SecMsgDB& addrpkdb, uint32_t& nTransactions, uint32_t& nInputs, uint32_t& nPubkeys, uint32_t& nDuplicates) { // -- should have LOCK(cs_smsg) where db is opened - BOOST_FOREACH(CTransaction& tx, block.vtx) + for (CTransaction& tx : block.vtx) { if (!tx.IsStandard()) continue; // leave out coinbase and others @@ -3674,7 +3674,7 @@ int SecureMsgSend(std::string& addressFrom, std::string& addressTo, std::string& std::string addressOutbox = "None"; CTrianglesAddress coinAddrOutbox; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, std::string)& entry, pwalletMain->mapAddressBook) + for (const auto& entry : pwalletMain->mapAddressBook) { // -- get first owned address if (!IsMine(*pwalletMain, entry.first)) diff --git a/src/sync.cpp b/src/sync.cpp index 1ac4403..b8a17f0 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -5,7 +5,6 @@ #include "sync.h" #include "util.h" -#include #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine) @@ -58,14 +57,14 @@ static void potential_deadlock_detected(const std::pair& mismatch, { printf("POTENTIAL DEADLOCK DETECTED\n"); printf("Previous lock order was:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) + for (const auto& i : s2) { if (i.first == mismatch.first) printf(" (1)"); if (i.first == mismatch.second) printf(" (2)"); printf(" %s\n", i.second.ToString().c_str()); } printf("Current lock order is:\n"); - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) + for (const auto& i : s1) { if (i.first == mismatch.first) printf(" (1)"); if (i.first == mismatch.second) printf(" (2)"); @@ -84,7 +83,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) (*lockstack).push_back(std::make_pair(c, locklocation)); if (!fTry) { - BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, (*lockstack)) { + for (const auto& i : (*lockstack)) { if (i.first == c) break; std::pair p1 = std::make_pair(i.first, c); diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp index fe99c28..fdd53eb 100644 --- a/src/test/Checkpoints_tests.cpp +++ b/src/test/Checkpoints_tests.cpp @@ -1,9 +1,7 @@ // // Unit tests for block-chain checkpoints // -#include // for 'map_list_of()' #include -#include #include "../checkpoints.h" #include "../util.h" diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 70707b6..3c2e71e 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -3,10 +3,8 @@ // #include -#include // for 'map_list_of()' #include #include -#include #include "main.h" #include "wallet.h" @@ -97,22 +95,21 @@ static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, in BOOST_AUTO_TEST_CASE(DoS_checknbits) { - using namespace boost::assign; // for 'map_list_of()' - // Timestamps,nBits from the Triangles blockchain. // These are the block-chain checkpoint blocks typedef std::map BlockData; - BlockData chainData = - map_list_of(1239852051,486604799)(1262749024,486594666) - (1279305360,469854461)(1280200847,469830746)(1281678674,469809688) - (1296207707,453179945)(1302624061,453036989)(1309640330,437004818) - (1313172719,436789733); + BlockData chainData = { + {1239852051,486604799},{1262749024,486594666}, + {1279305360,469854461},{1280200847,469830746},{1281678674,469809688}, + {1296207707,453179945},{1302624061,453036989},{1309640330,437004818}, + {1313172719,436789733}, + }; // Make sure CheckNBits considers every combination of block-chain-lock-in-points // "sane": - BOOST_FOREACH(const BlockData::value_type& i, chainData) + for (const BlockData::value_type& i : chainData) { - BOOST_FOREACH(const BlockData::value_type& j, chainData) + for (const BlockData::value_type& j : chainData) { BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first)); } diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index 8ac6572..a4f32a1 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -1,6 +1,5 @@ #include -#include #include "init.h" #include "wallet.h" @@ -16,7 +15,7 @@ GetResults(CWalletDB& walletdb, std::map& results) results.clear(); BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK); walletdb.ListAccountCreditDebit("", aes); - BOOST_FOREACH(CAccountingEntry& ae, aes) + for (CAccountingEntry& ae : aes) { results[ae.nOrderPos] = ae; } diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index d03703b..5454760 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(base58_EncodeBase58) { Array tests = read_json("base58_encode_decode.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) Array tests = read_json("base58_encode_decode.json"); std::vector result; - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Save global state bool fTestNet_stored = fTestNet; - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); @@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) // Save global state bool fTestNet_stored = fTestNet; - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) CTrianglesSecret secret; CTrianglesAddress addr; - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); std::string strTest = write_string(tv, false); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index a0911bb..3185966 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -1,5 +1,4 @@ #include -#include #include #include "util.h" @@ -17,7 +16,7 @@ ResetArgs(const std::string& strArg) // Convert to char*: std::vector vecChar; - BOOST_FOREACH(std::string& s, vecArg) + for (std::string& s : vecArg) vecChar.push_back(s.c_str()); ParseParameters(vecChar.size(), &vecChar[0]); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 6bc5e3b..2871893 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -1,9 +1,5 @@ #include -#include -#include -#include #include -#include #include #include @@ -15,7 +11,6 @@ #include "wallet.h" using namespace std; -using namespace boost::assign; typedef vector valtype; @@ -32,7 +27,7 @@ sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, CScript result; result << OP_0; // CHECKMULTISIG bug workaround - BOOST_FOREACH(CKey key, keys) + for (CKey key : keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); @@ -78,19 +73,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) // Test a AND b: keys.clear(); - keys += key[0],key[1]; // magic operator+= from boost.assign + keys.push_back(key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, true, 0)); for (int i = 0; i < 4; i++) { keys.clear(); - keys += key[i]; + keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, 0), strprintf("a&b 1: %d", i)); keys.clear(); - keys += key[1],key[i]; + keys.push_back(key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, true, 0), strprintf("a&b 2: %d", i)); } @@ -99,7 +94,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify) for (int i = 0; i < 4; i++) { keys.clear(); - keys += key[i]; + keys.push_back(key[i]); s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, true, 0), strprintf("a|b: %d", i)); @@ -118,7 +113,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify) for (int j = 0; j < 4; j++) { keys.clear(); - keys += key[i],key[j]; + keys.push_back(key[i]); keys.push_back(key[j]); s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, true, 0), strprintf("escrow 1: %d %d", i, j)); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index c37aabb..44038b8 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,5 +1,4 @@ #include -#include #include "base58.h" #include "util.h" diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index eabfcd0..869d645 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -1,9 +1,5 @@ #include -#include -#include -#include #include -#include #include "../main.h" #include "../script.h" diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 61d9a64..9875da5 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include "json/json_spirit_reader_template.h" @@ -48,7 +47,7 @@ ParseScript(string s) vector words; split(words, s, is_any_of(" \t\n"), token_compress_on); - BOOST_FOREACH(string w, words) + for (string w : words) { if (all(w, is_digit()) || (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit()))) @@ -128,7 +127,7 @@ BOOST_AUTO_TEST_CASE(script_valid) // scripts. Array tests = read_json("script_valid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -152,7 +151,7 @@ BOOST_AUTO_TEST_CASE(script_invalid) // Scripts that should evaluate as invalid Array tests = read_json("script_invalid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -211,7 +210,7 @@ sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transac // and vice-versa) // result << OP_0; - BOOST_FOREACH(CKey key, keys) + for (CKey key : keys) { vector vchSig; BOOST_CHECK(key.Sign(hash, vchSig)); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 59673f9..a32a953 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "script.h" #include "key.h" diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c230458..2f43ae1 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // ... where all scripts are stringified scripts. Array tests = read_json("tx_valid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) map mapprevOutScriptPubKeys; Array inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { if (input.type() != array_type) { @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // ... where all scripts are stringified scripts. Array tests = read_json("tx_invalid.json"); - BOOST_FOREACH(Value& tv, tests) + for (Value& tv : tests) { Array test = tv.get_array(); string strTest = write_string(tv, false); @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) map mapprevOutScriptPubKeys; Array inputs = test[0].get_array(); bool fValid = true; - BOOST_FOREACH(Value& input, inputs) + for (Value& input : inputs) { if (input.type() != array_type) { diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 38f402b..c812993 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "main.h" #include "wallet.h" diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index d16fd1d..e6c433f 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -42,7 +42,7 @@ static void add_coin(int64 nValue, int nAge = 6*24, bool fIsFromMe = false, int static void empty_wallet(void) { - BOOST_FOREACH(COutput output, vCoins) + for (COutput output : vCoins) delete output.tx; vCoins.clear(); } diff --git a/src/tor/address.c b/src/tor/address.c deleted file mode 100644 index c30d2ab..0000000 --- a/src/tor/address.c +++ /dev/null @@ -1,1692 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file address.c - * \brief Functions to use and manipulate the tor_addr_t structure. - **/ - -#include "orconfig.h" -#include "tor_compat.h" -#include "tor_util.h" -#include "address.h" -#include "torlog.h" -#include "container.h" -#include "sandbox.h" - -#ifdef _WIN32 -#include -#include -#include -/* For access to structs needed by GetAdaptersAddresses */ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#include -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include /* FreeBSD needs this to know what version it is */ -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#ifdef HAVE_IFADDRS_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_NET_IF_H -#include -#endif -#include -#include -#include -#include -#include - -/* tor_addr_is_null() and maybe other functions rely on AF_UNSPEC being 0 to - * work correctly. Bail out here if we've found a platform where AF_UNSPEC - * isn't 0. */ -#if AF_UNSPEC != 0 -#error We rely on AF_UNSPEC being 0. Let us know about your platform, please! -#endif - -/** Convert the tor_addr_t in a, with port in port, into a - * sockaddr object in *sa_out of object size len. If not enough - * room is available in sa_out, or on error, return 0. On success, return - * the length of the sockaddr. - * - * Interface note: ordinarily, we return -1 for error. We can't do that here, - * since socklen_t is unsigned on some platforms. - **/ -socklen_t -tor_addr_to_sockaddr(const tor_addr_t *a, - uint16_t port, - struct sockaddr *sa_out, - socklen_t len) -{ - sa_family_t family = tor_addr_family(a); - if (family == AF_INET) { - struct sockaddr_in *sin; - if (len < (int)sizeof(struct sockaddr_in)) - return 0; - sin = (struct sockaddr_in *)sa_out; - memset(sin, 0, sizeof(struct sockaddr_in)); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin->sin_len = sizeof(struct sockaddr_in); -#endif - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - sin->sin_addr.s_addr = tor_addr_to_ipv4n(a); - return sizeof(struct sockaddr_in); - } else if (family == AF_INET6) { - struct sockaddr_in6 *sin6; - if (len < (int)sizeof(struct sockaddr_in6)) - return 0; - sin6 = (struct sockaddr_in6 *)sa_out; - memset(sin6, 0, sizeof(struct sockaddr_in6)); -#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof(struct sockaddr_in6); -#endif - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(port); - memcpy(&sin6->sin6_addr, tor_addr_to_in6(a), sizeof(struct in6_addr)); - return sizeof(struct sockaddr_in6); - } else { - return 0; - } -} - -/** Set the tor_addr_t in a to contain the socket address contained in - * sa. */ -int -tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, - uint16_t *port_out) -{ - tor_assert(a); - tor_assert(sa); - if (sa->sa_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *) sa; - tor_addr_from_ipv4n(a, sin->sin_addr.s_addr); - if (port_out) - *port_out = ntohs(sin->sin_port); - } else if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; - tor_addr_from_in6(a, &sin6->sin6_addr); - if (port_out) - *port_out = ntohs(sin6->sin6_port); - } else { - tor_addr_make_unspec(a); - return -1; - } - return 0; -} - -/** Return a newly allocated string holding the address described in - * sa. AF_UNIX, AF_UNSPEC, AF_INET, and AF_INET6 are supported. */ -char * -tor_sockaddr_to_str(const struct sockaddr *sa) -{ - char address[TOR_ADDR_BUF_LEN]; - char *result; - tor_addr_t addr; - uint16_t port; -#ifdef HAVE_SYS_UN_H - if (sa->sa_family == AF_UNIX) { - struct sockaddr_un *s_un = (struct sockaddr_un *)sa; - tor_asprintf(&result, "unix:%s", s_un->sun_path); - return result; - } -#endif - if (sa->sa_family == AF_UNSPEC) - return tor_strdup("unspec"); - - if (tor_addr_from_sockaddr(&addr, sa, &port) < 0) - return NULL; - if (! tor_addr_to_str(address, &addr, sizeof(address), 1)) - return NULL; - tor_asprintf(&result, "%s:%d", address, (int)port); - return result; -} - -/** Set address a to the unspecified address. This address belongs to - * no family. */ -void -tor_addr_make_unspec(tor_addr_t *a) -{ - memset(a, 0, sizeof(*a)); - a->family = AF_UNSPEC; -} - -/** Set address a to the null address in address family family. - * The null address for AF_INET is 0.0.0.0. The null address for AF_INET6 is - * [::]. AF_UNSPEC is all null. */ -void -tor_addr_make_null(tor_addr_t *a, sa_family_t family) -{ - memset(a, 0, sizeof(*a)); - a->family = family; -} - -/** Similar behavior to Unix gethostbyname: resolve name, and set - * *addr to the proper IP address and family. The family - * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a - * preferred family, though another one may be returned if only one - * family is implemented for this address. - * - * Return 0 on success, -1 on failure; 1 on transient failure. - */ -int -tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) -{ - /* Perhaps eventually this should be replaced by a tor_getaddrinfo or - * something. - */ - struct in_addr iaddr; - struct in6_addr iaddr6; - tor_assert(name); - tor_assert(addr); - tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC); - if (!*name) { - /* Empty address is an error. */ - return -1; - } else if (tor_inet_pton(AF_INET, name, &iaddr)) { - /* It's an IPv4 IP. */ - if (family == AF_INET6) - return -1; - tor_addr_from_in(addr, &iaddr); - return 0; - } else if (tor_inet_pton(AF_INET6, name, &iaddr6)) { - if (family == AF_INET) - return -1; - tor_addr_from_in6(addr, &iaddr6); - return 0; - } else { -#ifdef HAVE_GETADDRINFO - int err; - struct addrinfo *res=NULL, *res_p; - struct addrinfo *best=NULL; - struct addrinfo hints; - int result = -1; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - err = sandbox_getaddrinfo(name, NULL, &hints, &res); - if (!err) { - best = NULL; - for (res_p = res; res_p; res_p = res_p->ai_next) { - if (family == AF_UNSPEC) { - if (res_p->ai_family == AF_INET) { - best = res_p; - break; - } else if (res_p->ai_family == AF_INET6 && !best) { - best = res_p; - } - } else if (family == res_p->ai_family) { - best = res_p; - break; - } - } - if (!best) - best = res; - if (best->ai_family == AF_INET) { - tor_addr_from_in(addr, - &((struct sockaddr_in*)best->ai_addr)->sin_addr); - result = 0; - } else if (best->ai_family == AF_INET6) { - tor_addr_from_in6(addr, - &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); - result = 0; - } - freeaddrinfo(res); - return result; - } - return (err == EAI_AGAIN) ? 1 : -1; -#else - struct hostent *ent; - int err; -#ifdef HAVE_GETHOSTBYNAME_R_6_ARG - char buf[2048]; - struct hostent hostent; - int r; - r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); -#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) - char buf[2048]; - struct hostent hostent; - ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); -#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) - struct hostent_data data; - struct hostent hent; - memset(&data, 0, sizeof(data)); - err = gethostbyname_r(name, &hent, &data); - ent = err ? NULL : &hent; -#else - ent = gethostbyname(name); -#ifdef _WIN32 - err = WSAGetLastError(); -#else - err = h_errno; -#endif -#endif /* endif HAVE_GETHOSTBYNAME_R_6_ARG. */ - if (ent) { - if (ent->h_addrtype == AF_INET) { - tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); - } else if (ent->h_addrtype == AF_INET6) { - tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); - } else { - tor_assert(0); /* gethostbyname() returned a bizarre addrtype */ - } - return 0; - } -#ifdef _WIN32 - return (err == WSATRY_AGAIN) ? 1 : -1; -#else - return (err == TRY_AGAIN) ? 1 : -1; -#endif -#endif - } -} - -/** Return true iff ip is an IP reserved to localhost or local networks - * in RFC1918 or RFC4193 or RFC4291. (fec0::/10, deprecated by RFC3879, is - * also treated as internal for now.) - */ -int -tor_addr_is_internal_(const tor_addr_t *addr, int for_listening, - const char *filename, int lineno) -{ - uint32_t iph4 = 0; - uint32_t iph6[4]; - sa_family_t v_family; - v_family = tor_addr_family(addr); - - if (v_family == AF_INET) { - iph4 = tor_addr_to_ipv4h(addr); - } else if (v_family == AF_INET6) { - if (tor_addr_is_v4(addr)) { /* v4-mapped */ - v_family = AF_INET; - iph4 = ntohl(tor_addr_to_in6_addr32(addr)[3]); - } - } - - if (v_family == AF_INET6) { - const uint32_t *a32 = tor_addr_to_in6_addr32(addr); - iph6[0] = ntohl(a32[0]); - iph6[1] = ntohl(a32[1]); - iph6[2] = ntohl(a32[2]); - iph6[3] = ntohl(a32[3]); - if (for_listening && !iph6[0] && !iph6[1] && !iph6[2] && !iph6[3]) /* :: */ - return 0; - - if (((iph6[0] & 0xfe000000) == 0xfc000000) || /* fc00/7 - RFC4193 */ - ((iph6[0] & 0xffc00000) == 0xfe800000) || /* fe80/10 - RFC4291 */ - ((iph6[0] & 0xffc00000) == 0xfec00000)) /* fec0/10 D- RFC3879 */ - return 1; - - if (!iph6[0] && !iph6[1] && !iph6[2] && - ((iph6[3] & 0xfffffffe) == 0x00000000)) /* ::/127 */ - return 1; - - return 0; - } else if (v_family == AF_INET) { - if (for_listening && !iph4) /* special case for binding to 0.0.0.0 */ - return 0; - if (((iph4 & 0xff000000) == 0x0a000000) || /* 10/8 */ - ((iph4 & 0xff000000) == 0x00000000) || /* 0/8 */ - ((iph4 & 0xff000000) == 0x7f000000) || /* 127/8 */ - ((iph4 & 0xffff0000) == 0xa9fe0000) || /* 169.254/16 */ - ((iph4 & 0xfff00000) == 0xac100000) || /* 172.16/12 */ - ((iph4 & 0xffff0000) == 0xc0a80000)) /* 192.168/16 */ - return 1; - return 0; - } - - /* unknown address family... assume it's not safe for external use */ - /* rather than tor_assert(0) */ - log_warn(LD_BUG, "tor_addr_is_internal() called from %s:%d with a " - "non-IP address of type %d", filename, lineno, (int)v_family); - tor_fragile_assert(); - return 1; -} - -/** Convert a tor_addr_t addr into a string, and store it in - * dest of size len. Returns a pointer to dest on success, - * or NULL on failure. If decorate, surround IPv6 addresses with - * brackets. - */ -const char * -tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) -{ - const char *ptr; - tor_assert(addr && dest); - - switch (tor_addr_family(addr)) { - case AF_INET: - /* Shortest addr x.x.x.x + \0 */ - if (len < 8) - return NULL; - ptr = tor_inet_ntop(AF_INET, &addr->addr.in_addr, dest, len); - break; - case AF_INET6: - /* Shortest addr [ :: ] + \0 */ - if (len < (3 + (decorate ? 2 : 0))) - return NULL; - - if (decorate) - ptr = tor_inet_ntop(AF_INET6, &addr->addr.in6_addr, dest+1, len-2); - else - ptr = tor_inet_ntop(AF_INET6, &addr->addr.in6_addr, dest, len); - - if (ptr && decorate) { - *dest = '['; - memcpy(dest+strlen(dest), "]", 2); - tor_assert(ptr == dest+1); - ptr = dest; - } - break; - default: - return NULL; - } - return ptr; -} - -/** Parse an .in-addr.arpa or .ip6.arpa address from address. Return 0 - * if this is not an .in-addr.arpa address or an .ip6.arpa address. Return -1 - * if this is an ill-formed .in-addr.arpa address or an .ip6.arpa address. - * Also return -1 if family is not AF_UNSPEC, and the parsed address - * family does not match family. On success, return 1, and store the - * result, if any, into result, if provided. - * - * If accept_regular is set and the address is in neither recognized - * reverse lookup hostname format, try parsing the address as a regular - * IPv4 or IPv6 address too. - */ -int -tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, - int family, int accept_regular) -{ - if (!strcasecmpend(address, ".in-addr.arpa")) { - /* We have an in-addr.arpa address. */ - char buf[INET_NTOA_BUF_LEN]; - size_t len; - struct in_addr inaddr; - if (family == AF_INET6) - return -1; - - len = strlen(address) - strlen(".in-addr.arpa"); - if (len >= INET_NTOA_BUF_LEN) - return -1; /* Too long. */ - - memcpy(buf, address, len); - buf[len] = '\0'; - if (tor_inet_aton(buf, &inaddr) == 0) - return -1; /* malformed. */ - - /* reverse the bytes */ - inaddr.s_addr = (uint32_t) - (((inaddr.s_addr & 0x000000ff) << 24) - |((inaddr.s_addr & 0x0000ff00) << 8) - |((inaddr.s_addr & 0x00ff0000) >> 8) - |((inaddr.s_addr & 0xff000000) >> 24)); - - if (result) { - tor_addr_from_in(result, &inaddr); - } - return 1; - } - - if (!strcasecmpend(address, ".ip6.arpa")) { - const char *cp; - int i; - int n0, n1; - struct in6_addr in6; - - if (family == AF_INET) - return -1; - - cp = address; - for (i = 0; i < 16; ++i) { - n0 = hex_decode_digit(*cp++); /* The low-order nybble appears first. */ - if (*cp++ != '.') return -1; /* Then a dot. */ - n1 = hex_decode_digit(*cp++); /* The high-order nybble appears first. */ - if (*cp++ != '.') return -1; /* Then another dot. */ - if (n0<0 || n1 < 0) /* Both nybbles must be hex. */ - return -1; - - /* We don't check the length of the string in here. But that's okay, - * since we already know that the string ends with ".ip6.arpa", and - * there is no way to frameshift .ip6.arpa so it fits into the pattern - * of hexdigit, period, hexdigit, period that we enforce above. - */ - - /* Assign from low-byte to high-byte. */ - in6.s6_addr[15-i] = n0 | (n1 << 4); - } - if (strcasecmp(cp, "ip6.arpa")) - return -1; - - if (result) { - tor_addr_from_in6(result, &in6); - } - return 1; - } - - if (accept_regular) { - tor_addr_t tmp; - int r = tor_addr_parse(&tmp, address); - if (r < 0) - return 0; - if (r != family && family != AF_UNSPEC) - return -1; - - if (result) - memcpy(result, &tmp, sizeof(tor_addr_t)); - - return 1; - } - - return 0; -} - -/** Convert addr to an in-addr.arpa name or a .ip6.arpa name, - * and store the result in the outlen-byte buffer at - * out. Return the number of chars written to out, not - * including the trailing \0, on success. Returns -1 on failure. */ -int -tor_addr_to_PTR_name(char *out, size_t outlen, - const tor_addr_t *addr) -{ - tor_assert(out); - tor_assert(addr); - - if (addr->family == AF_INET) { - uint32_t a = tor_addr_to_ipv4h(addr); - - return tor_snprintf(out, outlen, "%d.%d.%d.%d.in-addr.arpa", - (int)(uint8_t)((a )&0xff), - (int)(uint8_t)((a>>8 )&0xff), - (int)(uint8_t)((a>>16)&0xff), - (int)(uint8_t)((a>>24)&0xff)); - } else if (addr->family == AF_INET6) { - int i; - char *cp = out; - const uint8_t *bytes = tor_addr_to_in6_addr8(addr); - if (outlen < REVERSE_LOOKUP_NAME_BUF_LEN) - return -1; - for (i = 15; i >= 0; --i) { - uint8_t byte = bytes[i]; - *cp++ = "0123456789abcdef"[byte & 0x0f]; - *cp++ = '.'; - *cp++ = "0123456789abcdef"[byte >> 4]; - *cp++ = '.'; - } - memcpy(cp, "ip6.arpa", 9); /* 8 characters plus NUL */ - return 32 * 2 + 8; - } - return -1; -} - -/** Parse a string s containing an IPv4/IPv6 address, and possibly - * a mask and port or port range. Store the parsed address in - * addr_out, a mask (if any) in mask_out, and port(s) (if any) - * in port_min_out and port_max_out. - * - * The syntax is: - * Address OptMask OptPortRange - * Address ::= IPv4Address / "[" IPv6Address "]" / "*" - * OptMask ::= "/" Integer / - * OptPortRange ::= ":*" / ":" Integer / ":" Integer "-" Integer / - * - * - If mask, minport, or maxport are NULL, we do not want these - * options to be set; treat them as an error if present. - * - If the string has no mask, the mask is set to /32 (IPv4) or /128 (IPv6). - * - If the string has one port, it is placed in both min and max port - * variables. - * - If the string has no port(s), port_(min|max)_out are set to 1 and 65535. - * - * Return an address family on success, or -1 if an invalid address string is - * provided. - * - * If 'flags & TAPMP_EXTENDED_STAR' is false, then the wildcard address '*' - * yield an IPv4 wildcard. - * - * If 'flags & TAPMP_EXTENDED_STAR' is true, then the wildcard address '*' - * yields an AF_UNSPEC wildcard address, and the following change is made - * in the grammar above: - * Address ::= IPv4Address / "[" IPv6Address "]" / "*" / "*4" / "*6" - * with the new "*4" and "*6" productions creating a wildcard to match - * IPv4 or IPv6 addresses. - * - */ -int -tor_addr_parse_mask_ports(const char *s, - unsigned flags, - tor_addr_t *addr_out, - maskbits_t *maskbits_out, - uint16_t *port_min_out, uint16_t *port_max_out) -{ - char *base = NULL, *address, *mask = NULL, *port = NULL, *rbracket = NULL; - char *endptr; - int any_flag=0, v4map=0; - sa_family_t family; - struct in6_addr in6_tmp; - struct in_addr in_tmp; - - tor_assert(s); - tor_assert(addr_out); - - /** Longest possible length for an address, mask, and port-range combination. - * Includes IP, [], /mask, :, ports */ -#define MAX_ADDRESS_LENGTH (TOR_ADDR_BUF_LEN+2+(1+INET_NTOA_BUF_LEN)+12+1) - - if (strlen(s) > MAX_ADDRESS_LENGTH) { - log_warn(LD_GENERAL, "Impossibly long IP %s; rejecting", escaped(s)); - goto err; - } - base = tor_strdup(s); - - /* Break 'base' into separate strings. */ - address = base; - if (*address == '[') { /* Probably IPv6 */ - address++; - rbracket = strchr(address, ']'); - if (!rbracket) { - log_warn(LD_GENERAL, - "No closing IPv6 bracket in address pattern; rejecting."); - goto err; - } - } - mask = strchr((rbracket?rbracket:address),'/'); - port = strchr((mask?mask:(rbracket?rbracket:address)), ':'); - if (port) - *port++ = '\0'; - if (mask) - *mask++ = '\0'; - if (rbracket) - *rbracket = '\0'; - if (port && mask) - tor_assert(port > mask); - if (mask && rbracket) - tor_assert(mask > rbracket); - - /* Now "address" is the a.b.c.d|'*'|abcd::1 part... - * "mask" is the Mask|Maskbits part... - * and "port" is the *|port|min-max part. - */ - - /* Process the address portion */ - memset(addr_out, 0, sizeof(tor_addr_t)); - - if (!strcmp(address, "*")) { - if (flags & TAPMP_EXTENDED_STAR) { - family = AF_UNSPEC; - tor_addr_make_unspec(addr_out); - } else { - family = AF_INET; - tor_addr_from_ipv4h(addr_out, 0); - } - any_flag = 1; - } else if (!strcmp(address, "*4") && (flags & TAPMP_EXTENDED_STAR)) { - family = AF_INET; - tor_addr_from_ipv4h(addr_out, 0); - any_flag = 1; - } else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) { - static char nil_bytes[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; - family = AF_INET6; - tor_addr_from_ipv6_bytes(addr_out, nil_bytes); - any_flag = 1; - } else if (tor_inet_pton(AF_INET6, address, &in6_tmp) > 0) { - family = AF_INET6; - tor_addr_from_in6(addr_out, &in6_tmp); - } else if (tor_inet_pton(AF_INET, address, &in_tmp) > 0) { - family = AF_INET; - tor_addr_from_in(addr_out, &in_tmp); - } else { - log_warn(LD_GENERAL, "Malformed IP %s in address pattern; rejecting.", - escaped(address)); - goto err; - } - - v4map = tor_addr_is_v4(addr_out); - - /* Parse mask */ - if (maskbits_out) { - int bits = 0; - struct in_addr v4mask; - - if (mask) { /* the caller (tried to) specify a mask */ - bits = (int) strtol(mask, &endptr, 10); - if (!*endptr) { /* strtol converted everything, so it was an integer */ - if ((bits<0 || bits>128) || - (family == AF_INET && bits > 32)) { - log_warn(LD_GENERAL, - "Bad number of mask bits (%d) on address range; rejecting.", - bits); - goto err; - } - } else { /* mask might still be an address-style mask */ - if (tor_inet_pton(AF_INET, mask, &v4mask) > 0) { - bits = addr_mask_get_bits(ntohl(v4mask.s_addr)); - if (bits < 0) { - log_warn(LD_GENERAL, - "IPv4-style mask %s is not a prefix address; rejecting.", - escaped(mask)); - goto err; - } - } else { /* Not IPv4; we don't do address-style IPv6 masks. */ - log_warn(LD_GENERAL, - "Malformed mask on address range %s; rejecting.", - escaped(s)); - goto err; - } - } - if (family == AF_INET6 && v4map) { - if (bits > 32 && bits < 96) { /* Crazy */ - log_warn(LD_GENERAL, - "Bad mask bits %d for V4-mapped V6 address; rejecting.", - bits); - goto err; - } - /* XXXX_IP6 is this really what we want? */ - bits = 96 + bits%32; /* map v4-mapped masks onto 96-128 bits */ - } - } else { /* pick an appropriate mask, as none was given */ - if (any_flag) - bits = 0; /* This is okay whether it's V6 or V4 (FIX V4-mapped V6!) */ - else if (tor_addr_family(addr_out) == AF_INET) - bits = 32; - else if (tor_addr_family(addr_out) == AF_INET6) - bits = 128; - } - *maskbits_out = (maskbits_t) bits; - } else { - if (mask) { - log_warn(LD_GENERAL, - "Unexpected mask in address %s; rejecting", escaped(s)); - goto err; - } - } - - /* Parse port(s) */ - if (port_min_out) { - uint16_t port2; - if (!port_max_out) /* caller specified one port; fake the second one */ - port_max_out = &port2; - - if (parse_port_range(port, port_min_out, port_max_out) < 0) { - goto err; - } else if ((*port_min_out != *port_max_out) && port_max_out == &port2) { - log_warn(LD_GENERAL, - "Wanted one port from address range, but there are two."); - - port_max_out = NULL; /* caller specified one port, so set this back */ - goto err; - } - } else { - if (port) { - log_warn(LD_GENERAL, - "Unexpected ports in address %s; rejecting", escaped(s)); - goto err; - } - } - - tor_free(base); - return tor_addr_family(addr_out); - err: - tor_free(base); - return -1; -} - -/** Determine whether an address is IPv4, either native or IPv4-mapped IPv6. - * Note that this is about representation only, as any decent stack will - * reject IPv4-mapped addresses received on the wire (and won't use them - * on the wire either). - */ -int -tor_addr_is_v4(const tor_addr_t *addr) -{ - tor_assert(addr); - - if (tor_addr_family(addr) == AF_INET) - return 1; - - if (tor_addr_family(addr) == AF_INET6) { - /* First two don't need to be ordered */ - uint32_t *a32 = tor_addr_to_in6_addr32(addr); - if (a32[0] == 0 && a32[1] == 0 && ntohl(a32[2]) == 0x0000ffffu) - return 1; - } - - return 0; /* Not IPv4 - unknown family or a full-blood IPv6 address */ -} - -/** Determine whether an address addr is null, either all zeroes or - * belonging to family AF_UNSPEC. - */ -int -tor_addr_is_null(const tor_addr_t *addr) -{ - tor_assert(addr); - - switch (tor_addr_family(addr)) { - case AF_INET6: { - uint32_t *a32 = tor_addr_to_in6_addr32(addr); - return (a32[0] == 0) && (a32[1] == 0) && (a32[2] == 0) && (a32[3] == 0); - } - case AF_INET: - return (tor_addr_to_ipv4n(addr) == 0); - case AF_UNSPEC: - return 1; - default: - log_warn(LD_BUG, "Called with unknown address family %d", - (int)tor_addr_family(addr)); - return 0; - } - //return 1; -} - -/** Return true iff addr is a loopback address */ -int -tor_addr_is_loopback(const tor_addr_t *addr) -{ - tor_assert(addr); - switch (tor_addr_family(addr)) { - case AF_INET6: { - /* ::1 */ - uint32_t *a32 = tor_addr_to_in6_addr32(addr); - return (a32[0] == 0) && (a32[1] == 0) && (a32[2] == 0) && - (ntohl(a32[3]) == 1); - } - case AF_INET: - /* 127.0.0.1 */ - return (tor_addr_to_ipv4h(addr) & 0xff000000) == 0x7f000000; - case AF_UNSPEC: - return 0; - default: - tor_fragile_assert(); - return 0; - } -} - -/** Set dest to equal the IPv4 address in v4addr (given in - * network order). */ -void -tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr) -{ - tor_assert(dest); - memset(dest, 0, sizeof(tor_addr_t)); - dest->family = AF_INET; - dest->addr.in_addr.s_addr = v4addr; -} - -/** Set dest to equal the IPv6 address in the 16 bytes at - * ipv6_bytes. */ -void -tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *ipv6_bytes) -{ - tor_assert(dest); - tor_assert(ipv6_bytes); - memset(dest, 0, sizeof(tor_addr_t)); - dest->family = AF_INET6; - memcpy(dest->addr.in6_addr.s6_addr, ipv6_bytes, 16); -} - -/** Set dest equal to the IPv6 address in the in6_addr in6. */ -void -tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6) -{ - tor_addr_from_ipv6_bytes(dest, (const char*)in6->s6_addr); -} - -/** Copy a tor_addr_t from src to dest. - */ -void -tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src) -{ - if (src == dest) - return; - tor_assert(src); - tor_assert(dest); - memcpy(dest, src, sizeof(tor_addr_t)); -} - -/** Given two addresses addr1 and addr2, return 0 if the two - * addresses are equivalent under the mask mbits, less than 0 if addr1 - * precedes addr2, and greater than 0 otherwise. - * - * Different address families (IPv4 vs IPv6) are always considered unequal if - * how is CMP_EXACT; otherwise, IPv6-mapped IPv4 addresses are - * considered equivalent to their IPv4 equivalents. - */ -int -tor_addr_compare(const tor_addr_t *addr1, const tor_addr_t *addr2, - tor_addr_comparison_t how) -{ - return tor_addr_compare_masked(addr1, addr2, 128, how); -} - -/** As tor_addr_compare(), but only looks at the first mask bits of - * the address. - * - * Reduce over-specific masks (>128 for ipv6, >32 for ipv4) to 128 or 32. - * - * The mask is interpreted relative to addr1, so that if a is - * \::ffff:1.2.3.4, and b is 3.4.5.6, - * tor_addr_compare_masked(a,b,100,CMP_SEMANTIC) is the same as - * -tor_addr_compare_masked(b,a,4,CMP_SEMANTIC). - * - * We guarantee that the ordering from tor_addr_compare_masked is a total - * order on addresses, but not that it is any particular order, or that it - * will be the same from one version to the next. - */ -int -tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, - maskbits_t mbits, tor_addr_comparison_t how) -{ - /** Helper: Evaluates to -1 if a is less than b, 0 if a equals b, or 1 if a - * is greater than b. May evaluate a and b more than once. */ -#define TRISTATE(a,b) (((a)<(b))?-1: (((a)==(b))?0:1)) - sa_family_t family1, family2, v_family1, v_family2; - - tor_assert(addr1 && addr2); - - v_family1 = family1 = tor_addr_family(addr1); - v_family2 = family2 = tor_addr_family(addr2); - - if (family1==family2) { - /* When the families are the same, there's only one way to do the - * comparison: exactly. */ - int r; - switch (family1) { - case AF_UNSPEC: - return 0; /* All unspecified addresses are equal */ - case AF_INET: { - uint32_t a1 = tor_addr_to_ipv4h(addr1); - uint32_t a2 = tor_addr_to_ipv4h(addr2); - if (mbits <= 0) - return 0; - if (mbits > 32) - mbits = 32; - a1 >>= (32-mbits); - a2 >>= (32-mbits); - r = TRISTATE(a1, a2); - return r; - } - case AF_INET6: { - const uint8_t *a1 = tor_addr_to_in6_addr8(addr1); - const uint8_t *a2 = tor_addr_to_in6_addr8(addr2); - const int bytes = mbits >> 3; - const int leftover_bits = mbits & 7; - if (bytes && (r = tor_memcmp(a1, a2, bytes))) { - return r; - } else if (leftover_bits) { - uint8_t b1 = a1[bytes] >> (8-leftover_bits); - uint8_t b2 = a2[bytes] >> (8-leftover_bits); - return TRISTATE(b1, b2); - } else { - return 0; - } - } - default: - tor_fragile_assert(); - return 0; - } - } else if (how == CMP_EXACT) { - /* Unequal families and an exact comparison? Stop now! */ - return TRISTATE(family1, family2); - } - - if (mbits == 0) - return 0; - - if (family1 == AF_INET6 && tor_addr_is_v4(addr1)) - v_family1 = AF_INET; - if (family2 == AF_INET6 && tor_addr_is_v4(addr2)) - v_family2 = AF_INET; - if (v_family1 == v_family2) { - /* One or both addresses are a mapped ipv4 address. */ - uint32_t a1, a2; - if (family1 == AF_INET6) { - a1 = tor_addr_to_mapped_ipv4h(addr1); - if (mbits <= 96) - return 0; - mbits -= 96; /* We just decided that the first 96 bits of a1 "match". */ - } else { - a1 = tor_addr_to_ipv4h(addr1); - } - if (family2 == AF_INET6) { - a2 = tor_addr_to_mapped_ipv4h(addr2); - } else { - a2 = tor_addr_to_ipv4h(addr2); - } - if (mbits <= 0) return 0; - if (mbits > 32) mbits = 32; - a1 >>= (32-mbits); - a2 >>= (32-mbits); - return TRISTATE(a1, a2); - } else { - /* Unequal families, and semantic comparison, and no semantic family - * matches. */ - return TRISTATE(family1, family2); - } -} - -/** Return a hash code based on the address addr */ -unsigned int -tor_addr_hash(const tor_addr_t *addr) -{ - switch (tor_addr_family(addr)) { - case AF_INET: - return tor_addr_to_ipv4h(addr); - case AF_UNSPEC: - return 0x4e4d5342; - case AF_INET6: { - const uint32_t *u = tor_addr_to_in6_addr32(addr); - return u[0] + u[1] + u[2] + u[3]; - } - default: - tor_fragile_assert(); - return 0; - } -} - -/** Return a newly allocated string with a representation of addr. */ -char * -tor_dup_addr(const tor_addr_t *addr) -{ - char buf[TOR_ADDR_BUF_LEN]; - if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) { - return tor_strdup(buf); - } else { - return tor_strdup(""); - } -} - -/** Return a string representing the address addr. This string - * is statically allocated, and must not be freed. Each call to - * fmt_addr_impl invalidates the last result of the function. - * This function is not thread-safe. If decorate is set, add - * brackets to IPv6 addresses. - * - * It's better to use the wrapper macros of this function: - * fmt_addr() and fmt_and_decorate_addr(). - */ -const char * -fmt_addr_impl(const tor_addr_t *addr, int decorate) -{ - static char buf[TOR_ADDR_BUF_LEN]; - if (!addr) return ""; - if (tor_addr_to_str(buf, addr, sizeof(buf), decorate)) - return buf; - else - return "???"; -} - -/** Return a string representing the pair addr and port. - * This calls fmt_and_decorate_addr internally, so IPv6 addresses will - * have brackets, and the caveats of fmt_addr_impl apply. - */ -const char * -fmt_addrport(const tor_addr_t *addr, uint16_t port) -{ - /* Add space for a colon and up to 5 digits. */ - static char buf[TOR_ADDR_BUF_LEN + 6]; - tor_snprintf(buf, sizeof(buf), "%s:%u", fmt_and_decorate_addr(addr), port); - return buf; -} - -/** Like fmt_addr(), but takes addr as a host-order IPv4 - * addresses. Also not thread-safe, also clobbers its return buffer on - * repeated calls. */ -const char * -fmt_addr32(uint32_t addr) -{ - static char buf[INET_NTOA_BUF_LEN]; - struct in_addr in; - in.s_addr = htonl(addr); - tor_inet_ntoa(&in, buf, sizeof(buf)); - return buf; -} - -/** Convert the string in src to a tor_addr_t addr. The string - * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by - * square brackets. - * - * Return an address family on success, or -1 if an invalid address string is - * provided. */ -int -tor_addr_parse(tor_addr_t *addr, const char *src) -{ - char *tmp = NULL; /* Holds substring if we got a dotted quad. */ - int result; - struct in_addr in_tmp; - struct in6_addr in6_tmp; - tor_assert(addr && src); - if (src[0] == '[' && src[1]) - src = tmp = tor_strndup(src+1, strlen(src)-2); - - if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { - result = AF_INET6; - tor_addr_from_in6(addr, &in6_tmp); - } else if (tor_inet_pton(AF_INET, src, &in_tmp) > 0) { - result = AF_INET; - tor_addr_from_in(addr, &in_tmp); - } else { - result = -1; - } - - tor_free(tmp); - return result; -} - -/** Parse an address or address-port combination from s, resolve the - * address as needed, and put the result in addr_out and (optionally) - * port_out. Return 0 on success, negative on failure. */ -int -tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) -{ - const char *port; - tor_addr_t addr; - uint16_t portval; - char *tmp = NULL; - - tor_assert(s); - tor_assert(addr_out); - - s = eat_whitespace(s); - - if (*s == '[') { - port = strstr(s, "]"); - if (!port) - goto err; - tmp = tor_strndup(s+1, port-(s+1)); - port = port+1; - if (*port == ':') - port++; - else - port = NULL; - } else { - port = strchr(s, ':'); - if (port) - tmp = tor_strndup(s, port-s); - else - tmp = tor_strdup(s); - if (port) - ++port; - } - - if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) - goto err; - tor_free(tmp); - - if (port) { - portval = (int) tor_parse_long(port, 10, 1, 65535, NULL, NULL); - if (!portval) - goto err; - } else { - portval = 0; - } - - if (port_out) - *port_out = portval; - tor_addr_copy(addr_out, &addr); - - return 0; - err: - tor_free(tmp); - return -1; -} - -#ifdef _WIN32 -typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)( - ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); -#endif - -/** Try to ask our network interfaces what addresses they are bound to. - * Return a new smartlist of tor_addr_t on success, and NULL on failure. - * (An empty smartlist indicates that we successfully learned that we have no - * addresses.) Log failure messages at severity. */ -static smartlist_t * -get_interface_addresses_raw(int severity) -{ -#if defined(HAVE_GETIFADDRS) - /* Most free Unixy systems provide getifaddrs, which gives us a linked list - * of struct ifaddrs. */ - struct ifaddrs *ifa = NULL; - const struct ifaddrs *i; - smartlist_t *result; - if (getifaddrs(&ifa) < 0) { - log_fn(severity, LD_NET, "Unable to call getifaddrs(): %s", - strerror(errno)); - return NULL; - } - - result = smartlist_new(); - for (i = ifa; i; i = i->ifa_next) { - tor_addr_t tmp; - if ((i->ifa_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) - continue; - if (!i->ifa_addr) - continue; - if (i->ifa_addr->sa_family != AF_INET && - i->ifa_addr->sa_family != AF_INET6) - continue; - if (tor_addr_from_sockaddr(&tmp, i->ifa_addr, NULL) < 0) - continue; - smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); - } - - freeifaddrs(ifa); - return result; -#elif defined(_WIN32) - /* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a - "GetAdaptersInfo", but that's deprecated; let's just try - GetAdaptersAddresses and fall back to connect+getsockname. - */ - HANDLE lib = load_windows_system_library(TEXT("iphlpapi.dll")); - smartlist_t *result = NULL; - GetAdaptersAddresses_fn_t fn; - ULONG size, res; - IP_ADAPTER_ADDRESSES *addresses = NULL, *address; - - (void) severity; - -#define FLAGS (GAA_FLAG_SKIP_ANYCAST | \ - GAA_FLAG_SKIP_MULTICAST | \ - GAA_FLAG_SKIP_DNS_SERVER) - - if (!lib) { - log_fn(severity, LD_NET, "Unable to load iphlpapi.dll"); - goto done; - } - - if (!(fn = (GetAdaptersAddresses_fn_t) - GetProcAddress(lib, "GetAdaptersAddresses"))) { - log_fn(severity, LD_NET, "Unable to obtain pointer to " - "GetAdaptersAddresses"); - goto done; - } - - /* Guess how much space we need. */ - size = 15*1024; - addresses = tor_malloc(size); - res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); - if (res == ERROR_BUFFER_OVERFLOW) { - /* we didn't guess that we needed enough space; try again */ - tor_free(addresses); - addresses = tor_malloc(size); - res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); - } - if (res != NO_ERROR) { - log_fn(severity, LD_NET, "GetAdaptersAddresses failed (result: %lu)", res); - goto done; - } - - result = smartlist_new(); - for (address = addresses; address; address = address->Next) { - IP_ADAPTER_UNICAST_ADDRESS *a; - for (a = address->FirstUnicastAddress; a; a = a->Next) { - /* Yes, it's a linked list inside a linked list */ - struct sockaddr *sa = a->Address.lpSockaddr; - tor_addr_t tmp; - if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) - continue; - if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0) - continue; - smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); - } - } - - done: - if (lib) - FreeLibrary(lib); - tor_free(addresses); - return result; -#elif defined(SIOCGIFCONF) && defined(HAVE_IOCTL) - /* Some older unixy systems make us use ioctl(SIOCGIFCONF) */ - struct ifconf ifc; - int fd, i, sz, n; - smartlist_t *result = NULL; - /* This interface, AFAICT, only supports AF_INET addresses */ - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - tor_log(severity, LD_NET, "socket failed: %s", strerror(errno)); - goto done; - } - /* Guess how much space we need. */ - ifc.ifc_len = sz = 15*1024; - ifc.ifc_ifcu.ifcu_req = tor_malloc(sz); - if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { - tor_log(severity, LD_NET, "ioctl failed: %s", strerror(errno)); - close(fd); - goto done; - } - close(fd); - result = smartlist_new(); - if (ifc.ifc_len < sz) - sz = ifc.ifc_len; - n = sz / sizeof(struct ifreq); - for (i = 0; i < n ; ++i) { - struct ifreq *r = &ifc.ifc_ifcu.ifcu_req[i]; - struct sockaddr *sa = &r->ifr_addr; - tor_addr_t tmp; - if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) - continue; /* should be impossible */ - if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0) - continue; - smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); - } - done: - tor_free(ifc.ifc_ifcu.ifcu_req); - return result; -#else - (void) severity; - return NULL; -#endif -} - -/** Return true iff a is a multicast address. */ -static int -tor_addr_is_multicast(const tor_addr_t *a) -{ - sa_family_t family = tor_addr_family(a); - if (family == AF_INET) { - uint32_t ipv4h = tor_addr_to_ipv4h(a); - if ((ipv4h >> 24) == 0xe0) - return 1; /* Multicast */ - } else if (family == AF_INET6) { - const uint8_t *a32 = tor_addr_to_in6_addr8(a); - if (a32[0] == 0xff) - return 1; - } - return 0; -} - -/** Set *addr to the IP address (if any) of whatever interface - * connects to the Internet. This address should only be used in checking - * whether our address has changed. Return 0 on success, -1 on failure. - */ -int -get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr) -{ - /* XXX really, this function should yield a smartlist of addresses. */ - smartlist_t *addrs; - int sock=-1, r=-1; - struct sockaddr_storage my_addr, target_addr; - socklen_t addr_len; - tor_assert(addr); - - /* Try to do this the smart way if possible. */ - if ((addrs = get_interface_addresses_raw(severity))) { - int rv = -1; - SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { - if (family != AF_UNSPEC && family != tor_addr_family(a)) - continue; - if (tor_addr_is_loopback(a) || - tor_addr_is_multicast(a)) - continue; - - tor_addr_copy(addr, a); - rv = 0; - - /* If we found a non-internal address, declare success. Otherwise, - * keep looking. */ - if (!tor_addr_is_internal(a, 0)) - break; - } SMARTLIST_FOREACH_END(a); - - SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); - smartlist_free(addrs); - return rv; - } - - /* Okay, the smart way is out. */ - memset(addr, 0, sizeof(tor_addr_t)); - memset(&target_addr, 0, sizeof(target_addr)); - /* Don't worry: no packets are sent. We just need to use a real address - * on the actual Internet. */ - if (family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&target_addr; - /* Use the "discard" service port */ - sin6->sin6_port = htons(9); - sock = tor_open_socket(PF_INET6,SOCK_DGRAM,IPPROTO_UDP); - addr_len = (socklen_t)sizeof(struct sockaddr_in6); - sin6->sin6_family = AF_INET6; - S6_ADDR16(sin6->sin6_addr)[0] = htons(0x2002); /* 2002:: */ - } else if (family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in*)&target_addr; - /* Use the "discard" service port */ - sin->sin_port = htons(9); - sock = tor_open_socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); - addr_len = (socklen_t)sizeof(struct sockaddr_in); - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(0x12000001); /* 18.0.0.1 */ - } else { - return -1; - } - if (sock < 0) { - int e = tor_socket_errno(-1); - log_fn(severity, LD_NET, "unable to create socket: %s", - tor_socket_strerror(e)); - goto err; - } - - if (connect(sock,(struct sockaddr *)&target_addr, addr_len) < 0) { - int e = tor_socket_errno(sock); - log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e)); - goto err; - } - - if (getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) { - int e = tor_socket_errno(sock); - log_fn(severity, LD_NET, "getsockname() to determine interface failed: %s", - tor_socket_strerror(e)); - goto err; - } - - tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL); - r=0; - err: - if (sock >= 0) - tor_close_socket(sock); - return r; -} - -/* ====== - * IPv4 helpers - * XXXX024 IPv6 deprecate some of these. - */ - -/** Return true iff ip (in host order) is an IP reserved to localhost, - * or reserved for local networks by RFC 1918. - */ -int -is_internal_IP(uint32_t ip, int for_listening) -{ - tor_addr_t myaddr; - myaddr.family = AF_INET; - myaddr.addr.in_addr.s_addr = htonl(ip); - - return tor_addr_is_internal(&myaddr, for_listening); -} - -/** Given an address of the form "ip:port", try to divide it into its - * ip and port portions, setting *address_out to a newly - * allocated string holding the address portion and *port_out - * to the port. - * - * Don't do DNS lookups and don't allow domain names in the field. - * Don't accept addrport of the form "" or ":0". - * - * Return 0 on success, -1 on failure. */ -int -tor_addr_port_parse(int severity, const char *addrport, - tor_addr_t *address_out, uint16_t *port_out) -{ - int retval = -1; - int r; - char *addr_tmp = NULL; - - tor_assert(addrport); - tor_assert(address_out); - tor_assert(port_out); - - r = tor_addr_port_split(severity, addrport, &addr_tmp, port_out); - if (r < 0) - goto done; - - if (!*port_out) - goto done; - - /* make sure that address_out is an IP address */ - if (tor_addr_parse(address_out, addr_tmp) < 0) - goto done; - - retval = 0; - - done: - tor_free(addr_tmp); - return retval; -} - -/** Given an address of the form "host[:port]", try to divide it into its host - * ane port portions, setting *address_out to a newly allocated string - * holding the address portion and *port_out to the port (or 0 if no - * port is given). Return 0 on success, -1 on failure. */ -int -tor_addr_port_split(int severity, const char *addrport, - char **address_out, uint16_t *port_out) -{ - tor_assert(addrport); - tor_assert(address_out); - tor_assert(port_out); - return addr_port_lookup(severity, addrport, address_out, NULL, port_out); -} - -/** Parse a string of the form "host[:port]" from addrport. If - * address is provided, set *address to a copy of the - * host portion of the string. If addr is provided, try to - * resolve the host portion of the string and store it into - * *addr (in host byte order). If port_out is provided, - * store the port number into *port_out, or 0 if no port is given. - * If port_out is NULL, then there must be no port number in - * addrport. - * Return 0 on success, -1 on failure. - */ -int -addr_port_lookup(int severity, const char *addrport, char **address, - uint32_t *addr, uint16_t *port_out) -{ - const char *colon; - char *address_ = NULL; - int port_; - int ok = 1; - - tor_assert(addrport); - - colon = strrchr(addrport, ':'); - if (colon) { - address_ = tor_strndup(addrport, colon-addrport); - port_ = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL); - if (!port_) { - log_fn(severity, LD_GENERAL, "Port %s out of range", escaped(colon+1)); - ok = 0; - } - if (!port_out) { - char *esc_addrport = esc_for_log(addrport); - log_fn(severity, LD_GENERAL, - "Port %s given on %s when not required", - escaped(colon+1), esc_addrport); - tor_free(esc_addrport); - ok = 0; - } - } else { - address_ = tor_strdup(addrport); - port_ = 0; - } - - if (addr) { - /* There's an addr pointer, so we need to resolve the hostname. */ - if (tor_lookup_hostname(address_,addr)) { - log_fn(severity, LD_NET, "Couldn't look up %s", escaped(address_)); - ok = 0; - *addr = 0; - } - } - - if (address && ok) { - *address = address_; - } else { - if (address) - *address = NULL; - tor_free(address_); - } - if (port_out) - *port_out = ok ? ((uint16_t) port_) : 0; - - return ok ? 0 : -1; -} - -/** If mask is an address mask for a bit-prefix, return the number of - * bits. Otherwise, return -1. */ -int -addr_mask_get_bits(uint32_t mask) -{ - int i; - if (mask == 0) - return 0; - if (mask == 0xFFFFFFFFu) - return 32; - for (i=0; i<=32; ++i) { - if (mask == (uint32_t) ~((1u<<(32-i))-1)) { - return i; - } - } - return -1; -} - -/** Parse a string s in the format of (*|port(-maxport)?)?, setting the - * various *out pointers as appropriate. Return 0 on success, -1 on failure. - */ -int -parse_port_range(const char *port, uint16_t *port_min_out, - uint16_t *port_max_out) -{ - int port_min, port_max, ok; - tor_assert(port_min_out); - tor_assert(port_max_out); - - if (!port || *port == '\0' || strcmp(port, "*") == 0) { - port_min = 1; - port_max = 65535; - } else { - char *endptr = NULL; - port_min = (int)tor_parse_long(port, 10, 0, 65535, &ok, &endptr); - if (!ok) { - log_warn(LD_GENERAL, - "Malformed port %s on address range; rejecting.", - escaped(port)); - return -1; - } else if (endptr && *endptr == '-') { - port = endptr+1; - endptr = NULL; - port_max = (int)tor_parse_long(port, 10, 1, 65535, &ok, &endptr); - if (!ok) { - log_warn(LD_GENERAL, - "Malformed port %s on address range; rejecting.", - escaped(port)); - return -1; - } - } else { - port_max = port_min; - } - if (port_min > port_max) { - log_warn(LD_GENERAL, "Insane port range on address policy; rejecting."); - return -1; - } - } - - if (port_min < 1) - port_min = 1; - if (port_max > 65535) - port_max = 65535; - - *port_min_out = (uint16_t) port_min; - *port_max_out = (uint16_t) port_max; - - return 0; -} - -/** Given an IPv4 in_addr struct *in (in network order, as usual), - * write it as a string into the buf_len-byte buffer in - * buf. - */ -int -tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len) -{ - uint32_t a = ntohl(in->s_addr); - return tor_snprintf(buf, buf_len, "%d.%d.%d.%d", - (int)(uint8_t)((a>>24)&0xff), - (int)(uint8_t)((a>>16)&0xff), - (int)(uint8_t)((a>>8 )&0xff), - (int)(uint8_t)((a )&0xff)); -} - -/** Given a host-order addr, call tor_inet_ntop() on it - * and return a strdup of the resulting address. - */ -char * -tor_dup_ip(uint32_t addr) -{ - char buf[TOR_ADDR_BUF_LEN]; - struct in_addr in; - - in.s_addr = htonl(addr); - tor_inet_ntop(AF_INET, &in, buf, sizeof(buf)); - return tor_strdup(buf); -} - -/** - * Set *addr to the host-order IPv4 address (if any) of whatever - * interface connects to the Internet. This address should only be used in - * checking whether our address has changed. Return 0 on success, -1 on - * failure. - */ -int -get_interface_address(int severity, uint32_t *addr) -{ - tor_addr_t local_addr; - int r; - - r = get_interface_address6(severity, AF_INET, &local_addr); - if (r>=0) - *addr = tor_addr_to_ipv4h(&local_addr); - return r; -} - -/** Return true if we can tell that name is a canonical name for the - * loopback address. */ -int -tor_addr_hostname_is_local(const char *name) -{ - return !strcasecmp(name, "localhost") || - !strcasecmp(name, "local") || - !strcasecmpend(name, ".local"); -} - -/** Return a newly allocated tor_addr_port_t with addr and - port 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/tor/address.h b/src/tor/address.h deleted file mode 100644 index 87909a3..0000000 --- a/src/tor/address.h +++ /dev/null @@ -1,232 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file address.h - * \brief Headers for address.h - **/ - -#ifndef TOR_ADDRESS_H -#define TOR_ADDRESS_H - -#include "orconfig.h" -#include "torint.h" -#include "tor_compat.h" - -/** The number of bits from an address to consider while doing a masked - * comparison. */ -typedef uint8_t maskbits_t; - -struct in_addr; -/** Holds an IPv4 or IPv6 address. (Uses less memory than struct - * sockaddr_storage.) */ -typedef struct tor_addr_t -{ - sa_family_t family; - union { - uint32_t dummy_; /* This field is here so we have something to initialize - * with a reliable cross-platform type. */ - struct in_addr in_addr; - struct in6_addr in6_addr; - } addr; -} tor_addr_t; - -/** Holds an IP address and a TCP/UDP port. */ -typedef struct tor_addr_port_t -{ - tor_addr_t addr; - uint16_t port; -} tor_addr_port_t; - -#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); -static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a); -static INLINE uint32_t tor_addr_to_mapped_ipv4h(const tor_addr_t *a); -static INLINE sa_family_t tor_addr_family(const tor_addr_t *a); -static INLINE const struct in_addr *tor_addr_to_in(const tor_addr_t *a); -static INLINE int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u); - -socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port, - struct sockaddr *sa_out, socklen_t len); -int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, - uint16_t *port_out); -void tor_addr_make_unspec(tor_addr_t *a); -void tor_addr_make_null(tor_addr_t *a, sa_family_t family); -char *tor_sockaddr_to_str(const struct sockaddr *sa); - -/** Return an in6_addr* equivalent to a, or NULL if a is not - * an IPv6 address. */ -static INLINE const struct in6_addr * -tor_addr_to_in6(const tor_addr_t *a) -{ - return a->family == AF_INET6 ? &a->addr.in6_addr : NULL; -} - -/** Given an IPv6 address x, yield it as an array of uint8_t. - * - * Requires that x is actually an IPv6 address. - */ -#define tor_addr_to_in6_addr8(x) tor_addr_to_in6(x)->s6_addr -/** Given an IPv6 address x, yield it as an array of uint16_t. - * - * Requires that x is actually an IPv6 address. - */ -#define tor_addr_to_in6_addr16(x) S6_ADDR16(*tor_addr_to_in6(x)) -/** Given an IPv6 address x, yield it as an array of uint32_t. - * - * Requires that x is actually an IPv6 address. - */ -#define tor_addr_to_in6_addr32(x) S6_ADDR32(*tor_addr_to_in6(x)) - -/** Return an IPv4 address in network order for a, or 0 if - * a is not an IPv4 address. */ -static INLINE uint32_t -tor_addr_to_ipv4n(const tor_addr_t *a) -{ - return a->family == AF_INET ? a->addr.in_addr.s_addr : 0; -} -/** Return an IPv4 address in host order for a, or 0 if - * a is not an IPv4 address. */ -static INLINE uint32_t -tor_addr_to_ipv4h(const tor_addr_t *a) -{ - return ntohl(tor_addr_to_ipv4n(a)); -} -/** Given an IPv6 address, return its mapped IPv4 address in host order, or - * 0 if a is not an IPv6 address. - * - * (Does not check whether the address is really a mapped address */ -static INLINE uint32_t -tor_addr_to_mapped_ipv4h(const tor_addr_t *a) -{ - return a->family == AF_INET6 ? ntohl(tor_addr_to_in6_addr32(a)[3]) : 0; -} -/** Return the address family of a. Possible values are: - * AF_INET6, AF_INET, AF_UNSPEC. */ -static INLINE sa_family_t -tor_addr_family(const tor_addr_t *a) -{ - return a->family; -} -/** Return an in_addr* equivalent to a, or NULL if a is not - * an IPv4 address. */ -static INLINE const struct in_addr * -tor_addr_to_in(const tor_addr_t *a) -{ - return a->family == AF_INET ? &a->addr.in_addr : NULL; -} -/** Return true iff a is an IPv4 address equal to the host-ordered - * address in u. */ -static INLINE int -tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u) -{ - return a->family == AF_INET ? (tor_addr_to_ipv4h(a) == u) : 0; -} - -/** Length of a buffer that you need to allocate to be sure you can encode - * any tor_addr_t. - * - * This allows enough space for - * "[ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]", - * plus a terminating NUL. - */ -#define TOR_ADDR_BUF_LEN 48 - -int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out); -char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; - -/** Wrapper function of fmt_addr_impl(). It does not decorate IPv6 - * addresses. */ -#define fmt_addr(a) fmt_addr_impl((a), 0) -/** Wrapper function of fmt_addr_impl(). It decorates IPv6 - * addresses. */ -#define fmt_and_decorate_addr(a) fmt_addr_impl((a), 1) -const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); -const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); -const char * fmt_addr32(uint32_t addr); -int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr); - -/** Flag to specify how to do a comparison between addresses. In an "exact" - * comparison, addresses are equivalent only if they are in the same family - * with the same value. In a "semantic" comparison, IPv4 addresses match all - * IPv6 encodings of those addresses. */ -typedef enum { - CMP_EXACT, - CMP_SEMANTIC, -} tor_addr_comparison_t; - -int tor_addr_compare(const tor_addr_t *addr1, const tor_addr_t *addr2, - tor_addr_comparison_t how); -int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, - maskbits_t mask, tor_addr_comparison_t how); -/** Return true iff a and b are the same address. The comparison is done - * "exactly". */ -#define tor_addr_eq(a,b) (0==tor_addr_compare((a),(b),CMP_EXACT)) - -unsigned int tor_addr_hash(const tor_addr_t *addr); -int tor_addr_is_v4(const tor_addr_t *addr); -int tor_addr_is_internal_(const tor_addr_t *ip, int for_listening, - const char *filename, int lineno); -#define tor_addr_is_internal(addr, for_listening) \ - tor_addr_is_internal_((addr), (for_listening), SHORT_FILE__, __LINE__) - -/** Longest length that can be required for a reverse lookup name. */ -/* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ -#define REVERSE_LOOKUP_NAME_BUF_LEN 73 -int tor_addr_to_PTR_name(char *out, size_t outlen, - const tor_addr_t *addr); -int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, - int family, int accept_regular); - -int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, - uint16_t *port_out); -#define TAPMP_EXTENDED_STAR 1 -int tor_addr_parse_mask_ports(const char *s, unsigned flags, - tor_addr_t *addr_out, maskbits_t *mask_out, - uint16_t *port_min_out, uint16_t *port_max_out); -const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, - int decorate); -int tor_addr_parse(tor_addr_t *addr, const char *src); -void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src); -void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr); -/** Set dest to the IPv4 address encoded in v4addr in host - * order. */ -#define tor_addr_from_ipv4h(dest, v4addr) \ - tor_addr_from_ipv4n((dest), htonl(v4addr)) -void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes); -/** Set dest to the IPv4 address incoded in in. */ -#define tor_addr_from_in(dest, in) \ - tor_addr_from_ipv4n((dest), (in)->s_addr); -void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); -int tor_addr_is_null(const tor_addr_t *addr); -int tor_addr_is_loopback(const tor_addr_t *addr); - -int tor_addr_port_split(int severity, const char *addrport, - char **address_out, uint16_t *port_out); - -int tor_addr_port_parse(int severity, const char *addrport, - tor_addr_t *address_out, uint16_t *port_out); - -int tor_addr_hostname_is_local(const char *name); - -/* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening); -int addr_port_lookup(int severity, const char *addrport, char **address, - uint32_t *addr, uint16_t *port_out); -int parse_port_range(const char *port, uint16_t *port_min_out, - uint16_t *port_max_out); -int addr_mask_get_bits(uint32_t mask); -/** Length of a buffer to allocate to hold the results of tor_inet_ntoa.*/ -#define INET_NTOA_BUF_LEN 16 -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/tor/addressmap.c b/src/tor/addressmap.c deleted file mode 100644 index 9bc79bd..0000000 --- a/src/tor/addressmap.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#define ADDRESSMAP_PRIVATE - -#include "or.h" -#include "addressmap.h" -#include "circuituse.h" -#include "config.h" -#include "connection_edge.h" -#include "control.h" -#include "dns.h" -#include "routerset.h" -#include "nodelist.h" - -/** A client-side struct to remember requests to rewrite addresses - * to new addresses. These structs are stored in the hash table - * "addressmap" below. - * - * There are 5 ways to set an address mapping: - * - A MapAddress command from the controller [permanent] - * - An AddressMap directive in the torrc [permanent] - * - When a TrackHostExits torrc directive is triggered [temporary] - * - When a DNS resolve succeeds [temporary] - * - When a DNS resolve fails [temporary] - * - * When an addressmap request is made but one is already registered, - * the new one is replaced only if the currently registered one has - * no "new_address" (that is, it's in the process of DNS resolve), - * or if the new one is permanent (expires==0 or 1). - * - * (We overload the 'expires' field, using "0" for mappings set via - * the configuration file, "1" for mappings set from the control - * interface, and other values for DNS and TrackHostExit mappings that can - * expire.) - * - * A mapping may be 'wildcarded'. If "src_wildcard" is true, then - * any address that ends with a . followed by the key for this entry will - * get remapped by it. If "dst_wildcard" is also true, then only the - * matching suffix of such addresses will get replaced by new_address. - */ -typedef struct { - char *new_address; - time_t expires; - ENUM_BF(addressmap_entry_source_t) source:3; - unsigned src_wildcard:1; - unsigned dst_wildcard:1; - short num_resolve_failures; -} addressmap_entry_t; - -/** Entry for mapping addresses to which virtual address we mapped them to. */ -typedef struct { - char *ipv4_address; - char *ipv6_address; - char *hostname_address; -} virtaddress_entry_t; - -/** A hash table to store client-side address rewrite instructions. */ -static strmap_t *addressmap=NULL; - -/** - * Table mapping addresses to which virtual address, if any, we - * assigned them to. - * - * We maintain the following invariant: if [A,B] is in - * virtaddress_reversemap, then B must be a virtual address, and [A,B] - * must be in addressmap. We do not require that the converse hold: - * if it fails, then we could end up mapping two virtual addresses to - * the same address, which is no disaster. - **/ -static strmap_t *virtaddress_reversemap=NULL; - -/** Initialize addressmap. */ -void -addressmap_init(void) -{ - addressmap = strmap_new(); - virtaddress_reversemap = strmap_new(); -} - -/** Free the memory associated with the addressmap entry _ent. */ -static void -addressmap_ent_free(void *_ent) -{ - addressmap_entry_t *ent; - if (!_ent) - return; - - ent = _ent; - tor_free(ent->new_address); - tor_free(ent); -} - -/** Free storage held by a virtaddress_entry_t* entry in ent. */ -static void -addressmap_virtaddress_ent_free(void *_ent) -{ - virtaddress_entry_t *ent; - if (!_ent) - return; - - ent = _ent; - tor_free(ent->ipv4_address); - tor_free(ent->hostname_address); - tor_free(ent); -} - -/** Free storage held by a virtaddress_entry_t* entry in ent. */ -static void -addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent) -{ - if (ent && ent->new_address && - address_is_in_virtual_range(ent->new_address)) { - virtaddress_entry_t *ve = - strmap_get(virtaddress_reversemap, ent->new_address); - /*log_fn(LOG_NOTICE,"remove reverse mapping for %s",ent->new_address);*/ - if (ve) { - if (!strcmp(address, ve->ipv4_address)) - tor_free(ve->ipv4_address); - if (!strcmp(address, ve->hostname_address)) - tor_free(ve->hostname_address); - if (!ve->ipv4_address && !ve->hostname_address) { - tor_free(ve); - strmap_remove(virtaddress_reversemap, ent->new_address); - } - } - } -} - -/** Remove ent (which must be mapped to by address) from the - * client address maps. */ -static void -addressmap_ent_remove(const char *address, addressmap_entry_t *ent) -{ - addressmap_virtaddress_remove(address, ent); - addressmap_ent_free(ent); -} - -/** Unregister all TrackHostExits mappings from any address to - * *.exitname.exit. */ -void -clear_trackexithost_mappings(const char *exitname) -{ - char *suffix = NULL; - if (!addressmap || !exitname) - return; - tor_asprintf(&suffix, ".%s.exit", exitname); - tor_strlower(suffix); - - STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) { - if (ent->source == ADDRMAPSRC_TRACKEXIT && - !strcmpend(ent->new_address, suffix)) { - addressmap_ent_remove(address, ent); - MAP_DEL_CURRENT(address); - } - } STRMAP_FOREACH_END; - - tor_free(suffix); -} - -/** Remove all TRACKEXIT mappings from the addressmap for which the target - * host is unknown or no longer allowed, or for which the source address - * is no longer in trackexithosts. */ -void -addressmap_clear_excluded_trackexithosts(const or_options_t *options) -{ - const routerset_t *allow_nodes = options->ExitNodes; - const routerset_t *exclude_nodes = options->ExcludeExitNodesUnion_; - - if (!addressmap) - return; - if (routerset_is_empty(allow_nodes)) - allow_nodes = NULL; - if (allow_nodes == NULL && routerset_is_empty(exclude_nodes)) - return; - - STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) { - size_t len; - const char *target = ent->new_address, *dot; - char *nodename; - const node_t *node; - - if (!target) { - /* DNS resolving in progress */ - continue; - } else if (strcmpend(target, ".exit")) { - /* Not a .exit mapping */ - continue; - } else if (ent->source != ADDRMAPSRC_TRACKEXIT) { - /* Not a trackexit mapping. */ - continue; - } - len = strlen(target); - if (len < 6) - continue; /* malformed. */ - dot = target + len - 6; /* dot now points to just before .exit */ - while (dot > target && *dot != '.') - dot--; - if (*dot == '.') dot++; - nodename = tor_strndup(dot, len-5-(dot-target));; - node = node_get_by_nickname(nodename, 0); - tor_free(nodename); - if (!node || - (allow_nodes && !routerset_contains_node(allow_nodes, node)) || - routerset_contains_node(exclude_nodes, node) || - !hostname_in_track_host_exits(options, address)) { - /* We don't know this one, or we want to be rid of it. */ - addressmap_ent_remove(address, ent); - MAP_DEL_CURRENT(address); - } - } STRMAP_FOREACH_END; -} - -/** Return true iff address is one that we are configured to - * automap on resolve according to options. */ -int -addressmap_address_should_automap(const char *address, - const or_options_t *options) -{ - const smartlist_t *suffix_list = options->AutomapHostsSuffixes; - - if (!suffix_list) - return 0; - - SMARTLIST_FOREACH_BEGIN(suffix_list, const char *, suffix) { - if (!strcasecmpend(address, suffix)) - return 1; - } SMARTLIST_FOREACH_END(suffix); - return 0; -} - -/** Remove all AUTOMAP mappings from the addressmap for which the - * source address no longer matches AutomapHostsSuffixes, which is - * no longer allowed by AutomapHostsOnResolve, or for which the - * target address is no longer in the virtual network. */ -void -addressmap_clear_invalid_automaps(const or_options_t *options) -{ - int clear_all = !options->AutomapHostsOnResolve; - const smartlist_t *suffixes = options->AutomapHostsSuffixes; - - if (!addressmap) - return; - - if (!suffixes) - clear_all = 1; /* This should be impossible, but let's be sure. */ - - STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) { - int remove = clear_all; - if (ent->source != ADDRMAPSRC_AUTOMAP) - continue; /* not an automap mapping. */ - - if (!remove) { - remove = ! addressmap_address_should_automap(src_address, options); - } - - if (!remove && ! address_is_in_virtual_range(ent->new_address)) - remove = 1; - - if (remove) { - addressmap_ent_remove(src_address, ent); - MAP_DEL_CURRENT(src_address); - } - } STRMAP_FOREACH_END; -} - -/** Remove all entries from the addressmap that were set via the - * configuration file or the command line. */ -void -addressmap_clear_configured(void) -{ - addressmap_get_mappings(NULL, 0, 0, 0); -} - -/** Remove all entries from the addressmap that are set to expire, ever. */ -void -addressmap_clear_transient(void) -{ - addressmap_get_mappings(NULL, 2, TIME_MAX, 0); -} - -/** Clean out entries from the addressmap cache that were - * added long enough ago that they are no longer valid. - */ -void -addressmap_clean(time_t now) -{ - addressmap_get_mappings(NULL, 2, now, 0); -} - -/** Free all the elements in the addressmap, and free the addressmap - * itself. */ -void -addressmap_free_all(void) -{ - strmap_free(addressmap, addressmap_ent_free); - addressmap = NULL; - - strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free); - virtaddress_reversemap = NULL; -} - -/** Try to find a match for AddressMap expressions that use - * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or - * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c). - * Return the matching entry in AddressMap or NULL if no match is found. - * For expressions such as '*.c.d *.e.f', truncate address 'a.c.d' - * to 'a' before we return the matching AddressMap entry. - * - * This function does not handle the case where a pattern of the form "*.c.d" - * matches the address c.d -- that's done by the main addressmap_rewrite - * function. - */ -static addressmap_entry_t * -addressmap_match_superdomains(char *address) -{ - addressmap_entry_t *val; - char *cp; - - cp = address; - while ((cp = strchr(cp, '.'))) { - /* cp now points to a suffix of address that begins with a . */ - val = strmap_get_lc(addressmap, cp+1); - if (val && val->src_wildcard) { - if (val->dst_wildcard) - *cp = '\0'; - return val; - } - ++cp; - } - return NULL; -} - -/** Look at address, and rewrite it until it doesn't want any - * more rewrites; but don't get into an infinite loop. - * Don't write more than maxlen chars into address. Return true if the - * address changed; false otherwise. Set *expires_out to the - * expiry time of the result, or to time_max if the result does - * not expire. - * - * If exit_source_out is non-null, we set it as follows. If we the - * address starts out as a non-exit address, and we remap it to an .exit - * address at any point, then set *exit_source_out to the - * address_entry_source_t of the first such rule. Set *exit_source_out - * to ADDRMAPSRC_NONE if there is no such rewrite, or if the original address - * was a .exit. - */ -int -addressmap_rewrite(char *address, size_t maxlen, - unsigned flags, - time_t *expires_out, - addressmap_entry_source_t *exit_source_out) -{ - addressmap_entry_t *ent; - int rewrites; - time_t expires = TIME_MAX; - addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE; - char *addr_orig = tor_strdup(address); - char *log_addr_orig = NULL; - - for (rewrites = 0; rewrites < 16; rewrites++) { - int exact_match = 0; - log_addr_orig = tor_strdup(escaped_safe_str_client(address)); - - ent = strmap_get(addressmap, address); - - if (!ent || !ent->new_address) { - ent = addressmap_match_superdomains(address); - } else { - if (ent->src_wildcard && !ent->dst_wildcard && - !strcasecmp(address, ent->new_address)) { - /* This is a rule like *.example.com example.com, and we just got - * "example.com" */ - goto done; - } - - exact_match = 1; - } - - if (!ent || !ent->new_address) { - goto done; - } - - if (ent && ent->source == ADDRMAPSRC_DNS) { - sa_family_t f; - tor_addr_t tmp; - f = tor_addr_parse(&tmp, ent->new_address); - if (f == AF_INET && !(flags & AMR_FLAG_USE_IPV4_DNS)) - goto done; - else if (f == AF_INET6 && !(flags & AMR_FLAG_USE_IPV6_DNS)) - goto done; - } - - if (ent->dst_wildcard && !exact_match) { - strlcat(address, ".", maxlen); - strlcat(address, ent->new_address, maxlen); - } else { - strlcpy(address, ent->new_address, maxlen); - } - - if (!strcmpend(address, ".exit") && - strcmpend(addr_orig, ".exit") && - exit_source == ADDRMAPSRC_NONE) { - exit_source = ent->source; - } - - log_info(LD_APP, "Addressmap: rewriting %s to %s", - log_addr_orig, escaped_safe_str_client(address)); - if (ent->expires > 1 && ent->expires < expires) - expires = ent->expires; - - tor_free(log_addr_orig); - } - log_warn(LD_CONFIG, - "Loop detected: we've rewritten %s 16 times! Using it as-is.", - escaped_safe_str_client(address)); - /* it's fine to rewrite a rewrite, but don't loop forever */ - - done: - tor_free(addr_orig); - tor_free(log_addr_orig); - if (exit_source_out) - *exit_source_out = exit_source; - if (expires_out) - *expires_out = TIME_MAX; - return (rewrites > 0); -} - -/** If we have a cached reverse DNS entry for the address stored in the - * maxlen-byte buffer address (typically, a dotted quad) then - * rewrite to the cached value and return 1. Otherwise return 0. Set - * *expires_out to the expiry time of the result, or to time_max - * if the result does not expire. */ -int -addressmap_rewrite_reverse(char *address, size_t maxlen, unsigned flags, - time_t *expires_out) -{ - char *s, *cp; - addressmap_entry_t *ent; - int r = 0; - { - sa_family_t f; - tor_addr_t tmp; - f = tor_addr_parse(&tmp, address); - if (f == AF_INET && !(flags & AMR_FLAG_USE_IPV4_DNS)) - return 0; - else if (f == AF_INET6 && !(flags & AMR_FLAG_USE_IPV6_DNS)) - return 0; - } - - tor_asprintf(&s, "REVERSE[%s]", address); - ent = strmap_get(addressmap, s); - if (ent) { - cp = tor_strdup(escaped_safe_str_client(ent->new_address)); - log_info(LD_APP, "Rewrote reverse lookup %s -> %s", - escaped_safe_str_client(s), cp); - tor_free(cp); - strlcpy(address, ent->new_address, maxlen); - r = 1; - } - - if (expires_out) - *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX; - - tor_free(s); - return r; -} - -/** Return 1 if address is already registered, else return 0. If address - * is already registered, and update_expires is non-zero, then update - * the expiry time on the mapping with update_expires if it is a - * mapping created by TrackHostExits. */ -int -addressmap_have_mapping(const char *address, int update_expiry) -{ - addressmap_entry_t *ent; - if (!(ent=strmap_get_lc(addressmap, address))) - return 0; - if (update_expiry && ent->source==ADDRMAPSRC_TRACKEXIT) - ent->expires=time(NULL) + update_expiry; - return 1; -} - -/** Register a request to map address to new_address, - * which will expire on expires (or 0 if never expires from - * config file, 1 if never expires from controller, 2 if never expires - * (virtual address mapping) from the controller.) - * - * new_address should be a newly dup'ed string, which we'll use or - * free as appropriate. We will leave address alone. - * - * If wildcard_addr is true, then the mapping will match any address - * equal to address, or any address ending with a period followed by - * address. If wildcard_addr and wildcard_new_addr are - * both true, the mapping will rewrite addresses that end with - * ".address" into ones that end with ".new_address." - * - * If new_address is NULL, or new_address is equal to - * address and wildcard_addr is equal to - * wildcard_new_addr, remove any mappings that exist from - * address. - * - * - * It is an error to set wildcard_new_addr if wildcard_addr is - * not set. */ -void -addressmap_register(const char *address, char *new_address, time_t expires, - addressmap_entry_source_t source, - const int wildcard_addr, - const int wildcard_new_addr) -{ - addressmap_entry_t *ent; - - if (wildcard_new_addr) - tor_assert(wildcard_addr); - - ent = strmap_get(addressmap, address); - if (!new_address || (!strcasecmp(address,new_address) && - wildcard_addr == wildcard_new_addr)) { - /* Remove the mapping, if any. */ - tor_free(new_address); - if (ent) { - addressmap_ent_remove(address,ent); - strmap_remove(addressmap, address); - } - return; - } - if (!ent) { /* make a new one and register it */ - ent = tor_malloc_zero(sizeof(addressmap_entry_t)); - strmap_set(addressmap, address, ent); - } else if (ent->new_address) { /* we need to clean up the old mapping. */ - if (expires > 1) { - log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, " - "since it's already mapped to '%s'", - safe_str_client(address), - safe_str_client(new_address), - safe_str_client(ent->new_address)); - tor_free(new_address); - return; - } - if (address_is_in_virtual_range(ent->new_address) && - expires != 2) { - /* XXX This isn't the perfect test; we want to avoid removing - * mappings set from the control interface _as virtual mapping */ - addressmap_virtaddress_remove(address, ent); - } - tor_free(ent->new_address); - } /* else { we have an in-progress resolve with no mapping. } */ - - ent->new_address = new_address; - ent->expires = expires==2 ? 1 : expires; - ent->num_resolve_failures = 0; - ent->source = source; - ent->src_wildcard = wildcard_addr ? 1 : 0; - ent->dst_wildcard = wildcard_new_addr ? 1 : 0; - - log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'", - safe_str_client(address), - safe_str_client(ent->new_address)); - control_event_address_mapped(address, ent->new_address, expires, NULL, 1); -} - -/** An attempt to resolve address failed at some OR. - * Increment the number of resolve failures we have on record - * for it, and then return that number. - */ -int -client_dns_incr_failures(const char *address) -{ - addressmap_entry_t *ent = strmap_get(addressmap, address); - if (!ent) { - ent = tor_malloc_zero(sizeof(addressmap_entry_t)); - ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE; - strmap_set(addressmap,address,ent); - } - if (ent->num_resolve_failures < SHORT_MAX) - ++ent->num_resolve_failures; /* don't overflow */ - log_info(LD_APP, "Address %s now has %d resolve failures.", - safe_str_client(address), - ent->num_resolve_failures); - return ent->num_resolve_failures; -} - -/** If address is in the client DNS addressmap, reset - * the number of resolve failures we have on record for it. - * This is used when we fail a stream because it won't resolve: - * otherwise future attempts on that address will only try once. - */ -void -client_dns_clear_failures(const char *address) -{ - addressmap_entry_t *ent = strmap_get(addressmap, address); - if (ent) - ent->num_resolve_failures = 0; -} - -/** Record the fact that address resolved to name. - * We can now use this in subsequent streams via addressmap_rewrite() - * so we can more correctly choose an exit that will allow address. - * - * If exitname is defined, then append the addresses with - * ".exitname.exit" before registering the mapping. - * - * If ttl is nonnegative, the mapping will be valid for - * ttlseconds; otherwise, we use the default. - */ -static void -client_dns_set_addressmap_impl(entry_connection_t *for_conn, - const char *address, const char *name, - const char *exitname, - int ttl) -{ - char *extendedaddress=NULL, *extendedval=NULL; - (void)for_conn; - - tor_assert(address); - tor_assert(name); - - if (ttl<0) - ttl = DEFAULT_DNS_TTL; - else - ttl = dns_clip_ttl(ttl); - - if (exitname) { - /* XXXX fails to ever get attempts to get an exit address of - * google.com.digest[=~]nickname.exit; we need a syntax for this that - * won't make strict RFC952-compliant applications (like us) barf. */ - tor_asprintf(&extendedaddress, - "%s.%s.exit", address, exitname); - tor_asprintf(&extendedval, - "%s.%s.exit", name, exitname); - } else { - tor_asprintf(&extendedaddress, - "%s", address); - tor_asprintf(&extendedval, - "%s", name); - } - addressmap_register(extendedaddress, extendedval, - time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0); - tor_free(extendedaddress); -} - -/** Record the fact that address resolved to val. - * We can now use this in subsequent streams via addressmap_rewrite() - * so we can more correctly choose an exit that will allow address. - * - * If exitname is defined, then append the addresses with - * ".exitname.exit" before registering the mapping. - * - * If ttl is nonnegative, the mapping will be valid for - * ttlseconds; otherwise, we use the default. - */ -void -client_dns_set_addressmap(entry_connection_t *for_conn, - const char *address, - const tor_addr_t *val, - const char *exitname, - int ttl) -{ - tor_addr_t addr_tmp; - char valbuf[TOR_ADDR_BUF_LEN]; - - tor_assert(address); - tor_assert(val); - - if (tor_addr_parse(&addr_tmp, address) >= 0) - return; /* If address was an IP address already, don't add a mapping. */ - - if (tor_addr_family(val) == AF_INET) { - if (! for_conn->cache_ipv4_answers) - return; - } else if (tor_addr_family(val) == AF_INET6) { - if (! for_conn->cache_ipv6_answers) - return; - } - - if (! tor_addr_to_str(valbuf, val, sizeof(valbuf), 1)) - return; - - client_dns_set_addressmap_impl(for_conn, address, valbuf, exitname, ttl); -} - -/** Add a cache entry noting that address (ordinarily a dotted quad) - * resolved via a RESOLVE_PTR request to the hostname v. - * - * If exitname is defined, then append the addresses with - * ".exitname.exit" before registering the mapping. - * - * If ttl is nonnegative, the mapping will be valid for - * ttlseconds; otherwise, we use the default. - */ -void -client_dns_set_reverse_addressmap(entry_connection_t *for_conn, - const char *address, const char *v, - const char *exitname, - int ttl) -{ - char *s = NULL; - { - tor_addr_t tmp_addr; - sa_family_t f = tor_addr_parse(&tmp_addr, address); - if ((f == AF_INET && ! for_conn->cache_ipv4_answers) || - (f == AF_INET6 && ! for_conn->cache_ipv6_answers)) - return; - } - tor_asprintf(&s, "REVERSE[%s]", address); - client_dns_set_addressmap_impl(for_conn, s, v, exitname, ttl); - tor_free(s); -} - -/* By default, we hand out 127.192.0.1 through 127.254.254.254. - * These addresses should map to localhost, so even if the - * application accidentally tried to connect to them directly (not - * via Tor), it wouldn't get too far astray. - * - * These options are configured by parse_virtual_addr_network(). - */ - -static virtual_addr_conf_t virtaddr_conf_ipv4; -static virtual_addr_conf_t virtaddr_conf_ipv6; - -/** Read a netmask of the form 127.192.0.0/10 from "val", and check whether - * it's a valid set of virtual addresses to hand out in response to MAPADDRESS - * requests. Return 0 on success; set *msg (if provided) to a newly allocated - * string and return -1 on failure. If validate_only is false, sets the - * actual virtual address range to the parsed value. */ -int -parse_virtual_addr_network(const char *val, sa_family_t family, - int validate_only, - char **msg) -{ - const int ipv6 = (family == AF_INET6); - tor_addr_t addr; - maskbits_t bits; - const int max_bits = ipv6 ? 40 : 16; - virtual_addr_conf_t *conf = ipv6 ? &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4; - - if (tor_addr_parse_mask_ports(val, 0, &addr, &bits, NULL, NULL) < 0) { - if (msg) - tor_asprintf(msg, "Error parsing VirtualAddressNetwork%s %s", - ipv6?"IPv6":"", val); - return -1; - } - if (tor_addr_family(&addr) != family) { - if (msg) - tor_asprintf(msg, "Incorrect address type for VirtualAddressNetwork%s", - ipv6?"IPv6":""); - return -1; - } -#if 0 - if (port_min != 1 || port_max != 65535) { - if (msg) - tor_asprintf(msg, "Can't specify ports on VirtualAddressNetwork%s", - ipv6?"IPv6":""); - return -1; - } -#endif - - if (bits > max_bits) { - if (msg) - tor_asprintf(msg, "VirtualAddressNetwork%s expects a /%d " - "network or larger",ipv6?"IPv6":"", max_bits); - return -1; - } - - if (validate_only) - return 0; - - tor_addr_copy(&conf->addr, &addr); - conf->bits = bits; - - return 0; -} - -/** - * Return true iff addr is likely to have been returned by - * client_dns_get_unused_address. - **/ -int -address_is_in_virtual_range(const char *address) -{ - tor_addr_t addr; - tor_assert(address); - if (!strcasecmpend(address, ".virtual")) { - return 1; - } else if (tor_addr_parse(&addr, address) >= 0) { - const virtual_addr_conf_t *conf = (tor_addr_family(&addr) == AF_INET6) ? - &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4; - if (tor_addr_compare_masked(&addr, &conf->addr, conf->bits, CMP_EXACT)==0) - return 1; - } - return 0; -} - -/** Return a random address conforming to the virtual address configuration - * in conf. - */ -STATIC void -get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) -{ - uint8_t tmp[4]; - const uint8_t *addr_bytes; - uint8_t bytes[16]; - const int ipv6 = tor_addr_family(&conf->addr) == AF_INET6; - const int total_bytes = ipv6 ? 16 : 4; - - tor_assert(conf->bits <= total_bytes * 8); - - /* Set addr_bytes to the bytes of the virtual network, in host order */ - if (ipv6) { - addr_bytes = tor_addr_to_in6_addr8(&conf->addr); - } else { - set_uint32(tmp, tor_addr_to_ipv4n(&conf->addr)); - addr_bytes = tmp; - } - - /* Get an appropriate number of random bytes. */ - crypto_rand((char*)bytes, total_bytes); - - /* Now replace the first "conf->bits" bits of 'bytes' with addr_bytes*/ - if (conf->bits >= 8) - memcpy(bytes, addr_bytes, conf->bits / 8); - if (conf->bits & 7) { - uint8_t mask = 0xff >> (conf->bits & 7); - bytes[conf->bits/8] &= mask; - bytes[conf->bits/8] |= addr_bytes[conf->bits/8] & ~mask; - } - - if (ipv6) - tor_addr_from_ipv6_bytes(addr_out, (char*) bytes); - else - tor_addr_from_ipv4n(addr_out, get_uint32(bytes)); - - tor_assert(tor_addr_compare_masked(addr_out, &conf->addr, - conf->bits, CMP_EXACT)==0); -} - -/** Return a newly allocated string holding an address of type - * (one of RESOLVED_TYPE_{IPV4|HOSTNAME}) that has not yet been mapped, - * and that is very unlikely to be the address of any real host. - * - * May return NULL if we have run out of virtual addresses. - */ -static char * -addressmap_get_virtual_address(int type) -{ - char buf[64]; - tor_assert(addressmap); - - if (type == RESOLVED_TYPE_HOSTNAME) { - char rand[10]; - do { - crypto_rand(rand, sizeof(rand)); - base32_encode(buf,sizeof(buf),rand,sizeof(rand)); - strlcat(buf, ".virtual", sizeof(buf)); - } while (strmap_get(addressmap, buf)); - return tor_strdup(buf); - } else if (type == RESOLVED_TYPE_IPV4 || type == RESOLVED_TYPE_IPV6) { - const int ipv6 = (type == RESOLVED_TYPE_IPV6); - const virtual_addr_conf_t *conf = ipv6 ? - &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4; - - /* Don't try more than 1000 times. This gives us P < 1e-9 for - * failing to get a good address so long as the address space is - * less than ~97.95% full. That's always going to be true under - * sensible circumstances for an IPv6 /10, and it's going to be - * true for an IPv4 /10 as long as we've handed out less than - * 4.08 million addresses. */ - uint32_t attempts = 1000; - - tor_addr_t addr; - - while (attempts--) { - get_random_virtual_addr(conf, &addr); - - if (!ipv6) { - /* Don't hand out any .0 or .255 address. */ - const uint32_t a = tor_addr_to_ipv4h(&addr); - if ((a & 0xff) == 0 || (a & 0xff) == 0xff) - continue; - } - - tor_addr_to_str(buf, &addr, sizeof(buf), 1); - if (!strmap_get(addressmap, buf)) { - /* XXXX This code is to make sure I didn't add an undecorated version - * by mistake. I hope it's needless. */ - char tmp[TOR_ADDR_BUF_LEN]; - tor_addr_to_str(buf, &addr, sizeof(tmp), 0); - if (strmap_get(addressmap, tmp)) { - log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.", - buf, tmp); - continue; - } - - return tor_strdup(buf); - } - } - log_warn(LD_CONFIG, "Ran out of virtual addresses!"); - return NULL; - } else { - log_warn(LD_BUG, "Called with unsupported address type (%d)", type); - return NULL; - } -} - -/** A controller has requested that we map some address of type - * type to the address new_address. Choose an address - * that is unlikely to be used, and map it, and return it in a newly - * allocated string. If another address of the same type is already - * mapped to new_address, try to return a copy of that address. - * - * The string in new_address may be freed or inserted into a map - * as appropriate. May return NULL if are out of virtual addresses. - **/ -const char * -addressmap_register_virtual_address(int type, char *new_address) -{ - char **addrp; - virtaddress_entry_t *vent; - int vent_needs_to_be_added = 0; - - tor_assert(new_address); - tor_assert(addressmap); - tor_assert(virtaddress_reversemap); - - vent = strmap_get(virtaddress_reversemap, new_address); - if (!vent) { - vent = tor_malloc_zero(sizeof(virtaddress_entry_t)); - vent_needs_to_be_added = 1; - } - - if (type == RESOLVED_TYPE_IPV4) - addrp = &vent->ipv4_address; - else if (type == RESOLVED_TYPE_IPV6) - addrp = &vent->ipv6_address; - else - addrp = &vent->hostname_address; - - if (*addrp) { - addressmap_entry_t *ent = strmap_get(addressmap, *addrp); - if (ent && ent->new_address && - !strcasecmp(new_address, ent->new_address)) { - tor_free(new_address); - tor_assert(!vent_needs_to_be_added); - return tor_strdup(*addrp); - } else { - log_warn(LD_BUG, - "Internal confusion: I thought that '%s' was mapped to by " - "'%s', but '%s' really maps to '%s'. This is a harmless bug.", - safe_str_client(new_address), - safe_str_client(*addrp), - safe_str_client(*addrp), - ent?safe_str_client(ent->new_address):"(nothing)"); - } - } - - tor_free(*addrp); - *addrp = addressmap_get_virtual_address(type); - if (!*addrp) { - tor_free(vent); - tor_free(new_address); - return NULL; - } - log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address); - if (vent_needs_to_be_added) - strmap_set(virtaddress_reversemap, new_address, vent); - addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0); - -#if 0 - { - /* Try to catch possible bugs */ - addressmap_entry_t *ent; - ent = strmap_get(addressmap, *addrp); - tor_assert(ent); - tor_assert(!strcasecmp(ent->new_address,new_address)); - vent = strmap_get(virtaddress_reversemap, new_address); - tor_assert(vent); - tor_assert(!strcasecmp(*addrp, - (type == RESOLVED_TYPE_IPV4) ? - vent->ipv4_address : vent->hostname_address)); - log_info(LD_APP, "Map from %s to %s okay.", - safe_str_client(*addrp), - safe_str_client(new_address)); - } -#endif - - return *addrp; -} - -/** Return 1 if address has funny characters in it like colons. Return - * 0 if it's fine, or if we're configured to allow it anyway. client - * should be true if we're using this address as a client; false if we're - * using it as a server. - */ -int -address_is_invalid_destination(const char *address, int client) -{ - if (client) { - if (get_options()->AllowNonRFC953Hostnames) - return 0; - } else { - if (get_options()->ServerDNSAllowNonRFC953Hostnames) - return 0; - } - - /* It might be an IPv6 address! */ - { - tor_addr_t a; - if (tor_addr_parse(&a, address) >= 0) - return 0; - } - - while (*address) { - if (TOR_ISALNUM(*address) || - *address == '-' || - *address == '.' || - *address == '_') /* Underscore is not allowed, but Windows does it - * sometimes, just to thumb its nose at the IETF. */ - ++address; - else - return 1; - } - return 0; -} - -/** Iterate over all address mappings which have expiry times between - * min_expires and max_expires, inclusive. If sl is provided, add an - * "old-addr new-addr expiry" string to sl for each mapping, omitting - * the expiry time if want_expiry is false. If sl is NULL, remove the - * mappings. - */ -void -addressmap_get_mappings(smartlist_t *sl, time_t min_expires, - time_t max_expires, int want_expiry) -{ - strmap_iter_t *iter; - const char *key; - void *val_; - addressmap_entry_t *val; - - if (!addressmap) - addressmap_init(); - - for (iter = strmap_iter_init(addressmap); !strmap_iter_done(iter); ) { - strmap_iter_get(iter, &key, &val_); - val = val_; - if (val->expires >= min_expires && val->expires <= max_expires) { - if (!sl) { - iter = strmap_iter_next_rmv(addressmap,iter); - addressmap_ent_remove(key, val); - continue; - } else if (val->new_address) { - const char *src_wc = val->src_wildcard ? "*." : ""; - const char *dst_wc = val->dst_wildcard ? "*." : ""; - if (want_expiry) { - if (val->expires < 3 || val->expires == TIME_MAX) - smartlist_add_asprintf(sl, "%s%s %s%s NEVER", - src_wc, key, dst_wc, val->new_address); - else { - char time[ISO_TIME_LEN+1]; - format_iso_time(time, val->expires); - smartlist_add_asprintf(sl, "%s%s %s%s \"%s\"", - src_wc, key, dst_wc, val->new_address, - time); - } - } else { - smartlist_add_asprintf(sl, "%s%s %s%s", - src_wc, key, dst_wc, val->new_address); - } - } - } - iter = strmap_iter_next(addressmap,iter); - } -} - diff --git a/src/tor/addressmap.h b/src/tor/addressmap.h deleted file mode 100644 index 417832b..0000000 --- a/src/tor/addressmap.h +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_ADDRESSMAP_H -#define TOR_ADDRESSMAP_H - -#include "testsupport.h" - -void addressmap_init(void); -void addressmap_clear_excluded_trackexithosts(const or_options_t *options); -void addressmap_clear_invalid_automaps(const or_options_t *options); -void addressmap_clean(time_t now); -void addressmap_clear_configured(void); -void addressmap_clear_transient(void); -void addressmap_free_all(void); -#define AMR_FLAG_USE_IPV4_DNS (1u<<0) -#define AMR_FLAG_USE_IPV6_DNS (1u<<1) -int addressmap_rewrite(char *address, size_t maxlen, unsigned flags, - time_t *expires_out, - addressmap_entry_source_t *exit_source_out); -int addressmap_rewrite_reverse(char *address, size_t maxlen, unsigned flags, - time_t *expires_out); -int addressmap_have_mapping(const char *address, int update_timeout); - -void addressmap_register(const char *address, char *new_address, - time_t expires, addressmap_entry_source_t source, - const int address_wildcard, - const int new_address_wildcard); -int parse_virtual_addr_network(const char *val, - sa_family_t family, int validate_only, - char **msg); -int client_dns_incr_failures(const char *address); -void client_dns_clear_failures(const char *address); -void client_dns_set_addressmap(entry_connection_t *for_conn, - const char *address, const tor_addr_t *val, - const char *exitname, int ttl); -const char *addressmap_register_virtual_address(int type, char *new_address); -void addressmap_get_mappings(smartlist_t *sl, time_t min_expires, - time_t max_expires, int want_expiry); -int address_is_in_virtual_range(const char *addr); -void clear_trackexithost_mappings(const char *exitname); -void client_dns_set_reverse_addressmap(entry_connection_t *for_conn, - const char *address, const char *v, - const char *exitname, int ttl); -int addressmap_address_should_automap(const char *address, - const or_options_t *options); - -#ifdef ADDRESSMAP_PRIVATE -typedef struct virtual_addr_conf_t { - tor_addr_t addr; - maskbits_t bits; -} virtual_addr_conf_t; - -STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf, - tor_addr_t *addr_out); -#endif - -#endif - diff --git a/src/tor/aes.c b/src/tor/aes.c deleted file mode 100644 index 6dd27b2..0000000 --- a/src/tor/aes.c +++ /dev/null @@ -1,523 +0,0 @@ -/* Copyright (c) 2001, Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file aes.c - * \brief Implements a counter-mode stream cipher on top of AES. - **/ - -#include "orconfig.h" - -#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include "*/ - #ifndef _WIN32_WINNT - #define _WIN32_WINNT 0x0501 - #endif - #define WIN32_LEAN_AND_MEAN - #if defined(_MSC_VER) && (_MSC_VER < 1300) - #include - #else - #include - #include - #endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "crypto.h" -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) -/* See comments about which counter mode implementation to use below. */ -#include -#define CAN_USE_OPENSSL_CTR -#endif -#include "tor_compat.h" -#include "aes.h" -#include "tor_util.h" -#include "torlog.h" -#include "di_ops.h" - -#ifdef ANDROID -/* Android's OpenSSL seems to have removed all of its Engine support. */ -#define DISABLE_ENGINES -#endif - -/* We have five strategies for implementing AES counter mode. - * - * Best with x86 and x86_64: Use EVP_aes_ctr128() and EVP_EncryptUpdate(). - * This is possible with OpenSSL 1.0.1, where the counter-mode implementation - * can use bit-sliced or vectorized AES or AESNI as appropriate. - * - * Otherwise: Pick the best possible AES block implementation that OpenSSL - * gives us, and the best possible counter-mode implementation, and combine - * them. - */ -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,0,1) && \ - (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64) || defined(__x86_64__) || \ - defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__)) \ - -#define USE_EVP_AES_CTR - -#endif - -/* We have 2 strategies for getting the AES block cipher: Via OpenSSL's - * AES_encrypt function, or via OpenSSL's EVP_EncryptUpdate function. - * - * If there's any hardware acceleration in play, we want to be using EVP_* so - * we can get it. Otherwise, we'll want AES_*, which seems to be about 5% - * faster than indirecting through the EVP layer. - */ - -/* We have 2 strategies for getting a plug-in counter mode: use our own, or - * use OpenSSL's. - * - * Here we have a counter mode that's faster than the one shipping with - * OpenSSL pre-1.0 (by about 10%!). But OpenSSL 1.0.0 added a counter mode - * implementation faster than the one here (by about 7%). So we pick which - * one to used based on the Openssl version above. (OpenSSL 1.0.0a fixed a - * critical bug in that counter mode implementation, so we need to test to - * make sure that we have a fixed version.) - */ - -#ifdef USE_EVP_AES_CTR - -struct aes_cnt_cipher { - EVP_CIPHER_CTX *evp; -}; - -aes_cnt_cipher_t * -aes_new_cipher(const char *key, const char *iv) -{ - aes_cnt_cipher_t *cipher; - cipher = tor_malloc_zero(sizeof(aes_cnt_cipher_t)); - cipher->evp = EVP_CIPHER_CTX_new(); - EVP_EncryptInit(cipher->evp, EVP_aes_128_ctr(), - (const unsigned char*)key, (const unsigned char *)iv); - return cipher; -} -void -aes_cipher_free(aes_cnt_cipher_t *cipher) -{ - if (!cipher) - return; - EVP_CIPHER_CTX_free(cipher->evp); - memwipe(cipher, 0, sizeof(aes_cnt_cipher_t)); - tor_free(cipher); -} -void -aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, - char *output) -{ - int outl; - - tor_assert(len < INT_MAX); - - EVP_EncryptUpdate(cipher->evp, (unsigned char*)output, - &outl, (const unsigned char *)input, (int)len); -} -void -aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) -{ - int outl; - - tor_assert(len < INT_MAX); - - EVP_EncryptUpdate(cipher->evp, (unsigned char*)data, - &outl, (unsigned char*)data, (int)len); -} -int -evaluate_evp_for_aes(int force_val) -{ - (void) force_val; - log_info(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " - "counter-mode implementation. Using it."); - return 0; -} -int -evaluate_ctr_for_aes(void) -{ - return 0; -} -#else - -/*======================================================================*/ -/* Interface to AES code, and counter implementation */ - -/** Implements an AES counter-mode cipher. */ -struct aes_cnt_cipher { -/** This next element (however it's defined) is the AES key. */ - union { - EVP_CIPHER_CTX evp; - AES_KEY aes; - } key; - -#if !defined(WORDS_BIGENDIAN) -#define USING_COUNTER_VARS - /** These four values, together, implement a 128-bit counter, with - * counter0 as the low-order word and counter3 as the high-order word. */ - uint32_t counter3; - uint32_t counter2; - uint32_t counter1; - uint32_t counter0; -#endif - - union { - /** The counter, in big-endian order, as bytes. */ - uint8_t buf[16]; - /** The counter, in big-endian order, as big-endian words. Note that - * on big-endian platforms, this is redundant with counter3...0, - * so we just use these values instead. */ - uint32_t buf32[4]; - } ctr_buf; - - /** The encrypted value of ctr_buf. */ - uint8_t buf[16]; - /** Our current stream position within buf. */ - unsigned int pos; - - /** True iff we're using the evp implementation of this cipher. */ - uint8_t using_evp; -}; - -/** True iff we should prefer the EVP implementation for AES, either because - * we're testing it or because we have hardware acceleration configured */ -static int should_use_EVP = 0; - -#ifdef CAN_USE_OPENSSL_CTR -/** True iff we have tested the counter-mode implementation and found that it - * doesn't have the counter-mode bug from OpenSSL 1.0.0. */ -static int should_use_openssl_CTR = 0; -#endif - -/** Check whether we should use the EVP interface for AES. If force_val - * is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP - * if there is an engine enabled for aes-ecb. */ -int -evaluate_evp_for_aes(int force_val) -{ - ENGINE *e; - - if (force_val >= 0) { - should_use_EVP = force_val; - return 0; - } -#ifdef DISABLE_ENGINES - should_use_EVP = 0; -#else - e = ENGINE_get_cipher_engine(NID_aes_128_ecb); - - if (e) { - log_info(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", - ENGINE_get_name(e)); - should_use_EVP = 1; - } else { - log_info(LD_CRYPTO, "No AES engine found; using AES_* functions."); - should_use_EVP = 0; - } -#endif - - return 0; -} - -/** Test the OpenSSL counter mode implementation to see whether it has the - * counter-mode bug from OpenSSL 1.0.0. If the implementation works, then - * we will use it for future encryption/decryption operations. - * - * We can't just look at the OpenSSL version, since some distributions update - * their OpenSSL packages without changing the version number. - **/ -int -evaluate_ctr_for_aes(void) -{ -#ifdef CAN_USE_OPENSSL_CTR - /* Result of encrypting an all-zero block with an all-zero 128-bit AES key. - * This should be the same as encrypting an all-zero block with an all-zero - * 128-bit AES key in counter mode, starting at position 0 of the stream. - */ - static const unsigned char encrypt_zero[] = - "\x66\xe9\x4b\xd4\xef\x8a\x2c\x3b\x88\x4c\xfa\x59\xca\x34\x2b\x2e"; - unsigned char zero[16]; - unsigned char output[16]; - unsigned char ivec[16]; - unsigned char ivec_tmp[16]; - unsigned int pos, i; - AES_KEY key; - memset(zero, 0, sizeof(zero)); - memset(ivec, 0, sizeof(ivec)); - AES_set_encrypt_key(zero, 128, &key); - - pos = 0; - /* Encrypting a block one byte at a time should make the error manifest - * itself for known bogus openssl versions. */ - for (i=0; i<16; ++i) - AES_ctr128_encrypt(&zero[i], &output[i], 1, &key, ivec, ivec_tmp, &pos); - - if (fast_memneq(output, encrypt_zero, 16)) { - /* Counter mode is buggy */ - log_notice(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; " - "not using it."); - } else { - /* Counter mode is okay */ - log_info(LD_CRYPTO, "This OpenSSL has a good implementation of counter " - "mode; using it."); - should_use_openssl_CTR = 1; - } -#else - log_info(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " - "counter mode; not using it."); -#endif - return 0; -} - -#if !defined(USING_COUNTER_VARS) -#define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)]) -#else -#define COUNTER(c, n) ((c)->counter ## n) -#endif - -/** - * Helper function: set cipher's internal buffer to the encrypted - * value of the current counter. - */ -static INLINE void -aes_fill_buf_(aes_cnt_cipher_t *cipher) -{ - /* We don't currently use OpenSSL's counter mode implementation because: - * 1) some versions have known bugs - * 2) its attitude towards IVs is not our own - * 3) changing the counter position was not trivial, last time I looked. - * None of these issues are insurmountable in principle. - */ - - if (cipher->using_evp) { - int outl=16, inl=16; - EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl, - cipher->ctr_buf.buf, inl); - } else { - AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes); - } -} - -static void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, - int key_bits); -static void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv); - -/** - * Return a newly allocated counter-mode AES128 cipher implementation, - * using the 128-bit key key and the 128-bit IV iv. - */ -aes_cnt_cipher_t* -aes_new_cipher(const char *key, const char *iv) -{ - aes_cnt_cipher_t* result = tor_malloc_zero(sizeof(aes_cnt_cipher_t)); - - aes_set_key(result, key, 128); - aes_set_iv(result, iv); - - return result; -} - -/** Set the key of cipher to key, which is - * key_bits bits long (must be 128, 192, or 256). Also resets - * the counter to 0. - */ -static void -aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) -{ - if (should_use_EVP) { - const EVP_CIPHER *c; - switch (key_bits) { - case 128: c = EVP_aes_128_ecb(); break; - case 192: c = EVP_aes_192_ecb(); break; - case 256: c = EVP_aes_256_ecb(); break; - default: tor_assert(0); - } - EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL); - cipher->using_evp = 1; - } else { - AES_set_encrypt_key((const unsigned char *)key, key_bits,&cipher->key.aes); - cipher->using_evp = 0; - } - -#ifdef USING_COUNTER_VARS - cipher->counter0 = 0; - cipher->counter1 = 0; - cipher->counter2 = 0; - cipher->counter3 = 0; -#endif - - memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf)); - - cipher->pos = 0; - -#ifdef CAN_USE_OPENSSL_CTR - if (should_use_openssl_CTR) - memset(cipher->buf, 0, sizeof(cipher->buf)); - else -#endif - aes_fill_buf_(cipher); -} - -/** Release storage held by cipher - */ -void -aes_cipher_free(aes_cnt_cipher_t *cipher) -{ - if (!cipher) - return; - if (cipher->using_evp) { - EVP_CIPHER_CTX_cleanup(&cipher->key.evp); - } - memwipe(cipher, 0, sizeof(aes_cnt_cipher_t)); - tor_free(cipher); -} - -#if defined(USING_COUNTER_VARS) -#define UPDATE_CTR_BUF(c, n) STMT_BEGIN \ - (c)->ctr_buf.buf32[3-(n)] = htonl((c)->counter ## n); \ - STMT_END -#else -#define UPDATE_CTR_BUF(c, n) -#endif - -#ifdef CAN_USE_OPENSSL_CTR -/* Helper function to use EVP with openssl's counter-mode wrapper. */ -static void -evp_block128_fn(const uint8_t in[16], - uint8_t out[16], - const void *key) -{ - EVP_CIPHER_CTX *ctx = (void*)key; - int inl=16, outl=16; - EVP_EncryptUpdate(ctx, out, &outl, in, inl); -} -#endif - -/** Encrypt len bytes from input, storing the result in - * output. Uses the key in cipher, and advances the counter - * by len bytes as it encrypts. - */ -void -aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, - char *output) -{ -#ifdef CAN_USE_OPENSSL_CTR - if (should_use_openssl_CTR) { - if (cipher->using_evp) { - /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If - * it weren't disabled, it might be better just to use that. - */ - CRYPTO_ctr128_encrypt((const unsigned char *)input, - (unsigned char *)output, - len, - &cipher->key.evp, - cipher->ctr_buf.buf, - cipher->buf, - &cipher->pos, - evp_block128_fn); - } else { - AES_ctr128_encrypt((const unsigned char *)input, - (unsigned char *)output, - len, - &cipher->key.aes, - cipher->ctr_buf.buf, - cipher->buf, - &cipher->pos); - } - return; - } else -#endif - { - int c = cipher->pos; - if (PREDICT_UNLIKELY(!len)) return; - - while (1) { - do { - if (len-- == 0) { cipher->pos = c; return; } - *(output++) = *(input++) ^ cipher->buf[c]; - } while (++c != 16); - cipher->pos = c = 0; - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { - ++COUNTER(cipher, 3); - UPDATE_CTR_BUF(cipher, 3); - } - UPDATE_CTR_BUF(cipher, 2); - } - UPDATE_CTR_BUF(cipher, 1); - } - UPDATE_CTR_BUF(cipher, 0); - aes_fill_buf_(cipher); - } - } -} - -/** Encrypt len bytes from input, storing the results in place. - * Uses the key in cipher, and advances the counter by len bytes - * as it encrypts. - */ -void -aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) -{ -#ifdef CAN_USE_OPENSSL_CTR - if (should_use_openssl_CTR) { - aes_crypt(cipher, data, len, data); - return; - } else -#endif - { - int c = cipher->pos; - if (PREDICT_UNLIKELY(!len)) return; - - while (1) { - do { - if (len-- == 0) { cipher->pos = c; return; } - *(data++) ^= cipher->buf[c]; - } while (++c != 16); - cipher->pos = c = 0; - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { - ++COUNTER(cipher, 3); - UPDATE_CTR_BUF(cipher, 3); - } - UPDATE_CTR_BUF(cipher, 2); - } - UPDATE_CTR_BUF(cipher, 1); - } - UPDATE_CTR_BUF(cipher, 0); - aes_fill_buf_(cipher); - } - } -} - -/** Reset the 128-bit counter of cipher to the 16-bit big-endian value - * in iv. */ -static void -aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) -{ -#ifdef USING_COUNTER_VARS - cipher->counter3 = ntohl(get_uint32(iv)); - cipher->counter2 = ntohl(get_uint32(iv+4)); - cipher->counter1 = ntohl(get_uint32(iv+8)); - cipher->counter0 = ntohl(get_uint32(iv+12)); -#endif - cipher->pos = 0; - memcpy(cipher->ctr_buf.buf, iv, 16); - -#ifdef CAN_USE_OPENSSL_CTR - if (!should_use_openssl_CTR) -#endif - aes_fill_buf_(cipher); -} - -#endif - diff --git a/src/tor/aes.h b/src/tor/aes.h deleted file mode 100644 index 8ff28a7..0000000 --- a/src/tor/aes.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2003, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/* Implements a minimal interface to counter-mode AES. */ - -#ifndef TOR_AES_H -#define TOR_AES_H - -/** - * \file aes.h - * \brief Headers for aes.c - */ - -struct aes_cnt_cipher; -typedef struct aes_cnt_cipher aes_cnt_cipher_t; - -aes_cnt_cipher_t* aes_new_cipher(const char *key, const char *iv); -void aes_cipher_free(aes_cnt_cipher_t *cipher); -void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, - char *output); -void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); - -int evaluate_evp_for_aes(int force_value); -int evaluate_ctr_for_aes(void); - -#endif - diff --git a/src/tor/anonymize.P b/src/tor/anonymize.P deleted file mode 100644 index ae97293..0000000 --- a/src/tor/anonymize.P +++ /dev/null @@ -1,1072 +0,0 @@ -tor/anonymize.o: tor/anonymize.cpp \ - /path/to/triangles/src/tor/anonymize.h \ - /path/to/triangles/src/util.h \ - /path/to/triangles/src/uint256.h \ - /opt/local/include/boost/thread.hpp \ - /opt/local/include/boost/thread/thread.hpp \ - /opt/local/include/boost/thread/thread_only.hpp \ - /opt/local/include/boost/thread/detail/platform.hpp \ - /opt/local/include/boost/config.hpp \ - /opt/local/include/boost/config/user.hpp \ - /opt/local/include/boost/config/select_compiler_config.hpp \ - /opt/local/include/boost/config/compiler/clang.hpp \ - /opt/local/include/boost/config/select_stdlib_config.hpp \ - /opt/local/include/boost/config/no_tr1/utility.hpp \ - /opt/local/include/boost/config/stdlib/libstdcpp3.hpp \ - /opt/local/include/boost/config/select_platform_config.hpp \ - /opt/local/include/boost/config/platform/macos.hpp \ - /opt/local/include/boost/config/posix_features.hpp \ - /opt/local/include/boost/config/suffix.hpp \ - /opt/local/include/boost/config/requires_threads.hpp \ - /opt/local/include/boost/thread/pthread/thread_data.hpp \ - /opt/local/include/boost/thread/detail/config.hpp \ - /opt/local/include/boost/detail/workaround.hpp \ - /opt/local/include/boost/config/auto_link.hpp \ - /opt/local/include/boost/thread/exceptions.hpp \ - /opt/local/include/boost/system/system_error.hpp \ - /opt/local/include/boost/system/error_code.hpp \ - /opt/local/include/boost/system/config.hpp \ - /opt/local/include/boost/system/api_config.hpp \ - /opt/local/include/boost/cstdint.hpp \ - /opt/local/include/boost/assert.hpp \ - /opt/local/include/boost/current_function.hpp \ - /opt/local/include/boost/operators.hpp \ - /opt/local/include/boost/iterator.hpp \ - /opt/local/include/boost/noncopyable.hpp \ - /opt/local/include/boost/utility/enable_if.hpp \ - /opt/local/include/boost/cerrno.hpp \ - /opt/local/include/boost/config/abi_prefix.hpp \ - /opt/local/include/boost/config/abi_suffix.hpp \ - /opt/local/include/boost/thread/lock_guard.hpp \ - /opt/local/include/boost/thread/detail/delete.hpp \ - /opt/local/include/boost/thread/detail/move.hpp \ - /opt/local/include/boost/type_traits/is_convertible.hpp \ - /opt/local/include/boost/type_traits/intrinsics.hpp \ - /opt/local/include/boost/type_traits/config.hpp \ - /opt/local/include/boost/type_traits/is_same.hpp \ - /opt/local/include/boost/type_traits/detail/bool_trait_def.hpp \ - /opt/local/include/boost/type_traits/detail/template_arity_spec.hpp \ - /opt/local/include/boost/mpl/int.hpp \ - /opt/local/include/boost/mpl/int_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/adl_barrier.hpp \ - /opt/local/include/boost/mpl/aux_/config/adl.hpp \ - /opt/local/include/boost/mpl/aux_/config/msvc.hpp \ - /opt/local/include/boost/mpl/aux_/config/intel.hpp \ - /opt/local/include/boost/mpl/aux_/config/gcc.hpp \ - /opt/local/include/boost/mpl/aux_/config/workaround.hpp \ - /opt/local/include/boost/mpl/aux_/nttp_decl.hpp \ - /opt/local/include/boost/mpl/aux_/config/nttp.hpp \ - /opt/local/include/boost/mpl/aux_/integral_wrapper.hpp \ - /opt/local/include/boost/mpl/integral_c_tag.hpp \ - /opt/local/include/boost/mpl/aux_/config/static_constant.hpp \ - /opt/local/include/boost/mpl/aux_/static_cast.hpp \ - /opt/local/include/boost/preprocessor/cat.hpp \ - /opt/local/include/boost/preprocessor/config/config.hpp \ - /opt/local/include/boost/mpl/aux_/template_arity_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessor/params.hpp \ - /opt/local/include/boost/mpl/aux_/config/preprocessor.hpp \ - /opt/local/include/boost/preprocessor/comma_if.hpp \ - /opt/local/include/boost/preprocessor/punctuation/comma_if.hpp \ - /opt/local/include/boost/preprocessor/control/if.hpp \ - /opt/local/include/boost/preprocessor/control/iif.hpp \ - /opt/local/include/boost/preprocessor/logical/bool.hpp \ - /opt/local/include/boost/preprocessor/facilities/empty.hpp \ - /opt/local/include/boost/preprocessor/punctuation/comma.hpp \ - /opt/local/include/boost/preprocessor/repeat.hpp \ - /opt/local/include/boost/preprocessor/repetition/repeat.hpp \ - /opt/local/include/boost/preprocessor/debug/error.hpp \ - /opt/local/include/boost/preprocessor/detail/auto_rec.hpp \ - /opt/local/include/boost/preprocessor/tuple/eat.hpp \ - /opt/local/include/boost/preprocessor/inc.hpp \ - /opt/local/include/boost/preprocessor/arithmetic/inc.hpp \ - /opt/local/include/boost/mpl/aux_/config/lambda.hpp \ - /opt/local/include/boost/mpl/aux_/config/ttp.hpp \ - /opt/local/include/boost/mpl/aux_/config/ctps.hpp \ - /opt/local/include/boost/mpl/aux_/config/overload_resolution.hpp \ - /opt/local/include/boost/type_traits/integral_constant.hpp \ - /opt/local/include/boost/mpl/bool.hpp \ - /opt/local/include/boost/mpl/bool_fwd.hpp \ - /opt/local/include/boost/mpl/integral_c.hpp \ - /opt/local/include/boost/mpl/integral_c_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/lambda_support.hpp \ - /opt/local/include/boost/type_traits/detail/bool_trait_undef.hpp \ - /opt/local/include/boost/type_traits/is_reference.hpp \ - /opt/local/include/boost/type_traits/is_lvalue_reference.hpp \ - /opt/local/include/boost/type_traits/is_rvalue_reference.hpp \ - /opt/local/include/boost/type_traits/ice.hpp \ - /opt/local/include/boost/type_traits/detail/yes_no_type.hpp \ - /opt/local/include/boost/type_traits/detail/ice_or.hpp \ - /opt/local/include/boost/type_traits/detail/ice_and.hpp \ - /opt/local/include/boost/type_traits/detail/ice_not.hpp \ - /opt/local/include/boost/type_traits/detail/ice_eq.hpp \ - /opt/local/include/boost/type_traits/is_volatile.hpp \ - /opt/local/include/boost/type_traits/detail/cv_traits_impl.hpp \ - /opt/local/include/boost/type_traits/is_abstract.hpp \ - /opt/local/include/boost/type_traits/remove_reference.hpp \ - /opt/local/include/boost/type_traits/broken_compiler_spec.hpp \ - /opt/local/include/boost/type_traits/detail/type_trait_def.hpp \ - /opt/local/include/boost/type_traits/detail/type_trait_undef.hpp \ - /opt/local/include/boost/type_traits/remove_cv.hpp \ - /opt/local/include/boost/type_traits/decay.hpp \ - /opt/local/include/boost/type_traits/is_array.hpp \ - /opt/local/include/boost/type_traits/is_function.hpp \ - /opt/local/include/boost/type_traits/detail/false_result.hpp \ - /opt/local/include/boost/type_traits/detail/is_function_ptr_helper.hpp \ - /opt/local/include/boost/type_traits/remove_bounds.hpp \ - /opt/local/include/boost/type_traits/add_pointer.hpp \ - /opt/local/include/boost/mpl/eval_if.hpp \ - /opt/local/include/boost/mpl/if.hpp \ - /opt/local/include/boost/mpl/aux_/value_wknd.hpp \ - /opt/local/include/boost/mpl/aux_/config/integral.hpp \ - /opt/local/include/boost/mpl/aux_/config/eti.hpp \ - /opt/local/include/boost/mpl/aux_/na_spec.hpp \ - /opt/local/include/boost/mpl/lambda_fwd.hpp \ - /opt/local/include/boost/mpl/void_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/na.hpp \ - /opt/local/include/boost/mpl/aux_/na_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/lambda_arity_param.hpp \ - /opt/local/include/boost/mpl/aux_/arity.hpp \ - /opt/local/include/boost/mpl/aux_/config/dtp.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessor/enum.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessor/def_params_tail.hpp \ - /opt/local/include/boost/mpl/limits/arity.hpp \ - /opt/local/include/boost/preprocessor/logical/and.hpp \ - /opt/local/include/boost/preprocessor/logical/bitand.hpp \ - /opt/local/include/boost/preprocessor/identity.hpp \ - /opt/local/include/boost/preprocessor/facilities/identity.hpp \ - /opt/local/include/boost/preprocessor/empty.hpp \ - /opt/local/include/boost/preprocessor/arithmetic/add.hpp \ - /opt/local/include/boost/preprocessor/arithmetic/dec.hpp \ - /opt/local/include/boost/preprocessor/control/while.hpp \ - /opt/local/include/boost/preprocessor/list/fold_left.hpp \ - /opt/local/include/boost/preprocessor/list/detail/fold_left.hpp \ - /opt/local/include/boost/preprocessor/control/expr_iif.hpp \ - /opt/local/include/boost/preprocessor/list/adt.hpp \ - /opt/local/include/boost/preprocessor/detail/is_binary.hpp \ - /opt/local/include/boost/preprocessor/detail/check.hpp \ - /opt/local/include/boost/preprocessor/logical/compl.hpp \ - /opt/local/include/boost/preprocessor/list/fold_right.hpp \ - /opt/local/include/boost/preprocessor/list/detail/fold_right.hpp \ - /opt/local/include/boost/preprocessor/list/reverse.hpp \ - /opt/local/include/boost/preprocessor/control/detail/while.hpp \ - /opt/local/include/boost/preprocessor/tuple/elem.hpp \ - /opt/local/include/boost/preprocessor/facilities/overload.hpp \ - /opt/local/include/boost/preprocessor/variadic/size.hpp \ - /opt/local/include/boost/preprocessor/tuple/rem.hpp \ - /opt/local/include/boost/preprocessor/variadic/elem.hpp \ - /opt/local/include/boost/preprocessor/arithmetic/sub.hpp \ - /opt/local/include/boost/mpl/identity.hpp \ - /opt/local/include/boost/move/utility.hpp \ - /opt/local/include/boost/move/detail/config_begin.hpp \ - /opt/local/include/boost/move/core.hpp \ - /opt/local/include/boost/move/detail/meta_utils.hpp \ - /opt/local/include/boost/move/detail/config_end.hpp \ - /opt/local/include/boost/move/traits.hpp \ - /opt/local/include/boost/type_traits/has_trivial_destructor.hpp \ - /opt/local/include/boost/type_traits/is_pod.hpp \ - /opt/local/include/boost/type_traits/is_void.hpp \ - /opt/local/include/boost/type_traits/is_scalar.hpp \ - /opt/local/include/boost/type_traits/is_arithmetic.hpp \ - /opt/local/include/boost/type_traits/is_integral.hpp \ - /opt/local/include/boost/type_traits/is_float.hpp \ - /opt/local/include/boost/type_traits/is_enum.hpp \ - /opt/local/include/boost/type_traits/is_pointer.hpp \ - /opt/local/include/boost/type_traits/is_member_pointer.hpp \ - /opt/local/include/boost/type_traits/is_member_function_pointer.hpp \ - /opt/local/include/boost/type_traits/detail/is_mem_fun_pointer_impl.hpp \ - /opt/local/include/boost/type_traits/is_nothrow_move_constructible.hpp \ - /opt/local/include/boost/type_traits/has_trivial_move_constructor.hpp \ - /opt/local/include/boost/type_traits/has_nothrow_copy.hpp \ - /opt/local/include/boost/type_traits/has_trivial_copy.hpp \ - /opt/local/include/boost/utility/declval.hpp \ - /opt/local/include/boost/type_traits/add_rvalue_reference.hpp \ - /opt/local/include/boost/type_traits/is_nothrow_move_assignable.hpp \ - /opt/local/include/boost/type_traits/has_trivial_move_assign.hpp \ - /opt/local/include/boost/type_traits/is_const.hpp \ - /opt/local/include/boost/type_traits/has_nothrow_assign.hpp \ - /opt/local/include/boost/type_traits/has_trivial_assign.hpp \ - /opt/local/include/boost/thread/detail/lockable_wrapper.hpp \ - /opt/local/include/boost/thread/lock_options.hpp \ - /opt/local/include/boost/thread/lock_types.hpp \ - /opt/local/include/boost/thread/lockable_traits.hpp \ - /opt/local/include/boost/type_traits/is_class.hpp \ - /opt/local/include/boost/thread/thread_time.hpp \ - /opt/local/include/boost/date_time/time_clock.hpp \ - /opt/local/include/boost/date_time/c_time.hpp \ - /opt/local/include/boost/throw_exception.hpp \ - /opt/local/include/boost/date_time/compiler_config.hpp \ - /opt/local/include/boost/date_time/locale_config.hpp \ - /opt/local/include/boost/shared_ptr.hpp \ - /opt/local/include/boost/smart_ptr/shared_ptr.hpp \ - /opt/local/include/boost/config/no_tr1/memory.hpp \ - /opt/local/include/boost/checked_delete.hpp \ - /opt/local/include/boost/smart_ptr/detail/shared_count.hpp \ - /opt/local/include/boost/smart_ptr/bad_weak_ptr.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_counted_base.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_has_sync.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp \ - /opt/local/include/boost/detail/sp_typeinfo.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_counted_impl.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_convertible.hpp \ - /opt/local/include/boost/smart_ptr/detail/sp_nullptr_t.hpp \ - /opt/local/include/boost/smart_ptr/detail/spinlock_pool.hpp \ - /opt/local/include/boost/smart_ptr/detail/spinlock.hpp \ - /opt/local/include/boost/smart_ptr/detail/spinlock_sync.hpp \ - /opt/local/include/boost/smart_ptr/detail/yield_k.hpp \ - /opt/local/include/boost/memory_order.hpp \ - /opt/local/include/boost/smart_ptr/detail/operator_bool.hpp \ - /opt/local/include/boost/date_time/microsec_time_clock.hpp \ - /opt/local/include/boost/date_time/filetime_functions.hpp \ - /opt/local/include/boost/date_time/posix_time/posix_time_types.hpp \ - /opt/local/include/boost/date_time/posix_time/ptime.hpp \ - /opt/local/include/boost/date_time/posix_time/posix_time_system.hpp \ - /opt/local/include/boost/date_time/posix_time/posix_time_config.hpp \ - /opt/local/include/boost/limits.hpp \ - /opt/local/include/boost/config/no_tr1/cmath.hpp \ - /opt/local/include/boost/date_time/time_duration.hpp \ - /opt/local/include/boost/static_assert.hpp \ - /opt/local/include/boost/date_time/time_defs.hpp \ - /opt/local/include/boost/date_time/special_defs.hpp \ - /opt/local/include/boost/date_time/time_resolution_traits.hpp \ - /opt/local/include/boost/date_time/int_adapter.hpp \ - /opt/local/include/boost/date_time/gregorian/gregorian_types.hpp \ - /opt/local/include/boost/date_time/date.hpp \ - /opt/local/include/boost/date_time/year_month_day.hpp \ - /opt/local/include/boost/date_time/period.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_calendar.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_weekday.hpp \ - /opt/local/include/boost/date_time/constrained_value.hpp \ - /opt/local/include/boost/type_traits/is_base_of.hpp \ - /opt/local/include/boost/type_traits/is_base_and_derived.hpp \ - /opt/local/include/boost/date_time/date_defs.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_day_of_year.hpp \ - /opt/local/include/boost/date_time/gregorian_calendar.hpp \ - /opt/local/include/boost/date_time/gregorian_calendar.ipp \ - /opt/local/include/boost/date_time/gregorian/greg_ymd.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_day.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_year.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_month.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_duration.hpp \ - /opt/local/include/boost/date_time/date_duration.hpp \ - /opt/local/include/boost/date_time/date_duration_types.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_duration_types.hpp \ - /opt/local/include/boost/date_time/gregorian/greg_date.hpp \ - /opt/local/include/boost/date_time/adjust_functors.hpp \ - /opt/local/include/boost/date_time/wrapping_int.hpp \ - /opt/local/include/boost/date_time/date_generators.hpp \ - /opt/local/include/boost/date_time/date_clock_device.hpp \ - /opt/local/include/boost/date_time/date_iterator.hpp \ - /opt/local/include/boost/date_time/time_system_split.hpp \ - /opt/local/include/boost/date_time/time_system_counted.hpp \ - /opt/local/include/boost/date_time/time.hpp \ - /opt/local/include/boost/date_time/posix_time/date_duration_operators.hpp \ - /opt/local/include/boost/date_time/posix_time/posix_time_duration.hpp \ - /opt/local/include/boost/date_time/posix_time/time_period.hpp \ - /opt/local/include/boost/date_time/time_iterator.hpp \ - /opt/local/include/boost/date_time/dst_rules.hpp \ - /opt/local/include/boost/chrono/time_point.hpp \ - /opt/local/include/boost/chrono/duration.hpp \ - /opt/local/include/boost/chrono/config.hpp \ - /opt/local/include/boost/chrono/detail/static_assert.hpp \ - /opt/local/include/boost/mpl/logical.hpp \ - /opt/local/include/boost/mpl/or.hpp \ - /opt/local/include/boost/mpl/aux_/config/use_preprocessed.hpp \ - /opt/local/include/boost/mpl/aux_/nested_type_wknd.hpp \ - /opt/local/include/boost/mpl/aux_/include_preprocessed.hpp \ - /opt/local/include/boost/mpl/aux_/config/compiler.hpp \ - /opt/local/include/boost/preprocessor/stringize.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/or.hpp \ - /opt/local/include/boost/mpl/and.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/and.hpp \ - /opt/local/include/boost/mpl/not.hpp \ - /opt/local/include/boost/ratio/ratio.hpp \ - /opt/local/include/boost/ratio/config.hpp \ - /opt/local/include/boost/ratio/detail/mpl/abs.hpp \ - /opt/local/include/boost/ratio/detail/mpl/sign.hpp \ - /opt/local/include/boost/ratio/detail/mpl/gcd.hpp \ - /opt/local/include/boost/mpl/aux_/largest_int.hpp \ - /opt/local/include/boost/mpl/aux_/config/dependent_nttp.hpp \ - /opt/local/include/boost/ratio/detail/mpl/lcm.hpp \ - /opt/local/include/boost/integer_traits.hpp \ - /opt/local/include/boost/ratio/ratio_fwd.hpp \ - /opt/local/include/boost/ratio/detail/overflow_helpers.hpp \ - /opt/local/include/boost/type_traits/common_type.hpp \ - /opt/local/include/boost/typeof/typeof.hpp \ - /opt/local/include/boost/typeof/message.hpp \ - /opt/local/include/boost/typeof/native.hpp \ - /opt/local/include/boost/type_traits/is_floating_point.hpp \ - /opt/local/include/boost/type_traits/is_unsigned.hpp \ - /opt/local/include/boost/chrono/detail/is_evenly_divisible_by.hpp \ - /opt/local/include/boost/thread/mutex.hpp \ - /opt/local/include/boost/thread/pthread/mutex.hpp \ - /opt/local/include/boost/thread/xtime.hpp \ - /opt/local/include/boost/date_time/posix_time/conversion.hpp \ - /opt/local/include/boost/date_time/gregorian/conversion.hpp \ - /opt/local/include/boost/thread/pthread/timespec.hpp \ - /opt/local/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp \ - /opt/local/include/boost/chrono/system_clocks.hpp \ - /opt/local/include/boost/chrono/detail/system.hpp \ - /opt/local/include/boost/version.hpp \ - /opt/local/include/boost/chrono/clock_string.hpp \ - /opt/local/include/boost/chrono/ceil.hpp \ - /opt/local/include/boost/thread/pthread/condition_variable_fwd.hpp \ - /opt/local/include/boost/thread/cv_status.hpp \ - /opt/local/include/boost/detail/scoped_enum_emulation.hpp \ - /opt/local/include/boost/enable_shared_from_this.hpp \ - /opt/local/include/boost/smart_ptr/enable_shared_from_this.hpp \ - /opt/local/include/boost/smart_ptr/weak_ptr.hpp \ - /opt/local/include/boost/optional.hpp \ - /opt/local/include/boost/optional/optional.hpp \ - /opt/local/include/boost/type.hpp \ - /opt/local/include/boost/type_traits/alignment_of.hpp \ - /opt/local/include/boost/type_traits/detail/size_t_trait_def.hpp \ - /opt/local/include/boost/mpl/size_t.hpp \ - /opt/local/include/boost/mpl/size_t_fwd.hpp \ - /opt/local/include/boost/type_traits/detail/size_t_trait_undef.hpp \ - /opt/local/include/boost/type_traits/has_nothrow_constructor.hpp \ - /opt/local/include/boost/type_traits/has_trivial_constructor.hpp \ - /opt/local/include/boost/type_traits/type_with_alignment.hpp \ - /opt/local/include/boost/preprocessor/list/for_each_i.hpp \ - /opt/local/include/boost/preprocessor/repetition/for.hpp \ - /opt/local/include/boost/preprocessor/repetition/detail/for.hpp \ - /opt/local/include/boost/preprocessor/tuple/to_list.hpp \ - /opt/local/include/boost/preprocessor/list/transform.hpp \ - /opt/local/include/boost/preprocessor/list/append.hpp \ - /opt/local/include/boost/detail/reference_content.hpp \ - /opt/local/include/boost/mpl/void.hpp \ - /opt/local/include/boost/none.hpp /opt/local/include/boost/none_t.hpp \ - /opt/local/include/boost/utility/swap.hpp \ - /opt/local/include/boost/utility/addressof.hpp \ - /opt/local/include/boost/utility/compare_pointees.hpp \ - /opt/local/include/boost/utility/in_place_factory.hpp \ - /opt/local/include/boost/utility/detail/in_place_factory_prefix.hpp \ - /opt/local/include/boost/preprocessor/punctuation/paren.hpp \ - /opt/local/include/boost/preprocessor/iteration/iterate.hpp \ - /opt/local/include/boost/preprocessor/array/elem.hpp \ - /opt/local/include/boost/preprocessor/array/data.hpp \ - /opt/local/include/boost/preprocessor/array/size.hpp \ - /opt/local/include/boost/preprocessor/slot/slot.hpp \ - /opt/local/include/boost/preprocessor/slot/detail/def.hpp \ - /opt/local/include/boost/preprocessor/repetition/enum.hpp \ - /opt/local/include/boost/preprocessor/repetition/enum_params.hpp \ - /opt/local/include/boost/preprocessor/repetition/enum_binary_params.hpp \ - /opt/local/include/boost/preprocessor/repetition/enum_trailing_params.hpp \ - /opt/local/include/boost/preprocessor/iteration/detail/iter/forward1.hpp \ - /opt/local/include/boost/preprocessor/iteration/detail/bounds/lower1.hpp \ - /opt/local/include/boost/preprocessor/slot/detail/shared.hpp \ - /opt/local/include/boost/preprocessor/iteration/detail/bounds/upper1.hpp \ - /opt/local/include/boost/utility/detail/in_place_factory_suffix.hpp \ - /opt/local/include/boost/optional/optional_fwd.hpp \ - /opt/local/include/boost/thread/detail/thread.hpp \ - /opt/local/include/boost/thread/detail/thread_heap_alloc.hpp \ - /opt/local/include/boost/thread/pthread/thread_heap_alloc.hpp \ - /opt/local/include/boost/thread/detail/make_tuple_indices.hpp \ - /opt/local/include/boost/thread/detail/invoke.hpp \ - /opt/local/include/boost/thread/detail/is_convertible.hpp \ - /opt/local/include/boost/ref.hpp /opt/local/include/boost/bind.hpp \ - /opt/local/include/boost/bind/bind.hpp \ - /opt/local/include/boost/mem_fn.hpp \ - /opt/local/include/boost/bind/mem_fn.hpp \ - /opt/local/include/boost/get_pointer.hpp \ - /opt/local/include/boost/bind/mem_fn_template.hpp \ - /opt/local/include/boost/bind/mem_fn_cc.hpp \ - /opt/local/include/boost/is_placeholder.hpp \ - /opt/local/include/boost/bind/arg.hpp \ - /opt/local/include/boost/visit_each.hpp \ - /opt/local/include/boost/bind/storage.hpp \ - /opt/local/include/boost/bind/bind_template.hpp \ - /opt/local/include/boost/bind/bind_cc.hpp \ - /opt/local/include/boost/bind/bind_mf_cc.hpp \ - /opt/local/include/boost/bind/bind_mf2_cc.hpp \ - /opt/local/include/boost/bind/placeholders.hpp \ - /opt/local/include/boost/io/ios_state.hpp \ - /opt/local/include/boost/io_fwd.hpp \ - /opt/local/include/boost/functional/hash.hpp \ - /opt/local/include/boost/functional/hash/hash.hpp \ - /opt/local/include/boost/functional/hash/hash_fwd.hpp \ - /opt/local/include/boost/functional/hash/detail/hash_float.hpp \ - /opt/local/include/boost/functional/hash/detail/float_functions.hpp \ - /opt/local/include/boost/functional/hash/detail/limits.hpp \ - /opt/local/include/boost/integer/static_log2.hpp \ - /opt/local/include/boost/integer_fwd.hpp \ - /opt/local/include/boost/functional/hash/extensions.hpp \ - /opt/local/include/boost/detail/container_fwd.hpp \ - /opt/local/include/boost/preprocessor/repetition/repeat_from_to.hpp \ - /opt/local/include/boost/thread/detail/thread_interruption.hpp \ - /opt/local/include/boost/thread/v2/thread.hpp \ - /opt/local/include/boost/thread/condition_variable.hpp \ - /opt/local/include/boost/thread/pthread/condition_variable.hpp \ - /opt/local/include/boost/thread/detail/thread_group.hpp \ - /opt/local/include/boost/thread/shared_mutex.hpp \ - /opt/local/include/boost/thread/pthread/shared_mutex.hpp \ - /opt/local/include/boost/thread/once.hpp \ - /opt/local/include/boost/thread/pthread/once_atomic.hpp \ - /opt/local/include/boost/detail/no_exceptions_support.hpp \ - /opt/local/include/boost/atomic.hpp \ - /opt/local/include/boost/atomic/atomic.hpp \ - /opt/local/include/boost/atomic/detail/config.hpp \ - /opt/local/include/boost/atomic/detail/platform.hpp \ - /opt/local/include/boost/atomic/detail/gcc-atomic.hpp \ - /opt/local/include/boost/atomic/detail/base.hpp \ - /opt/local/include/boost/atomic/detail/lockpool.hpp \ - /opt/local/include/boost/atomic/detail/link.hpp \ - /opt/local/include/boost/atomic/detail/type-classification.hpp \ - /opt/local/include/boost/type_traits/is_signed.hpp \ - /opt/local/include/boost/thread/recursive_mutex.hpp \ - /opt/local/include/boost/thread/pthread/recursive_mutex.hpp \ - /opt/local/include/boost/thread/tss.hpp \ - /opt/local/include/boost/thread/locks.hpp \ - /opt/local/include/boost/thread/lock_algorithms.hpp \ - /opt/local/include/boost/thread/barrier.hpp \ - /opt/local/include/boost/function.hpp \ - /opt/local/include/boost/preprocessor/iterate.hpp \ - /opt/local/include/boost/function/detail/prologue.hpp \ - /opt/local/include/boost/config/no_tr1/functional.hpp \ - /opt/local/include/boost/function/function_base.hpp \ - /opt/local/include/boost/integer.hpp \ - /opt/local/include/boost/type_traits/composite_traits.hpp \ - /opt/local/include/boost/type_traits/is_union.hpp \ - /opt/local/include/boost/function_equal.hpp \ - /opt/local/include/boost/function/function_fwd.hpp \ - /opt/local/include/boost/preprocessor/enum.hpp \ - /opt/local/include/boost/preprocessor/enum_params.hpp \ - /opt/local/include/boost/function/detail/function_iterate.hpp \ - /opt/local/include/boost/function/detail/maybe_include.hpp \ - /opt/local/include/boost/function/function_template.hpp \ - /opt/local/include/boost/utility/result_of.hpp \ - /opt/local/include/boost/preprocessor/repetition/enum_shifted_params.hpp \ - /opt/local/include/boost/preprocessor/facilities/intercept.hpp \ - /opt/local/include/boost/mpl/has_xxx.hpp \ - /opt/local/include/boost/mpl/aux_/type_wrapper.hpp \ - /opt/local/include/boost/mpl/aux_/yes_no.hpp \ - /opt/local/include/boost/mpl/aux_/config/arrays.hpp \ - /opt/local/include/boost/mpl/aux_/config/has_xxx.hpp \ - /opt/local/include/boost/mpl/aux_/config/msvc_typename.hpp \ - /opt/local/include/boost/utility/detail/result_of_iterate.hpp \ - /opt/local/include/boost/thread/future.hpp \ - /opt/local/include/boost/thread/detail/async_func.hpp \ - /opt/local/include/boost/tuple/tuple.hpp \ - /opt/local/include/boost/tuple/detail/tuple_basic.hpp \ - /opt/local/include/boost/type_traits/cv_traits.hpp \ - /opt/local/include/boost/type_traits/add_const.hpp \ - /opt/local/include/boost/type_traits/add_volatile.hpp \ - /opt/local/include/boost/type_traits/add_cv.hpp \ - /opt/local/include/boost/type_traits/remove_const.hpp \ - /opt/local/include/boost/type_traits/remove_volatile.hpp \ - /opt/local/include/boost/type_traits/function_traits.hpp \ - /opt/local/include/boost/exception_ptr.hpp \ - /opt/local/include/boost/exception/detail/exception_ptr.hpp \ - /opt/local/include/boost/scoped_ptr.hpp \ - /opt/local/include/boost/smart_ptr/scoped_ptr.hpp \ - /opt/local/include/boost/type_traits/is_fundamental.hpp \ - /opt/local/include/boost/scoped_array.hpp \ - /opt/local/include/boost/smart_ptr/scoped_array.hpp \ - /opt/local/include/boost/next_prior.hpp \ - /opt/local/include/boost/thread/future_error_code.hpp \ - /opt/local/include/boost/detail/atomic_undef_macros.hpp \ - /opt/local/include/boost/detail/atomic_redef_macros.hpp \ - /opt/local/include/boost/filesystem.hpp \ - /opt/local/include/boost/filesystem/config.hpp \ - /opt/local/include/boost/filesystem/path.hpp \ - /opt/local/include/boost/filesystem/path_traits.hpp \ - /opt/local/include/boost/iterator/iterator_facade.hpp \ - /opt/local/include/boost/iterator/interoperable.hpp \ - /opt/local/include/boost/iterator/detail/config_def.hpp \ - /opt/local/include/boost/iterator/detail/config_undef.hpp \ - /opt/local/include/boost/iterator/iterator_traits.hpp \ - /opt/local/include/boost/detail/iterator.hpp \ - /opt/local/include/boost/iterator/detail/facade_iterator_category.hpp \ - /opt/local/include/boost/iterator/iterator_categories.hpp \ - /opt/local/include/boost/mpl/placeholders.hpp \ - /opt/local/include/boost/mpl/arg.hpp \ - /opt/local/include/boost/mpl/arg_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/na_assert.hpp \ - /opt/local/include/boost/mpl/assert.hpp \ - /opt/local/include/boost/mpl/aux_/config/pp_counter.hpp \ - /opt/local/include/boost/mpl/aux_/arity_spec.hpp \ - /opt/local/include/boost/mpl/aux_/arg_typedef.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/arg.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/placeholders.hpp \ - /opt/local/include/boost/detail/indirect_traits.hpp \ - /opt/local/include/boost/type_traits/remove_pointer.hpp \ - /opt/local/include/boost/iterator/detail/enable_if.hpp \ - /opt/local/include/boost/mpl/always.hpp \ - /opt/local/include/boost/mpl/apply.hpp \ - /opt/local/include/boost/mpl/apply_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply_fwd.hpp \ - /opt/local/include/boost/mpl/apply_wrap.hpp \ - /opt/local/include/boost/mpl/aux_/has_apply.hpp \ - /opt/local/include/boost/mpl/aux_/config/has_apply.hpp \ - /opt/local/include/boost/mpl/aux_/msvc_never_true.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp \ - /opt/local/include/boost/mpl/lambda.hpp \ - /opt/local/include/boost/mpl/bind.hpp \ - /opt/local/include/boost/mpl/bind_fwd.hpp \ - /opt/local/include/boost/mpl/aux_/config/bind.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/bind_fwd.hpp \ - /opt/local/include/boost/mpl/next.hpp \ - /opt/local/include/boost/mpl/next_prior.hpp \ - /opt/local/include/boost/mpl/aux_/common_name_wknd.hpp \ - /opt/local/include/boost/mpl/protect.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/bind.hpp \ - /opt/local/include/boost/mpl/aux_/full_lambda.hpp \ - /opt/local/include/boost/mpl/quote.hpp \ - /opt/local/include/boost/mpl/aux_/has_type.hpp \ - /opt/local/include/boost/mpl/aux_/config/bcc.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/quote.hpp \ - /opt/local/include/boost/mpl/aux_/template_arity.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/template_arity.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/full_lambda.hpp \ - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply.hpp \ - /opt/local/include/boost/io/detail/quoted_manip.hpp \ - /opt/local/include/boost/functional/hash_fwd.hpp \ - /opt/local/include/boost/filesystem/operations.hpp \ - /opt/local/include/boost/detail/bitmask.hpp \ - /opt/local/include/boost/filesystem/convenience.hpp \ - /opt/local/include/openssl/sha.h /opt/local/include/openssl/e_os2.h \ - /opt/local/include/openssl/opensslconf.h \ - /opt/local/include/openssl/ripemd.h \ - /path/to/triangles/src/netbase.h \ - /path/to/triangles/src/serialize.h \ - /opt/local/include/boost/tuple/tuple_comparison.hpp \ - /opt/local/include/boost/tuple/tuple_io.hpp \ - /path/to/triangles/src/allocators.h \ - /path/to/triangles/src/version.h \ - /path/to/triangles/src/clientversion.h \ - /path/to/triangles/src/compat.h -tor/anonymize.cpp : - /path/to/triangles/src/tor/anonymize.h : - /path/to/triangles/src/util.h : - /path/to/triangles/src/uint256.h : - /opt/local/include/boost/thread.hpp : - /opt/local/include/boost/thread/thread.hpp : - /opt/local/include/boost/thread/thread_only.hpp : - /opt/local/include/boost/thread/detail/platform.hpp : - /opt/local/include/boost/config.hpp : - /opt/local/include/boost/config/user.hpp : - /opt/local/include/boost/config/select_compiler_config.hpp : - /opt/local/include/boost/config/compiler/clang.hpp : - /opt/local/include/boost/config/select_stdlib_config.hpp : - /opt/local/include/boost/config/no_tr1/utility.hpp : - /opt/local/include/boost/config/stdlib/libstdcpp3.hpp : - /opt/local/include/boost/config/select_platform_config.hpp : - /opt/local/include/boost/config/platform/macos.hpp : - /opt/local/include/boost/config/posix_features.hpp : - /opt/local/include/boost/config/suffix.hpp : - /opt/local/include/boost/config/requires_threads.hpp : - /opt/local/include/boost/thread/pthread/thread_data.hpp : - /opt/local/include/boost/thread/detail/config.hpp : - /opt/local/include/boost/detail/workaround.hpp : - /opt/local/include/boost/config/auto_link.hpp : - /opt/local/include/boost/thread/exceptions.hpp : - /opt/local/include/boost/system/system_error.hpp : - /opt/local/include/boost/system/error_code.hpp : - /opt/local/include/boost/system/config.hpp : - /opt/local/include/boost/system/api_config.hpp : - /opt/local/include/boost/cstdint.hpp : - /opt/local/include/boost/assert.hpp : - /opt/local/include/boost/current_function.hpp : - /opt/local/include/boost/operators.hpp : - /opt/local/include/boost/iterator.hpp : - /opt/local/include/boost/noncopyable.hpp : - /opt/local/include/boost/utility/enable_if.hpp : - /opt/local/include/boost/cerrno.hpp : - /opt/local/include/boost/config/abi_prefix.hpp : - /opt/local/include/boost/config/abi_suffix.hpp : - /opt/local/include/boost/thread/lock_guard.hpp : - /opt/local/include/boost/thread/detail/delete.hpp : - /opt/local/include/boost/thread/detail/move.hpp : - /opt/local/include/boost/type_traits/is_convertible.hpp : - /opt/local/include/boost/type_traits/intrinsics.hpp : - /opt/local/include/boost/type_traits/config.hpp : - /opt/local/include/boost/type_traits/is_same.hpp : - /opt/local/include/boost/type_traits/detail/bool_trait_def.hpp : - /opt/local/include/boost/type_traits/detail/template_arity_spec.hpp : - /opt/local/include/boost/mpl/int.hpp : - /opt/local/include/boost/mpl/int_fwd.hpp : - /opt/local/include/boost/mpl/aux_/adl_barrier.hpp : - /opt/local/include/boost/mpl/aux_/config/adl.hpp : - /opt/local/include/boost/mpl/aux_/config/msvc.hpp : - /opt/local/include/boost/mpl/aux_/config/intel.hpp : - /opt/local/include/boost/mpl/aux_/config/gcc.hpp : - /opt/local/include/boost/mpl/aux_/config/workaround.hpp : - /opt/local/include/boost/mpl/aux_/nttp_decl.hpp : - /opt/local/include/boost/mpl/aux_/config/nttp.hpp : - /opt/local/include/boost/mpl/aux_/integral_wrapper.hpp : - /opt/local/include/boost/mpl/integral_c_tag.hpp : - /opt/local/include/boost/mpl/aux_/config/static_constant.hpp : - /opt/local/include/boost/mpl/aux_/static_cast.hpp : - /opt/local/include/boost/preprocessor/cat.hpp : - /opt/local/include/boost/preprocessor/config/config.hpp : - /opt/local/include/boost/mpl/aux_/template_arity_fwd.hpp : - /opt/local/include/boost/mpl/aux_/preprocessor/params.hpp : - /opt/local/include/boost/mpl/aux_/config/preprocessor.hpp : - /opt/local/include/boost/preprocessor/comma_if.hpp : - /opt/local/include/boost/preprocessor/punctuation/comma_if.hpp : - /opt/local/include/boost/preprocessor/control/if.hpp : - /opt/local/include/boost/preprocessor/control/iif.hpp : - /opt/local/include/boost/preprocessor/logical/bool.hpp : - /opt/local/include/boost/preprocessor/facilities/empty.hpp : - /opt/local/include/boost/preprocessor/punctuation/comma.hpp : - /opt/local/include/boost/preprocessor/repeat.hpp : - /opt/local/include/boost/preprocessor/repetition/repeat.hpp : - /opt/local/include/boost/preprocessor/debug/error.hpp : - /opt/local/include/boost/preprocessor/detail/auto_rec.hpp : - /opt/local/include/boost/preprocessor/tuple/eat.hpp : - /opt/local/include/boost/preprocessor/inc.hpp : - /opt/local/include/boost/preprocessor/arithmetic/inc.hpp : - /opt/local/include/boost/mpl/aux_/config/lambda.hpp : - /opt/local/include/boost/mpl/aux_/config/ttp.hpp : - /opt/local/include/boost/mpl/aux_/config/ctps.hpp : - /opt/local/include/boost/mpl/aux_/config/overload_resolution.hpp : - /opt/local/include/boost/type_traits/integral_constant.hpp : - /opt/local/include/boost/mpl/bool.hpp : - /opt/local/include/boost/mpl/bool_fwd.hpp : - /opt/local/include/boost/mpl/integral_c.hpp : - /opt/local/include/boost/mpl/integral_c_fwd.hpp : - /opt/local/include/boost/mpl/aux_/lambda_support.hpp : - /opt/local/include/boost/type_traits/detail/bool_trait_undef.hpp : - /opt/local/include/boost/type_traits/is_reference.hpp : - /opt/local/include/boost/type_traits/is_lvalue_reference.hpp : - /opt/local/include/boost/type_traits/is_rvalue_reference.hpp : - /opt/local/include/boost/type_traits/ice.hpp : - /opt/local/include/boost/type_traits/detail/yes_no_type.hpp : - /opt/local/include/boost/type_traits/detail/ice_or.hpp : - /opt/local/include/boost/type_traits/detail/ice_and.hpp : - /opt/local/include/boost/type_traits/detail/ice_not.hpp : - /opt/local/include/boost/type_traits/detail/ice_eq.hpp : - /opt/local/include/boost/type_traits/is_volatile.hpp : - /opt/local/include/boost/type_traits/detail/cv_traits_impl.hpp : - /opt/local/include/boost/type_traits/is_abstract.hpp : - /opt/local/include/boost/type_traits/remove_reference.hpp : - /opt/local/include/boost/type_traits/broken_compiler_spec.hpp : - /opt/local/include/boost/type_traits/detail/type_trait_def.hpp : - /opt/local/include/boost/type_traits/detail/type_trait_undef.hpp : - /opt/local/include/boost/type_traits/remove_cv.hpp : - /opt/local/include/boost/type_traits/decay.hpp : - /opt/local/include/boost/type_traits/is_array.hpp : - /opt/local/include/boost/type_traits/is_function.hpp : - /opt/local/include/boost/type_traits/detail/false_result.hpp : - /opt/local/include/boost/type_traits/detail/is_function_ptr_helper.hpp : - /opt/local/include/boost/type_traits/remove_bounds.hpp : - /opt/local/include/boost/type_traits/add_pointer.hpp : - /opt/local/include/boost/mpl/eval_if.hpp : - /opt/local/include/boost/mpl/if.hpp : - /opt/local/include/boost/mpl/aux_/value_wknd.hpp : - /opt/local/include/boost/mpl/aux_/config/integral.hpp : - /opt/local/include/boost/mpl/aux_/config/eti.hpp : - /opt/local/include/boost/mpl/aux_/na_spec.hpp : - /opt/local/include/boost/mpl/lambda_fwd.hpp : - /opt/local/include/boost/mpl/void_fwd.hpp : - /opt/local/include/boost/mpl/aux_/na.hpp : - /opt/local/include/boost/mpl/aux_/na_fwd.hpp : - /opt/local/include/boost/mpl/aux_/lambda_arity_param.hpp : - /opt/local/include/boost/mpl/aux_/arity.hpp : - /opt/local/include/boost/mpl/aux_/config/dtp.hpp : - /opt/local/include/boost/mpl/aux_/preprocessor/enum.hpp : - /opt/local/include/boost/mpl/aux_/preprocessor/def_params_tail.hpp : - /opt/local/include/boost/mpl/limits/arity.hpp : - /opt/local/include/boost/preprocessor/logical/and.hpp : - /opt/local/include/boost/preprocessor/logical/bitand.hpp : - /opt/local/include/boost/preprocessor/identity.hpp : - /opt/local/include/boost/preprocessor/facilities/identity.hpp : - /opt/local/include/boost/preprocessor/empty.hpp : - /opt/local/include/boost/preprocessor/arithmetic/add.hpp : - /opt/local/include/boost/preprocessor/arithmetic/dec.hpp : - /opt/local/include/boost/preprocessor/control/while.hpp : - /opt/local/include/boost/preprocessor/list/fold_left.hpp : - /opt/local/include/boost/preprocessor/list/detail/fold_left.hpp : - /opt/local/include/boost/preprocessor/control/expr_iif.hpp : - /opt/local/include/boost/preprocessor/list/adt.hpp : - /opt/local/include/boost/preprocessor/detail/is_binary.hpp : - /opt/local/include/boost/preprocessor/detail/check.hpp : - /opt/local/include/boost/preprocessor/logical/compl.hpp : - /opt/local/include/boost/preprocessor/list/fold_right.hpp : - /opt/local/include/boost/preprocessor/list/detail/fold_right.hpp : - /opt/local/include/boost/preprocessor/list/reverse.hpp : - /opt/local/include/boost/preprocessor/control/detail/while.hpp : - /opt/local/include/boost/preprocessor/tuple/elem.hpp : - /opt/local/include/boost/preprocessor/facilities/overload.hpp : - /opt/local/include/boost/preprocessor/variadic/size.hpp : - /opt/local/include/boost/preprocessor/tuple/rem.hpp : - /opt/local/include/boost/preprocessor/variadic/elem.hpp : - /opt/local/include/boost/preprocessor/arithmetic/sub.hpp : - /opt/local/include/boost/mpl/identity.hpp : - /opt/local/include/boost/move/utility.hpp : - /opt/local/include/boost/move/detail/config_begin.hpp : - /opt/local/include/boost/move/core.hpp : - /opt/local/include/boost/move/detail/meta_utils.hpp : - /opt/local/include/boost/move/detail/config_end.hpp : - /opt/local/include/boost/move/traits.hpp : - /opt/local/include/boost/type_traits/has_trivial_destructor.hpp : - /opt/local/include/boost/type_traits/is_pod.hpp : - /opt/local/include/boost/type_traits/is_void.hpp : - /opt/local/include/boost/type_traits/is_scalar.hpp : - /opt/local/include/boost/type_traits/is_arithmetic.hpp : - /opt/local/include/boost/type_traits/is_integral.hpp : - /opt/local/include/boost/type_traits/is_float.hpp : - /opt/local/include/boost/type_traits/is_enum.hpp : - /opt/local/include/boost/type_traits/is_pointer.hpp : - /opt/local/include/boost/type_traits/is_member_pointer.hpp : - /opt/local/include/boost/type_traits/is_member_function_pointer.hpp : - /opt/local/include/boost/type_traits/detail/is_mem_fun_pointer_impl.hpp : - /opt/local/include/boost/type_traits/is_nothrow_move_constructible.hpp : - /opt/local/include/boost/type_traits/has_trivial_move_constructor.hpp : - /opt/local/include/boost/type_traits/has_nothrow_copy.hpp : - /opt/local/include/boost/type_traits/has_trivial_copy.hpp : - /opt/local/include/boost/utility/declval.hpp : - /opt/local/include/boost/type_traits/add_rvalue_reference.hpp : - /opt/local/include/boost/type_traits/is_nothrow_move_assignable.hpp : - /opt/local/include/boost/type_traits/has_trivial_move_assign.hpp : - /opt/local/include/boost/type_traits/is_const.hpp : - /opt/local/include/boost/type_traits/has_nothrow_assign.hpp : - /opt/local/include/boost/type_traits/has_trivial_assign.hpp : - /opt/local/include/boost/thread/detail/lockable_wrapper.hpp : - /opt/local/include/boost/thread/lock_options.hpp : - /opt/local/include/boost/thread/lock_types.hpp : - /opt/local/include/boost/thread/lockable_traits.hpp : - /opt/local/include/boost/type_traits/is_class.hpp : - /opt/local/include/boost/thread/thread_time.hpp : - /opt/local/include/boost/date_time/time_clock.hpp : - /opt/local/include/boost/date_time/c_time.hpp : - /opt/local/include/boost/throw_exception.hpp : - /opt/local/include/boost/date_time/compiler_config.hpp : - /opt/local/include/boost/date_time/locale_config.hpp : - /opt/local/include/boost/shared_ptr.hpp : - /opt/local/include/boost/smart_ptr/shared_ptr.hpp : - /opt/local/include/boost/config/no_tr1/memory.hpp : - /opt/local/include/boost/checked_delete.hpp : - /opt/local/include/boost/smart_ptr/detail/shared_count.hpp : - /opt/local/include/boost/smart_ptr/bad_weak_ptr.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_counted_base.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_has_sync.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp : - /opt/local/include/boost/detail/sp_typeinfo.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_counted_impl.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_convertible.hpp : - /opt/local/include/boost/smart_ptr/detail/sp_nullptr_t.hpp : - /opt/local/include/boost/smart_ptr/detail/spinlock_pool.hpp : - /opt/local/include/boost/smart_ptr/detail/spinlock.hpp : - /opt/local/include/boost/smart_ptr/detail/spinlock_sync.hpp : - /opt/local/include/boost/smart_ptr/detail/yield_k.hpp : - /opt/local/include/boost/memory_order.hpp : - /opt/local/include/boost/smart_ptr/detail/operator_bool.hpp : - /opt/local/include/boost/date_time/microsec_time_clock.hpp : - /opt/local/include/boost/date_time/filetime_functions.hpp : - /opt/local/include/boost/date_time/posix_time/posix_time_types.hpp : - /opt/local/include/boost/date_time/posix_time/ptime.hpp : - /opt/local/include/boost/date_time/posix_time/posix_time_system.hpp : - /opt/local/include/boost/date_time/posix_time/posix_time_config.hpp : - /opt/local/include/boost/limits.hpp : - /opt/local/include/boost/config/no_tr1/cmath.hpp : - /opt/local/include/boost/date_time/time_duration.hpp : - /opt/local/include/boost/static_assert.hpp : - /opt/local/include/boost/date_time/time_defs.hpp : - /opt/local/include/boost/date_time/special_defs.hpp : - /opt/local/include/boost/date_time/time_resolution_traits.hpp : - /opt/local/include/boost/date_time/int_adapter.hpp : - /opt/local/include/boost/date_time/gregorian/gregorian_types.hpp : - /opt/local/include/boost/date_time/date.hpp : - /opt/local/include/boost/date_time/year_month_day.hpp : - /opt/local/include/boost/date_time/period.hpp : - /opt/local/include/boost/date_time/gregorian/greg_calendar.hpp : - /opt/local/include/boost/date_time/gregorian/greg_weekday.hpp : - /opt/local/include/boost/date_time/constrained_value.hpp : - /opt/local/include/boost/type_traits/is_base_of.hpp : - /opt/local/include/boost/type_traits/is_base_and_derived.hpp : - /opt/local/include/boost/date_time/date_defs.hpp : - /opt/local/include/boost/date_time/gregorian/greg_day_of_year.hpp : - /opt/local/include/boost/date_time/gregorian_calendar.hpp : - /opt/local/include/boost/date_time/gregorian_calendar.ipp : - /opt/local/include/boost/date_time/gregorian/greg_ymd.hpp : - /opt/local/include/boost/date_time/gregorian/greg_day.hpp : - /opt/local/include/boost/date_time/gregorian/greg_year.hpp : - /opt/local/include/boost/date_time/gregorian/greg_month.hpp : - /opt/local/include/boost/date_time/gregorian/greg_duration.hpp : - /opt/local/include/boost/date_time/date_duration.hpp : - /opt/local/include/boost/date_time/date_duration_types.hpp : - /opt/local/include/boost/date_time/gregorian/greg_duration_types.hpp : - /opt/local/include/boost/date_time/gregorian/greg_date.hpp : - /opt/local/include/boost/date_time/adjust_functors.hpp : - /opt/local/include/boost/date_time/wrapping_int.hpp : - /opt/local/include/boost/date_time/date_generators.hpp : - /opt/local/include/boost/date_time/date_clock_device.hpp : - /opt/local/include/boost/date_time/date_iterator.hpp : - /opt/local/include/boost/date_time/time_system_split.hpp : - /opt/local/include/boost/date_time/time_system_counted.hpp : - /opt/local/include/boost/date_time/time.hpp : - /opt/local/include/boost/date_time/posix_time/date_duration_operators.hpp : - /opt/local/include/boost/date_time/posix_time/posix_time_duration.hpp : - /opt/local/include/boost/date_time/posix_time/time_period.hpp : - /opt/local/include/boost/date_time/time_iterator.hpp : - /opt/local/include/boost/date_time/dst_rules.hpp : - /opt/local/include/boost/chrono/time_point.hpp : - /opt/local/include/boost/chrono/duration.hpp : - /opt/local/include/boost/chrono/config.hpp : - /opt/local/include/boost/chrono/detail/static_assert.hpp : - /opt/local/include/boost/mpl/logical.hpp : - /opt/local/include/boost/mpl/or.hpp : - /opt/local/include/boost/mpl/aux_/config/use_preprocessed.hpp : - /opt/local/include/boost/mpl/aux_/nested_type_wknd.hpp : - /opt/local/include/boost/mpl/aux_/include_preprocessed.hpp : - /opt/local/include/boost/mpl/aux_/config/compiler.hpp : - /opt/local/include/boost/preprocessor/stringize.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/or.hpp : - /opt/local/include/boost/mpl/and.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/and.hpp : - /opt/local/include/boost/mpl/not.hpp : - /opt/local/include/boost/ratio/ratio.hpp : - /opt/local/include/boost/ratio/config.hpp : - /opt/local/include/boost/ratio/detail/mpl/abs.hpp : - /opt/local/include/boost/ratio/detail/mpl/sign.hpp : - /opt/local/include/boost/ratio/detail/mpl/gcd.hpp : - /opt/local/include/boost/mpl/aux_/largest_int.hpp : - /opt/local/include/boost/mpl/aux_/config/dependent_nttp.hpp : - /opt/local/include/boost/ratio/detail/mpl/lcm.hpp : - /opt/local/include/boost/integer_traits.hpp : - /opt/local/include/boost/ratio/ratio_fwd.hpp : - /opt/local/include/boost/ratio/detail/overflow_helpers.hpp : - /opt/local/include/boost/type_traits/common_type.hpp : - /opt/local/include/boost/typeof/typeof.hpp : - /opt/local/include/boost/typeof/message.hpp : - /opt/local/include/boost/typeof/native.hpp : - /opt/local/include/boost/type_traits/is_floating_point.hpp : - /opt/local/include/boost/type_traits/is_unsigned.hpp : - /opt/local/include/boost/chrono/detail/is_evenly_divisible_by.hpp : - /opt/local/include/boost/thread/mutex.hpp : - /opt/local/include/boost/thread/pthread/mutex.hpp : - /opt/local/include/boost/thread/xtime.hpp : - /opt/local/include/boost/date_time/posix_time/conversion.hpp : - /opt/local/include/boost/date_time/gregorian/conversion.hpp : - /opt/local/include/boost/thread/pthread/timespec.hpp : - /opt/local/include/boost/thread/pthread/pthread_mutex_scoped_lock.hpp : - /opt/local/include/boost/chrono/system_clocks.hpp : - /opt/local/include/boost/chrono/detail/system.hpp : - /opt/local/include/boost/version.hpp : - /opt/local/include/boost/chrono/clock_string.hpp : - /opt/local/include/boost/chrono/ceil.hpp : - /opt/local/include/boost/thread/pthread/condition_variable_fwd.hpp : - /opt/local/include/boost/thread/cv_status.hpp : - /opt/local/include/boost/detail/scoped_enum_emulation.hpp : - /opt/local/include/boost/enable_shared_from_this.hpp : - /opt/local/include/boost/smart_ptr/enable_shared_from_this.hpp : - /opt/local/include/boost/smart_ptr/weak_ptr.hpp : - /opt/local/include/boost/optional.hpp : - /opt/local/include/boost/optional/optional.hpp : - /opt/local/include/boost/type.hpp : - /opt/local/include/boost/type_traits/alignment_of.hpp : - /opt/local/include/boost/type_traits/detail/size_t_trait_def.hpp : - /opt/local/include/boost/mpl/size_t.hpp : - /opt/local/include/boost/mpl/size_t_fwd.hpp : - /opt/local/include/boost/type_traits/detail/size_t_trait_undef.hpp : - /opt/local/include/boost/type_traits/has_nothrow_constructor.hpp : - /opt/local/include/boost/type_traits/has_trivial_constructor.hpp : - /opt/local/include/boost/type_traits/type_with_alignment.hpp : - /opt/local/include/boost/preprocessor/list/for_each_i.hpp : - /opt/local/include/boost/preprocessor/repetition/for.hpp : - /opt/local/include/boost/preprocessor/repetition/detail/for.hpp : - /opt/local/include/boost/preprocessor/tuple/to_list.hpp : - /opt/local/include/boost/preprocessor/list/transform.hpp : - /opt/local/include/boost/preprocessor/list/append.hpp : - /opt/local/include/boost/detail/reference_content.hpp : - /opt/local/include/boost/mpl/void.hpp : - /opt/local/include/boost/none.hpp /opt/local/include/boost/none_t.hpp : - /opt/local/include/boost/utility/swap.hpp : - /opt/local/include/boost/utility/addressof.hpp : - /opt/local/include/boost/utility/compare_pointees.hpp : - /opt/local/include/boost/utility/in_place_factory.hpp : - /opt/local/include/boost/utility/detail/in_place_factory_prefix.hpp : - /opt/local/include/boost/preprocessor/punctuation/paren.hpp : - /opt/local/include/boost/preprocessor/iteration/iterate.hpp : - /opt/local/include/boost/preprocessor/array/elem.hpp : - /opt/local/include/boost/preprocessor/array/data.hpp : - /opt/local/include/boost/preprocessor/array/size.hpp : - /opt/local/include/boost/preprocessor/slot/slot.hpp : - /opt/local/include/boost/preprocessor/slot/detail/def.hpp : - /opt/local/include/boost/preprocessor/repetition/enum.hpp : - /opt/local/include/boost/preprocessor/repetition/enum_params.hpp : - /opt/local/include/boost/preprocessor/repetition/enum_binary_params.hpp : - /opt/local/include/boost/preprocessor/repetition/enum_trailing_params.hpp : - /opt/local/include/boost/preprocessor/iteration/detail/iter/forward1.hpp : - /opt/local/include/boost/preprocessor/iteration/detail/bounds/lower1.hpp : - /opt/local/include/boost/preprocessor/slot/detail/shared.hpp : - /opt/local/include/boost/preprocessor/iteration/detail/bounds/upper1.hpp : - /opt/local/include/boost/utility/detail/in_place_factory_suffix.hpp : - /opt/local/include/boost/optional/optional_fwd.hpp : - /opt/local/include/boost/thread/detail/thread.hpp : - /opt/local/include/boost/thread/detail/thread_heap_alloc.hpp : - /opt/local/include/boost/thread/pthread/thread_heap_alloc.hpp : - /opt/local/include/boost/thread/detail/make_tuple_indices.hpp : - /opt/local/include/boost/thread/detail/invoke.hpp : - /opt/local/include/boost/thread/detail/is_convertible.hpp : - /opt/local/include/boost/ref.hpp /opt/local/include/boost/bind.hpp : - /opt/local/include/boost/bind/bind.hpp : - /opt/local/include/boost/mem_fn.hpp : - /opt/local/include/boost/bind/mem_fn.hpp : - /opt/local/include/boost/get_pointer.hpp : - /opt/local/include/boost/bind/mem_fn_template.hpp : - /opt/local/include/boost/bind/mem_fn_cc.hpp : - /opt/local/include/boost/is_placeholder.hpp : - /opt/local/include/boost/bind/arg.hpp : - /opt/local/include/boost/visit_each.hpp : - /opt/local/include/boost/bind/storage.hpp : - /opt/local/include/boost/bind/bind_template.hpp : - /opt/local/include/boost/bind/bind_cc.hpp : - /opt/local/include/boost/bind/bind_mf_cc.hpp : - /opt/local/include/boost/bind/bind_mf2_cc.hpp : - /opt/local/include/boost/bind/placeholders.hpp : - /opt/local/include/boost/io/ios_state.hpp : - /opt/local/include/boost/io_fwd.hpp : - /opt/local/include/boost/functional/hash.hpp : - /opt/local/include/boost/functional/hash/hash.hpp : - /opt/local/include/boost/functional/hash/hash_fwd.hpp : - /opt/local/include/boost/functional/hash/detail/hash_float.hpp : - /opt/local/include/boost/functional/hash/detail/float_functions.hpp : - /opt/local/include/boost/functional/hash/detail/limits.hpp : - /opt/local/include/boost/integer/static_log2.hpp : - /opt/local/include/boost/integer_fwd.hpp : - /opt/local/include/boost/functional/hash/extensions.hpp : - /opt/local/include/boost/detail/container_fwd.hpp : - /opt/local/include/boost/preprocessor/repetition/repeat_from_to.hpp : - /opt/local/include/boost/thread/detail/thread_interruption.hpp : - /opt/local/include/boost/thread/v2/thread.hpp : - /opt/local/include/boost/thread/condition_variable.hpp : - /opt/local/include/boost/thread/pthread/condition_variable.hpp : - /opt/local/include/boost/thread/detail/thread_group.hpp : - /opt/local/include/boost/thread/shared_mutex.hpp : - /opt/local/include/boost/thread/pthread/shared_mutex.hpp : - /opt/local/include/boost/thread/once.hpp : - /opt/local/include/boost/thread/pthread/once_atomic.hpp : - /opt/local/include/boost/detail/no_exceptions_support.hpp : - /opt/local/include/boost/atomic.hpp : - /opt/local/include/boost/atomic/atomic.hpp : - /opt/local/include/boost/atomic/detail/config.hpp : - /opt/local/include/boost/atomic/detail/platform.hpp : - /opt/local/include/boost/atomic/detail/gcc-atomic.hpp : - /opt/local/include/boost/atomic/detail/base.hpp : - /opt/local/include/boost/atomic/detail/lockpool.hpp : - /opt/local/include/boost/atomic/detail/link.hpp : - /opt/local/include/boost/atomic/detail/type-classification.hpp : - /opt/local/include/boost/type_traits/is_signed.hpp : - /opt/local/include/boost/thread/recursive_mutex.hpp : - /opt/local/include/boost/thread/pthread/recursive_mutex.hpp : - /opt/local/include/boost/thread/tss.hpp : - /opt/local/include/boost/thread/locks.hpp : - /opt/local/include/boost/thread/lock_algorithms.hpp : - /opt/local/include/boost/thread/barrier.hpp : - /opt/local/include/boost/function.hpp : - /opt/local/include/boost/preprocessor/iterate.hpp : - /opt/local/include/boost/function/detail/prologue.hpp : - /opt/local/include/boost/config/no_tr1/functional.hpp : - /opt/local/include/boost/function/function_base.hpp : - /opt/local/include/boost/integer.hpp : - /opt/local/include/boost/type_traits/composite_traits.hpp : - /opt/local/include/boost/type_traits/is_union.hpp : - /opt/local/include/boost/function_equal.hpp : - /opt/local/include/boost/function/function_fwd.hpp : - /opt/local/include/boost/preprocessor/enum.hpp : - /opt/local/include/boost/preprocessor/enum_params.hpp : - /opt/local/include/boost/function/detail/function_iterate.hpp : - /opt/local/include/boost/function/detail/maybe_include.hpp : - /opt/local/include/boost/function/function_template.hpp : - /opt/local/include/boost/utility/result_of.hpp : - /opt/local/include/boost/preprocessor/repetition/enum_shifted_params.hpp : - /opt/local/include/boost/preprocessor/facilities/intercept.hpp : - /opt/local/include/boost/mpl/has_xxx.hpp : - /opt/local/include/boost/mpl/aux_/type_wrapper.hpp : - /opt/local/include/boost/mpl/aux_/yes_no.hpp : - /opt/local/include/boost/mpl/aux_/config/arrays.hpp : - /opt/local/include/boost/mpl/aux_/config/has_xxx.hpp : - /opt/local/include/boost/mpl/aux_/config/msvc_typename.hpp : - /opt/local/include/boost/utility/detail/result_of_iterate.hpp : - /opt/local/include/boost/thread/future.hpp : - /opt/local/include/boost/thread/detail/async_func.hpp : - /opt/local/include/boost/tuple/tuple.hpp : - /opt/local/include/boost/tuple/detail/tuple_basic.hpp : - /opt/local/include/boost/type_traits/cv_traits.hpp : - /opt/local/include/boost/type_traits/add_const.hpp : - /opt/local/include/boost/type_traits/add_volatile.hpp : - /opt/local/include/boost/type_traits/add_cv.hpp : - /opt/local/include/boost/type_traits/remove_const.hpp : - /opt/local/include/boost/type_traits/remove_volatile.hpp : - /opt/local/include/boost/type_traits/function_traits.hpp : - /opt/local/include/boost/exception_ptr.hpp : - /opt/local/include/boost/exception/detail/exception_ptr.hpp : - /opt/local/include/boost/scoped_ptr.hpp : - /opt/local/include/boost/smart_ptr/scoped_ptr.hpp : - /opt/local/include/boost/type_traits/is_fundamental.hpp : - /opt/local/include/boost/scoped_array.hpp : - /opt/local/include/boost/smart_ptr/scoped_array.hpp : - /opt/local/include/boost/next_prior.hpp : - /opt/local/include/boost/thread/future_error_code.hpp : - /opt/local/include/boost/detail/atomic_undef_macros.hpp : - /opt/local/include/boost/detail/atomic_redef_macros.hpp : - /opt/local/include/boost/filesystem.hpp : - /opt/local/include/boost/filesystem/config.hpp : - /opt/local/include/boost/filesystem/path.hpp : - /opt/local/include/boost/filesystem/path_traits.hpp : - /opt/local/include/boost/iterator/iterator_facade.hpp : - /opt/local/include/boost/iterator/interoperable.hpp : - /opt/local/include/boost/iterator/detail/config_def.hpp : - /opt/local/include/boost/iterator/detail/config_undef.hpp : - /opt/local/include/boost/iterator/iterator_traits.hpp : - /opt/local/include/boost/detail/iterator.hpp : - /opt/local/include/boost/iterator/detail/facade_iterator_category.hpp : - /opt/local/include/boost/iterator/iterator_categories.hpp : - /opt/local/include/boost/mpl/placeholders.hpp : - /opt/local/include/boost/mpl/arg.hpp : - /opt/local/include/boost/mpl/arg_fwd.hpp : - /opt/local/include/boost/mpl/aux_/na_assert.hpp : - /opt/local/include/boost/mpl/assert.hpp : - /opt/local/include/boost/mpl/aux_/config/pp_counter.hpp : - /opt/local/include/boost/mpl/aux_/arity_spec.hpp : - /opt/local/include/boost/mpl/aux_/arg_typedef.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/arg.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/placeholders.hpp : - /opt/local/include/boost/detail/indirect_traits.hpp : - /opt/local/include/boost/type_traits/remove_pointer.hpp : - /opt/local/include/boost/iterator/detail/enable_if.hpp : - /opt/local/include/boost/mpl/always.hpp : - /opt/local/include/boost/mpl/apply.hpp : - /opt/local/include/boost/mpl/apply_fwd.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply_fwd.hpp : - /opt/local/include/boost/mpl/apply_wrap.hpp : - /opt/local/include/boost/mpl/aux_/has_apply.hpp : - /opt/local/include/boost/mpl/aux_/config/has_apply.hpp : - /opt/local/include/boost/mpl/aux_/msvc_never_true.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp : - /opt/local/include/boost/mpl/lambda.hpp : - /opt/local/include/boost/mpl/bind.hpp : - /opt/local/include/boost/mpl/bind_fwd.hpp : - /opt/local/include/boost/mpl/aux_/config/bind.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/bind_fwd.hpp : - /opt/local/include/boost/mpl/next.hpp : - /opt/local/include/boost/mpl/next_prior.hpp : - /opt/local/include/boost/mpl/aux_/common_name_wknd.hpp : - /opt/local/include/boost/mpl/protect.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/bind.hpp : - /opt/local/include/boost/mpl/aux_/full_lambda.hpp : - /opt/local/include/boost/mpl/quote.hpp : - /opt/local/include/boost/mpl/aux_/has_type.hpp : - /opt/local/include/boost/mpl/aux_/config/bcc.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/quote.hpp : - /opt/local/include/boost/mpl/aux_/template_arity.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/template_arity.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/full_lambda.hpp : - /opt/local/include/boost/mpl/aux_/preprocessed/gcc/apply.hpp : - /opt/local/include/boost/io/detail/quoted_manip.hpp : - /opt/local/include/boost/functional/hash_fwd.hpp : - /opt/local/include/boost/filesystem/operations.hpp : - /opt/local/include/boost/detail/bitmask.hpp : - /opt/local/include/boost/filesystem/convenience.hpp : - /opt/local/include/openssl/sha.h /opt/local/include/openssl/e_os2.h : - /opt/local/include/openssl/opensslconf.h : - /opt/local/include/openssl/ripemd.h : - /path/to/triangles/src/netbase.h : - /path/to/triangles/src/serialize.h : - /opt/local/include/boost/tuple/tuple_comparison.hpp : - /opt/local/include/boost/tuple/tuple_io.hpp : - /path/to/triangles/src/allocators.h : - /path/to/triangles/src/version.h : - /path/to/triangles/src/clientversion.h : - /path/to/triangles/src/compat.h : diff --git a/src/tor/backtrace.c b/src/tor/backtrace.c deleted file mode 100644 index e1452b0..0000000 --- a/src/tor/backtrace.c +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (c) 2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#define __USE_GNU -#define _GNU_SOURCE 1 - -#include "orconfig.h" -#include "backtrace.h" -#include "tor_compat.h" -#include "tor_util.h" -#include "torlog.h" - -#ifdef HAVE_EXECINFO_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SIGNAL_H -#include -#endif - -#ifdef HAVE_CYGWIN_SIGNAL_H -#include -#elif defined(HAVE_SYS_UCONTEXT_H) -#include -#elif defined(HAVE_UCONTEXT_H) -#include -#endif - -#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ - defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) -#define USE_BACKTRACE -#endif - -#if !defined(USE_BACKTRACE) -#define NO_BACKTRACE_IMPL -#endif - -/** Version of Tor to report in backtrace messages. */ -static char *bt_version = NULL; - -#ifdef USE_BACKTRACE -/** Largest stack depth to try to dump. */ -#define MAX_DEPTH 256 -/** Static allocation of stack to dump. This is static so we avoid stack - * pressure. */ -static void *cb_buf[MAX_DEPTH]; - -/** Change a stacktrace in stack of depth depth so that it will - * log the correct function from which a signal was received with context - * ctx. (When we get a signal, the current function will not have - * called any other function, and will therefore have not pushed its address - * onto the stack. Fortunately, we usually have the program counter in the - * ucontext_t structure. - */ -static void -clean_backtrace(void **stack, int depth, const ucontext_t *ctx) -{ -#ifdef PC_FROM_UCONTEXT -#if defined(__linux__) - const int n = 1; -#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \ - || defined(__FreeBSD__) - const int n = 2; -#else - const int n = 1; -#endif - if (depth <= n) - return; - - stack[n] = (void*) ctx->PC_FROM_UCONTEXT; -#else - (void) depth; - (void) ctx; -#endif -} - -/** Log a message msg at severity in domain, and follow - * that with a backtrace log. */ -void -log_backtrace(int severity, int domain, const char *msg) -{ - int depth = backtrace(cb_buf, MAX_DEPTH); - char **symbols = backtrace_symbols(cb_buf, depth); - int i; - tor_log(severity, domain, "%s. Stack trace:", msg); - if (!symbols) { - tor_log(severity, domain, " Unable to generate backtrace."); - return; - } - for (i=0; i < depth; ++i) { - tor_log(severity, domain, " %s", symbols[i]); - } - free(symbols); -} - -static void crash_handler(int sig, siginfo_t *si, void *ctx_) - __attribute__((noreturn)); - -/** Signal handler: write a crash message with a stack trace, and die. */ -static void -crash_handler(int sig, siginfo_t *si, void *ctx_) -{ - char buf[40]; - int depth; - ucontext_t *ctx = (ucontext_t *) ctx_; - int n_fds, i; - const int *fds = NULL; - - (void) si; - - depth = backtrace(cb_buf, MAX_DEPTH); - /* Clean up the top stack frame so we get the real function - * name for the most recently failing function. */ - clean_backtrace(cb_buf, depth, ctx); - - format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf)); - - tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n", - NULL); - - n_fds = tor_log_get_sigsafe_err_fds(&fds); - for (i=0; i < n_fds; ++i) - backtrace_symbols_fd(cb_buf, depth, fds[i]); - - abort(); -} - -/** Install signal handlers as needed so that when we crash, we produce a - * useful stack trace. Return 0 on success, -1 on failure. */ -static int -install_bt_handler(void) -{ - int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, - SIGIO, -1 }; - int i, rv=0; - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = crash_handler; - sa.sa_flags = SA_SIGINFO; - sigfillset(&sa.sa_mask); - - for (i = 0; trap_signals[i] >= 0; ++i) { - if (sigaction(trap_signals[i], &sa, NULL) == -1) { - log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno)); - rv = -1; - } - } - return rv; -} - -/** Uninstall crash handlers. */ -static void -remove_bt_handler(void) -{ - /* We don't need to actually free anything at exit here. */ -} -#endif - -#ifdef NO_BACKTRACE_IMPL -void -log_backtrace(int severity, int domain, const char *msg) -{ - tor_log(severity, domain, "%s. (Stack trace not available)", msg); -} - -static int -install_bt_handler(void) -{ - return 0; -} - -static void -remove_bt_handler(void) -{ -} -#endif - -/** Set up code to handle generating error messages on crashes. */ -int -configure_backtrace_handler(const char *tor_version) -{ - tor_free(bt_version); - if (!tor_version) - tor_version = ""; - tor_asprintf(&bt_version, "Tor %s", tor_version); - - return install_bt_handler(); -} - -/** Perform end-of-process cleanup for code that generates error messages on - * crashes. */ -void -clean_up_backtrace_handler(void) -{ - remove_bt_handler(); - - tor_free(bt_version); -} - diff --git a/src/tor/backtrace.h b/src/tor/backtrace.h deleted file mode 100644 index 765436f..0000000 --- a/src/tor/backtrace.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright (c) 2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_BACKTRACE_H -#define TOR_BACKTRACE_H - -void log_backtrace(int severity, int domain, const char *msg); -int configure_backtrace_handler(const char *tor_version); -void clean_up_backtrace_handler(void); - -#endif - diff --git a/src/tor/buffers.c b/src/tor/buffers.c deleted file mode 100644 index 40083e7..0000000 --- a/src/tor/buffers.c +++ /dev/null @@ -1,2584 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file buffers.c - * \brief Implements a generic interface buffer. Buffers are - * fairly opaque string holders that can read to or flush from: - * memory, file descriptors, or TLS connections. - **/ -#define BUFFERS_PRIVATE -#include "or.h" -#include "addressmap.h" -#include "buffers.h" -#include "config.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "reasons.h" -#include "ext_orport.h" -#include "tor_util.h" -#include "torlog.h" -#ifdef HAVE_UNISTD_H -#include -#endif - -//#define PARANOIA - -#ifdef PARANOIA -/** Helper: If PARANOIA is defined, assert that the buffer in local variable - * buf is well-formed. */ -#define check() STMT_BEGIN assert_buf_ok(buf); STMT_END -#else -#define check() STMT_NIL -#endif - -/* Implementation notes: - * - * After flirting with memmove, and dallying with ring-buffers, we're finally - * getting up to speed with the 1970s and implementing buffers as a linked - * list of small chunks. Each buffer has such a list; data is removed from - * the head of the list, and added at the tail. The list is singly linked, - * and the buffer keeps a pointer to the head and the tail. - * - * Every chunk, except the tail, contains at least one byte of data. Data in - * each chunk is contiguous. - * - * When you need to treat the first N characters on a buffer as a contiguous - * string, use the buf_pullup function to make them so. Don't do this more - * than necessary. - * - * The major free Unix kernels have handled buffers like this since, like, - * forever. - */ - -static int parse_socks(const char *data, size_t datalen, socks_request_t *req, - int log_sockstype, int safe_socks, ssize_t *drain_out, - size_t *want_length_out); -static int parse_socks_client(const uint8_t *data, size_t datalen, - int state, char **reason, - ssize_t *drain_out); - -/* Chunk manipulation functions */ - -/** A single chunk on a buffer or in a freelist. */ -typedef struct chunk_t { - struct chunk_t *next; /**< The next chunk on the buffer or freelist. */ - size_t datalen; /**< The number of bytes stored in this chunk */ - size_t memlen; /**< The number of usable bytes of storage in mem. */ - char *data; /**< A pointer to the first byte of data stored in mem. */ - char mem[FLEXIBLE_ARRAY_MEMBER]; /**< The actual memory used for storage in - * this chunk. */ -} chunk_t; - -#define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0]) - -/** Return the number of bytes needed to allocate a chunk to hold - * memlen bytes. */ -#define CHUNK_ALLOC_SIZE(memlen) (CHUNK_HEADER_LEN + (memlen)) -/** Return the number of usable bytes in a chunk allocated with - * malloc(memlen). */ -#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - CHUNK_HEADER_LEN) - -/** Return the next character in chunk onto which data can be appended. - * If the chunk is full, this might be off the end of chunk->mem. */ -static INLINE char * -CHUNK_WRITE_PTR(chunk_t *chunk) -{ - return chunk->data + chunk->datalen; -} - -/** Return the number of bytes that can be written onto chunk without - * running out of space. */ -static INLINE size_t -CHUNK_REMAINING_CAPACITY(const chunk_t *chunk) -{ - return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen); -} - -/** Move all bytes stored in chunk to the front of chunk->mem, - * to free up space at the end. */ -static INLINE void -chunk_repack(chunk_t *chunk) -{ - if (chunk->datalen && chunk->data != &chunk->mem[0]) { - memmove(chunk->mem, chunk->data, chunk->datalen); - } - chunk->data = &chunk->mem[0]; -} - -#if defined(ENABLE_BUF_FREELISTS) || defined(RUNNING_DOXYGEN) -/** A freelist of chunks. */ -typedef struct chunk_freelist_t { - size_t alloc_size; /**< What size chunks does this freelist hold? */ - int max_length; /**< Never allow more than this number of chunks in the - * freelist. */ - int slack; /**< When trimming the freelist, leave this number of extra - * chunks beyond lowest_length.*/ - int cur_length; /**< How many chunks on the freelist now? */ - int lowest_length; /**< What's the smallest value of cur_length since the - * last time we cleaned this freelist? */ - uint64_t n_alloc; - uint64_t n_free; - uint64_t n_hit; - chunk_t *head; /**< First chunk on the freelist. */ -} chunk_freelist_t; - -/** Macro to help define freelists. */ -#define FL(a,m,s) { a, m, s, 0, 0, 0, 0, 0, NULL } - -/** Static array of freelists, sorted by alloc_len, terminated by an entry - * with alloc_size of 0. */ -static chunk_freelist_t freelists[] = { - FL(4096, 256, 8), FL(8192, 128, 4), FL(16384, 64, 4), FL(32768, 32, 2), - FL(0, 0, 0) -}; -#undef FL -/** How many times have we looked for a chunk of a size that no freelist - * could help with? */ -static uint64_t n_freelist_miss = 0; - -static void assert_freelist_ok(chunk_freelist_t *fl); - -/** Return the freelist to hold chunks of size alloc, or NULL if - * no freelist exists for that size. */ -static INLINE chunk_freelist_t * -get_freelist(size_t alloc) -{ - int i; - for (i=0; (freelists[i].alloc_size <= alloc && - freelists[i].alloc_size); ++i ) { - if (freelists[i].alloc_size == alloc) { - return &freelists[i]; - } - } - return NULL; -} - -/** Deallocate a chunk or put it on a freelist */ -static void -chunk_free_unchecked(chunk_t *chunk) -{ - size_t alloc; - chunk_freelist_t *freelist; - - alloc = CHUNK_ALLOC_SIZE(chunk->memlen); - freelist = get_freelist(alloc); - if (freelist && freelist->cur_length < freelist->max_length) { - chunk->next = freelist->head; - freelist->head = chunk; - ++freelist->cur_length; - } else { - if (freelist) - ++freelist->n_free; - tor_free(chunk); - } -} - -/** Allocate a new chunk with a given allocation size, or get one from the - * freelist. Note that a chunk with allocation size A can actually hold only - * CHUNK_SIZE_WITH_ALLOC(A) bytes in its mem field. */ -static INLINE chunk_t * -chunk_new_with_alloc_size(size_t alloc) -{ - chunk_t *ch; - chunk_freelist_t *freelist; - tor_assert(alloc >= sizeof(chunk_t)); - freelist = get_freelist(alloc); - if (freelist && freelist->head) { - ch = freelist->head; - freelist->head = ch->next; - if (--freelist->cur_length < freelist->lowest_length) - freelist->lowest_length = freelist->cur_length; - ++freelist->n_hit; - } else { - if (freelist) - ++freelist->n_alloc; - else - ++n_freelist_miss; - ch = tor_malloc(alloc); - } - ch->next = NULL; - ch->datalen = 0; - ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc); - ch->data = &ch->mem[0]; - return ch; -} -#else -static void -chunk_free_unchecked(chunk_t *chunk) -{ - tor_free(chunk); -} -static INLINE chunk_t * -chunk_new_with_alloc_size(size_t alloc) -{ - chunk_t *ch; - ch = tor_malloc(alloc); - ch->next = NULL; - ch->datalen = 0; - ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc); - ch->data = &ch->mem[0]; - return ch; -} -#endif - -/** Expand chunk until it can hold sz bytes, and return a - * new pointer to chunk. Old pointers are no longer valid. */ -static INLINE chunk_t * -chunk_grow(chunk_t *chunk, size_t sz) -{ - off_t offset; - tor_assert(sz > chunk->memlen); - offset = chunk->data - chunk->mem; - chunk = tor_realloc(chunk, CHUNK_ALLOC_SIZE(sz)); - chunk->memlen = sz; - chunk->data = chunk->mem + offset; - return chunk; -} - -/** If a read onto the end of a chunk would be smaller than this number, then - * just start a new chunk. */ -#define MIN_READ_LEN 8 -/** Every chunk should take up at least this many bytes. */ -#define MIN_CHUNK_ALLOC 256 -/** No chunk should take up more than this many bytes. */ -#define MAX_CHUNK_ALLOC 65536 - -/** Return the allocation size we'd like to use to hold target - * bytes. */ -static INLINE size_t -preferred_chunk_size(size_t target) -{ - size_t sz = MIN_CHUNK_ALLOC; - while (CHUNK_SIZE_WITH_ALLOC(sz) < target) { - sz <<= 1; - } - return sz; -} - -/** Remove from the freelists most chunks that have not been used since the - * last call to buf_shrink_freelists(). */ -void -buf_shrink_freelists(int free_all) -{ -#ifdef ENABLE_BUF_FREELISTS - int i; - disable_control_logging(); - for (i = 0; freelists[i].alloc_size; ++i) { - int slack = freelists[i].slack; - assert_freelist_ok(&freelists[i]); - if (free_all || freelists[i].lowest_length > slack) { - int n_to_free = free_all ? freelists[i].cur_length : - (freelists[i].lowest_length - slack); - int n_to_skip = freelists[i].cur_length - n_to_free; - int orig_length = freelists[i].cur_length; - int orig_n_to_free = n_to_free, n_freed=0; - int orig_n_to_skip = n_to_skip; - int new_length = n_to_skip; - chunk_t **chp = &freelists[i].head; - chunk_t *chunk; - while (n_to_skip) { - if (! (*chp)->next) { - log_warn(LD_BUG, "I wanted to skip %d chunks in the freelist for " - "%d-byte chunks, but only found %d. (Length %d)", - orig_n_to_skip, (int)freelists[i].alloc_size, - orig_n_to_skip-n_to_skip, freelists[i].cur_length); - assert_freelist_ok(&freelists[i]); - goto done; - } - // tor_assert((*chp)->next); - chp = &(*chp)->next; - --n_to_skip; - } - chunk = *chp; - *chp = NULL; - while (chunk) { - chunk_t *next = chunk->next; - tor_free(chunk); - chunk = next; - --n_to_free; - ++n_freed; - ++freelists[i].n_free; - } - if (n_to_free) { - log_warn(LD_BUG, "Freelist length for %d-byte chunks may have been " - "messed up somehow.", (int)freelists[i].alloc_size); - log_warn(LD_BUG, "There were %d chunks at the start. I decided to " - "keep %d. I wanted to free %d. I freed %d. I somehow think " - "I have %d left to free.", - freelists[i].cur_length, n_to_skip, orig_n_to_free, - n_freed, n_to_free); - } - // tor_assert(!n_to_free); - freelists[i].cur_length = new_length; - log_info(LD_MM, "Cleaned freelist for %d-byte chunks: original " - "length %d, kept %d, dropped %d.", - (int)freelists[i].alloc_size, orig_length, - orig_n_to_skip, orig_n_to_free); - } - freelists[i].lowest_length = freelists[i].cur_length; - assert_freelist_ok(&freelists[i]); - } - done: - enable_control_logging(); -#else - (void) free_all; -#endif -} - -/** Describe the current status of the freelists at log level severity. - */ -void -buf_dump_freelist_sizes(int severity) -{ -#ifdef ENABLE_BUF_FREELISTS - int i; - tor_log(severity, LD_MM, "====== Buffer freelists:"); - for (i = 0; freelists[i].alloc_size; ++i) { - uint64_t total = ((uint64_t)freelists[i].cur_length) * - freelists[i].alloc_size; - tor_log(severity, LD_MM, - U64_FORMAT" bytes in %d %d-byte chunks ["U64_FORMAT - " misses; "U64_FORMAT" frees; "U64_FORMAT" hits]", - U64_PRINTF_ARG(total), - freelists[i].cur_length, (int)freelists[i].alloc_size, - U64_PRINTF_ARG(freelists[i].n_alloc), - U64_PRINTF_ARG(freelists[i].n_free), - U64_PRINTF_ARG(freelists[i].n_hit)); - } - tor_log(severity, LD_MM, U64_FORMAT" allocations in non-freelist sizes", - U64_PRINTF_ARG(n_freelist_miss)); -#else - (void)severity; -#endif -} - -/** Magic value for buf_t.magic, to catch pointer errors. */ -#define BUFFER_MAGIC 0xB0FFF312u -/** A resizeable buffer, optimized for reading and writing. */ -struct buf_t { - uint32_t magic; /**< Magic cookie for debugging: Must be set to - * BUFFER_MAGIC. */ - size_t datalen; /**< How many bytes is this buffer holding right now? */ - size_t default_chunk_size; /**< Don't allocate any chunks smaller than - * this for this buffer. */ - chunk_t *head; /**< First chunk in the list, or NULL for none. */ - chunk_t *tail; /**< Last chunk in the list, or NULL for none. */ -}; - -/** Collapse data from the first N chunks from buf into buf->head, - * growing it as necessary, until buf->head has the first bytes bytes - * of data from the buffer, or until buf->head has all the data in buf. - * - * If nulterminate is true, ensure that there is a 0 byte in - * buf->head->mem right after all the data. */ -static void -buf_pullup(buf_t *buf, size_t bytes, int nulterminate) -{ - chunk_t *dest, *src; - size_t capacity; - if (!buf->head) - return; - - check(); - if (buf->datalen < bytes) - bytes = buf->datalen; - - if (nulterminate) { - capacity = bytes + 1; - if (buf->head->datalen >= bytes && CHUNK_REMAINING_CAPACITY(buf->head)) { - *CHUNK_WRITE_PTR(buf->head) = '\0'; - return; - } - } else { - capacity = bytes; - if (buf->head->datalen >= bytes) - return; - } - - if (buf->head->memlen >= capacity) { - /* We don't need to grow the first chunk, but we might need to repack it.*/ - size_t needed = capacity - buf->head->datalen; - if (CHUNK_REMAINING_CAPACITY(buf->head) < needed) - chunk_repack(buf->head); - tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= needed); - } else { - chunk_t *newhead; - size_t newsize; - /* We need to grow the chunk. */ - chunk_repack(buf->head); - newsize = CHUNK_SIZE_WITH_ALLOC(preferred_chunk_size(capacity)); - newhead = chunk_grow(buf->head, newsize); - tor_assert(newhead->memlen >= capacity); - if (newhead != buf->head) { - if (buf->tail == buf->head) - buf->tail = newhead; - buf->head = newhead; - } - } - - dest = buf->head; - while (dest->datalen < bytes) { - size_t n = bytes - dest->datalen; - src = dest->next; - tor_assert(src); - if (n > src->datalen) { - memcpy(CHUNK_WRITE_PTR(dest), src->data, src->datalen); - dest->datalen += src->datalen; - dest->next = src->next; - if (buf->tail == src) - buf->tail = dest; - chunk_free_unchecked(src); - } else { - memcpy(CHUNK_WRITE_PTR(dest), src->data, n); - dest->datalen += n; - src->data += n; - src->datalen -= n; - tor_assert(dest->datalen == bytes); - } - } - - if (nulterminate) { - tor_assert(CHUNK_REMAINING_CAPACITY(buf->head)); - *CHUNK_WRITE_PTR(buf->head) = '\0'; - } - - check(); -} - -/** Resize buf so it won't hold extra memory that we haven't been - * using lately. - */ -void -buf_shrink(buf_t *buf) -{ - (void)buf; -} - -/** Remove the first n bytes from buf. */ -static INLINE void -buf_remove_from_front(buf_t *buf, size_t n) -{ - tor_assert(buf->datalen >= n); - while (n) { - tor_assert(buf->head); - if (buf->head->datalen > n) { - buf->head->datalen -= n; - buf->head->data += n; - buf->datalen -= n; - return; - } else { - chunk_t *victim = buf->head; - n -= victim->datalen; - buf->datalen -= victim->datalen; - buf->head = victim->next; - if (buf->tail == victim) - buf->tail = NULL; - chunk_free_unchecked(victim); - } - } - check(); -} - -/** Create and return a new buf with default chunk capacity size. - */ -buf_t * -buf_new_with_capacity(size_t size) -{ - buf_t *b = buf_new(); - b->default_chunk_size = preferred_chunk_size(size); - return b; -} - -/** Allocate and return a new buffer with default capacity. */ -buf_t * -buf_new(void) -{ - buf_t *buf = tor_malloc_zero(sizeof(buf_t)); - buf->magic = BUFFER_MAGIC; - buf->default_chunk_size = 4096; - return buf; -} - -/** Remove all data from buf. */ -void -buf_clear(buf_t *buf) -{ - chunk_t *chunk, *next; - buf->datalen = 0; - for (chunk = buf->head; chunk; chunk = next) { - next = chunk->next; - chunk_free_unchecked(chunk); - } - buf->head = buf->tail = NULL; -} - -/** Return the number of bytes stored in buf */ -size_t -buf_datalen(const buf_t *buf) -{ - return buf->datalen; -} - -/** Return the total length of all chunks used in buf. */ -size_t -buf_allocation(const buf_t *buf) -{ - size_t total = 0; - const chunk_t *chunk; - for (chunk = buf->head; chunk; chunk = chunk->next) { - total += chunk->memlen; - } - return total; -} - -/** Return the number of bytes that can be added to buf without - * performing any additional allocation. */ -size_t -buf_slack(const buf_t *buf) -{ - if (!buf->tail) - return 0; - else - return CHUNK_REMAINING_CAPACITY(buf->tail); -} - -/** Release storage held by buf. */ -void -buf_free(buf_t *buf) -{ - if (!buf) - return; - - buf_clear(buf); - buf->magic = 0xdeadbeef; - tor_free(buf); -} - -/** Return a new copy of in_chunk */ -static chunk_t * -chunk_copy(const chunk_t *in_chunk) -{ - chunk_t *newch = tor_memdup(in_chunk, CHUNK_ALLOC_SIZE(in_chunk->memlen)); - newch->next = NULL; - if (in_chunk->data) { - off_t offset = in_chunk->data - in_chunk->mem; - newch->data = newch->mem + offset; - } - return newch; -} - -/** Return a new copy of buf */ -buf_t * -buf_copy(const buf_t *buf) -{ - chunk_t *ch; - buf_t *out = buf_new(); - out->default_chunk_size = buf->default_chunk_size; - for (ch = buf->head; ch; ch = ch->next) { - chunk_t *newch = chunk_copy(ch); - if (out->tail) { - out->tail->next = newch; - out->tail = newch; - } else { - out->head = out->tail = newch; - } - } - out->datalen = buf->datalen; - return out; -} - -/** Append a new chunk with enough capacity to hold capacity bytes to - * the tail of buf. If capped, don't allocate a chunk bigger - * than MAX_CHUNK_ALLOC. */ -static chunk_t * -buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) -{ - chunk_t *chunk; - if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) { - chunk = chunk_new_with_alloc_size(buf->default_chunk_size); - } else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) { - chunk = chunk_new_with_alloc_size(MAX_CHUNK_ALLOC); - } else { - chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity)); - } - if (buf->tail) { - tor_assert(buf->head); - buf->tail->next = chunk; - buf->tail = chunk; - } else { - tor_assert(!buf->head); - buf->head = buf->tail = chunk; - } - check(); - return chunk; -} - -/** Read up to at_most bytes from the socket fd into - * chunk (which must be on buf). If we get an EOF, set - * *reached_eof to 1. Return -1 on error, 0 on eof or blocking, - * and the number of bytes read otherwise. */ -static INLINE int -read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most, - int *reached_eof, int *socket_error) -{ - ssize_t read_result; - if (at_most > CHUNK_REMAINING_CAPACITY(chunk)) - at_most = CHUNK_REMAINING_CAPACITY(chunk); - read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0); - - if (read_result < 0) { - int e = tor_socket_errno(fd); - if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */ -#ifdef _WIN32 - if (e == WSAENOBUFS) - log_warn(LD_NET,"recv() failed: WSAENOBUFS. Not enough ram?"); -#endif - *socket_error = e; - return -1; - } - return 0; /* would block. */ - } else if (read_result == 0) { - log_debug(LD_NET,"Encountered eof on fd %d", (int)fd); - *reached_eof = 1; - return 0; - } else { /* actually got bytes. */ - buf->datalen += read_result; - chunk->datalen += read_result; - log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result, - (int)buf->datalen); - tor_assert(read_result < INT_MAX); - return (int)read_result; - } -} - -/** As read_to_chunk(), but return (negative) error code on error, blocking, - * or TLS, and the number of bytes read otherwise. */ -static INLINE int -read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls, - size_t at_most) -{ - int read_result; - - tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most); - read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most); - if (read_result < 0) - return read_result; - buf->datalen += read_result; - chunk->datalen += read_result; - return read_result; -} - -/** Read from socket s, writing onto end of buf. Read at most - * at_most bytes, growing the buffer as necessary. If recv() returns 0 - * (because of EOF), set *reached_eof to 1 and return 0. Return -1 on - * error; else return the number of bytes read. - */ -/* XXXX024 indicate "read blocked" somehow? */ -int -read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof, - int *socket_error) -{ - /* XXXX024 It's stupid to overload the return values for these functions: - * "error status" and "number of bytes read" are not mutually exclusive. - */ - int r = 0; - size_t total_read = 0; - - check(); - tor_assert(reached_eof); - tor_assert(SOCKET_OK(s)); - - while (at_most > total_read) { - size_t readlen = at_most - total_read; - chunk_t *chunk; - if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) { - chunk = buf_add_chunk_with_capacity(buf, at_most, 1); - if (readlen > chunk->memlen) - readlen = chunk->memlen; - } else { - size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail); - chunk = buf->tail; - if (cap < readlen) - readlen = cap; - } - - r = read_to_chunk(buf, chunk, s, readlen, reached_eof, socket_error); - check(); - if (r < 0) - return r; /* Error */ - tor_assert(total_read+r < INT_MAX); - total_read += r; - if ((size_t)r < readlen) { /* eof, block, or no more to read. */ - break; - } - } - return (int)total_read; -} - -/** As read_to_buf, but reads from a TLS connection, and returns a TLS - * status value rather than the number of bytes read. - * - * Using TLS on OR connections complicates matters in two ways. - * - * First, a TLS stream has its own read buffer independent of the - * connection's read buffer. (TLS needs to read an entire frame from - * the network before it can decrypt any data. Thus, trying to read 1 - * byte from TLS can require that several KB be read from the network - * and decrypted. The extra data is stored in TLS's decrypt buffer.) - * Because the data hasn't been read by Tor (it's still inside the TLS), - * this means that sometimes a connection "has stuff to read" even when - * poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is - * used in connection.c to detect TLS objects with non-empty internal - * buffers and read from them again. - * - * Second, the TLS stream's events do not correspond directly to network - * events: sometimes, before a TLS stream can read, the network must be - * ready to write -- or vice versa. - */ -int -read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) -{ - int r = 0; - size_t total_read = 0; - - check_no_tls_errors(); - - check(); - - while (at_most > total_read) { - size_t readlen = at_most - total_read; - chunk_t *chunk; - if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) { - chunk = buf_add_chunk_with_capacity(buf, at_most, 1); - if (readlen > chunk->memlen) - readlen = chunk->memlen; - } else { - size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail); - chunk = buf->tail; - if (cap < readlen) - readlen = cap; - } - - r = read_to_chunk_tls(buf, chunk, tls, readlen); - check(); - if (r < 0) - return r; /* Error */ - tor_assert(total_read+r < INT_MAX); - total_read += r; - if ((size_t)r < readlen) /* eof, block, or no more to read. */ - break; - } - return (int)total_read; -} - -/** Helper for flush_buf(): try to write sz bytes from chunk - * chunk of buffer buf onto socket s. On success, deduct - * the bytes written from *buf_flushlen. Return the number of bytes - * written on success, 0 on blocking, -1 on failure. - */ -static INLINE int -flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz, - size_t *buf_flushlen) -{ - ssize_t write_result; - - if (sz > chunk->datalen) - sz = chunk->datalen; - write_result = tor_socket_send(s, chunk->data, sz, 0); - - if (write_result < 0) { - int e = tor_socket_errno(s); - if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */ -#ifdef _WIN32 - if (e == WSAENOBUFS) - log_warn(LD_NET,"write() failed: WSAENOBUFS. Not enough ram?"); -#endif - return -1; - } - log_debug(LD_NET,"write() would block, returning."); - return 0; - } else { - *buf_flushlen -= write_result; - buf_remove_from_front(buf, write_result); - tor_assert(write_result < INT_MAX); - return (int)write_result; - } -} - -/** Helper for flush_buf_tls(): try to write sz bytes from chunk - * chunk of buffer buf onto socket s. (Tries to write - * more if there is a forced pending write size.) On success, deduct the - * bytes written from *buf_flushlen. Return the number of bytes - * written on success, and a TOR_TLS error code on failure or blocking. - */ -static INLINE int -flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, - size_t sz, size_t *buf_flushlen) -{ - int r; - size_t forced; - char *data; - - forced = tor_tls_get_forced_write_size(tls); - if (forced > sz) - sz = forced; - if (chunk) { - data = chunk->data; - tor_assert(sz <= chunk->datalen); - } else { - data = NULL; - tor_assert(sz == 0); - } - r = tor_tls_write(tls, data, sz); - if (r < 0) - return r; - if (*buf_flushlen > (size_t)r) - *buf_flushlen -= r; - else - *buf_flushlen = 0; - buf_remove_from_front(buf, r); - log_debug(LD_NET,"flushed %d bytes, %d ready to flush, %d remain.", - r,(int)*buf_flushlen,(int)buf->datalen); - return r; -} - -/** Write data from buf to the socket s. Write at most - * sz bytes, decrement *buf_flushlen by - * the number of bytes actually written, and remove the written bytes - * from the buffer. Return the number of bytes written on success, - * -1 on failure. Return 0 if write() would block. - */ -int -flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen) -{ - /* XXXX024 It's stupid to overload the return values for these functions: - * "error status" and "number of bytes flushed" are not mutually exclusive. - */ - int r; - size_t flushed = 0; - tor_assert(buf_flushlen); - tor_assert(SOCKET_OK(s)); - tor_assert(*buf_flushlen <= buf->datalen); - tor_assert(sz <= *buf_flushlen); - - check(); - while (sz) { - size_t flushlen0; - tor_assert(buf->head); - if (buf->head->datalen >= sz) - flushlen0 = sz; - else - flushlen0 = buf->head->datalen; - - r = flush_chunk(s, buf, buf->head, flushlen0, buf_flushlen); - check(); - if (r < 0) - return r; - flushed += r; - sz -= r; - if (r == 0 || (size_t)r < flushlen0) /* can't flush any more now. */ - break; - } - tor_assert(flushed < INT_MAX); - return (int)flushed; -} - -/** As flush_buf(), but writes data to a TLS connection. Can write more than - * flushlen bytes. - */ -int -flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t flushlen, - size_t *buf_flushlen) -{ - int r; - size_t flushed = 0; - ssize_t sz; - tor_assert(buf_flushlen); - tor_assert(*buf_flushlen <= buf->datalen); - tor_assert(flushlen <= *buf_flushlen); - sz = (ssize_t) flushlen; - - /* we want to let tls write even if flushlen is zero, because it might - * have a partial record pending */ - check_no_tls_errors(); - - check(); - do { - size_t flushlen0; - if (buf->head) { - if ((ssize_t)buf->head->datalen >= sz) - flushlen0 = sz; - else - flushlen0 = buf->head->datalen; - } else { - flushlen0 = 0; - } - - r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen); - check(); - if (r < 0) - return r; - flushed += r; - sz -= r; - if (r == 0) /* Can't flush any more now. */ - break; - } while (sz > 0); - tor_assert(flushed < INT_MAX); - return (int)flushed; -} - -/** Append string_len bytes from string to the end of - * buf. - * - * Return the new length of the buffer on success, -1 on failure. - */ -int -write_to_buf(const char *string, size_t string_len, buf_t *buf) -{ - if (!string_len) - return (int)buf->datalen; - check(); - - while (string_len) { - size_t copy; - if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail)) - buf_add_chunk_with_capacity(buf, string_len, 1); - - copy = CHUNK_REMAINING_CAPACITY(buf->tail); - if (copy > string_len) - copy = string_len; - memcpy(CHUNK_WRITE_PTR(buf->tail), string, copy); - string_len -= copy; - string += copy; - buf->datalen += copy; - buf->tail->datalen += copy; - } - - check(); - tor_assert(buf->datalen < INT_MAX); - return (int)buf->datalen; -} - -/** Helper: copy the first string_len bytes from buf - * onto string. - */ -static INLINE void -peek_from_buf(char *string, size_t string_len, const buf_t *buf) -{ - chunk_t *chunk; - - tor_assert(string); - /* make sure we don't ask for too much */ - tor_assert(string_len <= buf->datalen); - /* assert_buf_ok(buf); */ - - chunk = buf->head; - while (string_len) { - size_t copy = string_len; - tor_assert(chunk); - if (chunk->datalen < copy) - copy = chunk->datalen; - memcpy(string, chunk->data, copy); - string_len -= copy; - string += copy; - chunk = chunk->next; - } -} - -/** Remove string_len bytes from the front of buf, and store - * them into string. Return the new buffer size. string_len - * must be \<= the number of bytes on the buffer. - */ -int -fetch_from_buf(char *string, size_t string_len, buf_t *buf) -{ - /* There must be string_len bytes in buf; write them onto string, - * then memmove buf back (that is, remove them from buf). - * - * Return the number of bytes still on the buffer. */ - - check(); - peek_from_buf(string, string_len, buf); - buf_remove_from_front(buf, string_len); - check(); - tor_assert(buf->datalen < INT_MAX); - return (int)buf->datalen; -} - -/** True iff the cell command command is one that implies a - * variable-length cell in Tor link protocol linkproto. */ -static INLINE int -cell_command_is_var_length(uint8_t command, int linkproto) -{ - /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells - * work as implemented here. If it's 1, there are no variable-length cells. - * Tor does not support other versions right now, and so can't negotiate - * them. - */ - switch (linkproto) { - case 1: - /* Link protocol version 1 has no variable-length cells. */ - return 0; - case 2: - /* In link protocol version 2, VERSIONS is the only variable-length cell */ - return command == CELL_VERSIONS; - case 0: - case 3: - default: - /* In link protocol version 3 and later, and in version "unknown", - * commands 128 and higher indicate variable-length. VERSIONS is - * grandfathered in. */ - return command == CELL_VERSIONS || command >= 128; - } -} - -/** Check buf for a variable-length cell according to the rules of link - * protocol version linkproto. If one is found, pull it off the buffer - * and assign a newly allocated var_cell_t to *out, and return 1. - * Return 0 if whatever is on the start of buf_t is not a variable-length - * cell. Return 1 and set *out to NULL if there seems to be the start - * of a variable-length cell on buf, but the whole thing isn't there - * yet. */ -int -fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto) -{ - char hdr[VAR_CELL_MAX_HEADER_SIZE]; - var_cell_t *result; - uint8_t command; - uint16_t length; - const int wide_circ_ids = linkproto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS; - const int circ_id_len = get_circ_id_size(wide_circ_ids); - const unsigned header_len = get_var_cell_header_size(wide_circ_ids); - check(); - *out = NULL; - if (buf->datalen < header_len) - return 0; - peek_from_buf(hdr, header_len, buf); - - command = get_uint8(hdr + circ_id_len); - if (!(cell_command_is_var_length(command, linkproto))) - return 0; - - length = ntohs(get_uint16(hdr + circ_id_len + 1)); - if (buf->datalen < (size_t)(header_len+length)) - return 1; - result = var_cell_new(length); - result->command = command; - if (wide_circ_ids) - result->circ_id = ntohl(get_uint32(hdr)); - else - result->circ_id = ntohs(get_uint16(hdr)); - - buf_remove_from_front(buf, header_len); - peek_from_buf((char*) result->payload, length, buf); - buf_remove_from_front(buf, length); - check(); - - *out = result; - return 1; -} - -#ifdef USE_BUFFEREVENTS -/** Try to read n bytes from buf at pos (which may be - * NULL for the start of the buffer), copying the data only if necessary. Set - * *data_out to a pointer to the desired bytes. Set free_out - * to 1 if we needed to malloc *data because the original bytes were - * noncontiguous; 0 otherwise. Return the number of bytes actually available - * at *data_out. - */ -static ssize_t -inspect_evbuffer(struct evbuffer *buf, char **data_out, size_t n, - int *free_out, struct evbuffer_ptr *pos) -{ - int n_vecs, i; - - if (evbuffer_get_length(buf) < n) - n = evbuffer_get_length(buf); - if (n == 0) - return 0; - n_vecs = evbuffer_peek(buf, n, pos, NULL, 0); - tor_assert(n_vecs > 0); - if (n_vecs == 1) { - struct evbuffer_iovec v; - i = evbuffer_peek(buf, n, pos, &v, 1); - tor_assert(i == 1); - *data_out = v.iov_base; - *free_out = 0; - return v.iov_len; - } else { - ev_ssize_t copied; - *data_out = tor_malloc(n); - *free_out = 1; - copied = evbuffer_copyout(buf, *data_out, n); - tor_assert(copied >= 0 && (size_t)copied == n); - return copied; - } -} - -/** As fetch_var_cell_from_buf, buf works on an evbuffer. */ -int -fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out, - int linkproto) -{ - char *hdr = NULL; - int free_hdr = 0; - size_t n; - size_t buf_len; - uint8_t command; - uint16_t cell_length; - var_cell_t *cell; - int result = 0; - const int wide_circ_ids = linkproto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS; - const int circ_id_len = get_circ_id_size(wide_circ_ids); - const unsigned header_len = get_var_cell_header_size(wide_circ_ids); - - *out = NULL; - buf_len = evbuffer_get_length(buf); - if (buf_len < header_len) - return 0; - - n = inspect_evbuffer(buf, &hdr, header_len, &free_hdr, NULL); - tor_assert(n >= header_len); - - command = get_uint8(hdr + circ_id_len); - if (!(cell_command_is_var_length(command, linkproto))) { - goto done; - } - - cell_length = ntohs(get_uint16(hdr + circ_id_len + 1)); - if (buf_len < (size_t)(header_len+cell_length)) { - result = 1; /* Not all here yet. */ - goto done; - } - - cell = var_cell_new(cell_length); - cell->command = command; - if (wide_circ_ids) - cell->circ_id = ntohl(get_uint32(hdr)); - else - cell->circ_id = ntohs(get_uint16(hdr)); - evbuffer_drain(buf, header_len); - evbuffer_remove(buf, cell->payload, cell_length); - *out = cell; - result = 1; - - done: - if (free_hdr && hdr) - tor_free(hdr); - return result; -} -#endif - -/** Move up to *buf_flushlen bytes from buf_in to - * buf_out, and modify *buf_flushlen appropriately. - * Return the number of bytes actually copied. - */ -int -move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen) -{ - /* We can do way better here, but this doesn't turn up in any profiles. */ - char b[4096]; - size_t cp, len; - len = *buf_flushlen; - if (len > buf_in->datalen) - len = buf_in->datalen; - - cp = len; /* Remember the number of bytes we intend to copy. */ - tor_assert(cp < INT_MAX); - while (len) { - /* This isn't the most efficient implementation one could imagine, since - * it does two copies instead of 1, but I kinda doubt that this will be - * critical path. */ - size_t n = len > sizeof(b) ? sizeof(b) : len; - fetch_from_buf(b, n, buf_in); - write_to_buf(b, n, buf_out); - len -= n; - } - *buf_flushlen -= cp; - return (int)cp; -} - -/** Internal structure: represents a position in a buffer. */ -typedef struct buf_pos_t { - const chunk_t *chunk; /**< Which chunk are we pointing to? */ - int pos;/**< Which character inside the chunk's data are we pointing to? */ - size_t chunk_pos; /**< Total length of all previous chunks. */ -} buf_pos_t; - -/** Initialize out to point to the first character of buf.*/ -static void -buf_pos_init(const buf_t *buf, buf_pos_t *out) -{ - out->chunk = buf->head; - out->pos = 0; - out->chunk_pos = 0; -} - -/** Advance out to the first appearance of ch at the current - * position of out, or later. Return -1 if no instances are found; - * otherwise returns the absolute position of the character. */ -static off_t -buf_find_pos_of_char(char ch, buf_pos_t *out) -{ - const chunk_t *chunk; - int pos; - tor_assert(out); - if (out->chunk) { - if (out->chunk->datalen) { - tor_assert(out->pos < (off_t)out->chunk->datalen); - } else { - tor_assert(out->pos == 0); - } - } - pos = out->pos; - for (chunk = out->chunk; chunk; chunk = chunk->next) { - char *cp = memchr(chunk->data+pos, ch, chunk->datalen - pos); - if (cp) { - out->chunk = chunk; - tor_assert(cp - chunk->data < INT_MAX); - out->pos = (int)(cp - chunk->data); - return out->chunk_pos + out->pos; - } else { - out->chunk_pos += chunk->datalen; - pos = 0; - } - } - return -1; -} - -/** Advance pos by a single character, if there are any more characters - * in the buffer. Returns 0 on success, -1 on failure. */ -static INLINE int -buf_pos_inc(buf_pos_t *pos) -{ - ++pos->pos; - if (pos->pos == (off_t)pos->chunk->datalen) { - if (!pos->chunk->next) - return -1; - pos->chunk_pos += pos->chunk->datalen; - pos->chunk = pos->chunk->next; - pos->pos = 0; - } - return 0; -} - -/** Return true iff the n-character string in s appears - * (verbatim) at pos. */ -static int -buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n) -{ - buf_pos_t p; - if (!n) - return 1; - - memcpy(&p, pos, sizeof(p)); - - while (1) { - char ch = p.chunk->data[p.pos]; - if (ch != *s) - return 0; - ++s; - /* If we're out of characters that don't match, we match. Check this - * _before_ we test incrementing pos, in case we're at the end of the - * string. */ - if (--n == 0) - return 1; - if (buf_pos_inc(&p)<0) - return 0; - } -} - -/** Return the first position in buf at which the n-character - * string s occurs, or -1 if it does not occur. */ -STATIC int -buf_find_string_offset(const buf_t *buf, const char *s, size_t n) -{ - buf_pos_t pos; - buf_pos_init(buf, &pos); - while (buf_find_pos_of_char(*s, &pos) >= 0) { - if (buf_matches_at_pos(&pos, s, n)) { - tor_assert(pos.chunk_pos + pos.pos < INT_MAX); - return (int)(pos.chunk_pos + pos.pos); - } else { - if (buf_pos_inc(&pos)<0) - return -1; - } - } - return -1; -} - -/** There is a (possibly incomplete) http statement on buf, of the - * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.) - * If a) the headers include a Content-Length field and all bytes in - * the body are present, or b) there's no Content-Length field and - * all headers are present, then: - * - * - strdup headers into *headers_out, and NUL-terminate it. - * - memdup body into *body_out, and NUL-terminate it. - * - Then remove them from buf, and return 1. - * - * - If headers or body is NULL, discard that part of the buf. - * - If a headers or body doesn't fit in the arg, return -1. - * (We ensure that the headers or body don't exceed max len, - * _even if_ we're planning to discard them.) - * - If force_complete is true, then succeed even if not all of the - * content has arrived. - * - * Else, change nothing and return 0. - */ -int -fetch_from_buf_http(buf_t *buf, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, size_t max_bodylen, - int force_complete) -{ - char *headers, *p; - size_t headerlen, bodylen, contentlen; - int crlf_offset; - - check(); - if (!buf->head) - return 0; - - crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4); - if (crlf_offset > (int)max_headerlen || - (crlf_offset < 0 && buf->datalen > max_headerlen)) { - log_debug(LD_HTTP,"headers too long."); - return -1; - } else if (crlf_offset < 0) { - log_debug(LD_HTTP,"headers not all here yet."); - return 0; - } - /* Okay, we have a full header. Make sure it all appears in the first - * chunk. */ - if ((int)buf->head->datalen < crlf_offset + 4) - buf_pullup(buf, crlf_offset+4, 0); - headerlen = crlf_offset + 4; - - headers = buf->head->data; - bodylen = buf->datalen - headerlen; - log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen); - - if (max_headerlen <= headerlen) { - log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.", - (int)headerlen, (int)max_headerlen-1); - return -1; - } - if (max_bodylen <= bodylen) { - log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.", - (int)bodylen, (int)max_bodylen-1); - return -1; - } - -#define CONTENT_LENGTH "\r\nContent-Length: " - p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH); - if (p) { - int i; - i = atoi(p+strlen(CONTENT_LENGTH)); - if (i < 0) { - log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like " - "someone is trying to crash us."); - return -1; - } - contentlen = i; - /* if content-length is malformed, then our body length is 0. fine. */ - log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen); - if (bodylen < contentlen) { - if (!force_complete) { - log_debug(LD_HTTP,"body not all here yet."); - return 0; /* not all there yet */ - } - } - if (bodylen > contentlen) { - bodylen = contentlen; - log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen); - } - } - /* all happy. copy into the appropriate places, and return 1 */ - if (headers_out) { - *headers_out = tor_malloc(headerlen+1); - fetch_from_buf(*headers_out, headerlen, buf); - (*headers_out)[headerlen] = 0; /* NUL terminate it */ - } - if (body_out) { - tor_assert(body_used); - *body_used = bodylen; - *body_out = tor_malloc(bodylen+1); - fetch_from_buf(*body_out, bodylen, buf); - (*body_out)[bodylen] = 0; /* NUL terminate it */ - } - check(); - return 1; -} - -#ifdef USE_BUFFEREVENTS -/** As fetch_from_buf_http, buf works on an evbuffer. */ -int -fetch_from_evbuffer_http(struct evbuffer *buf, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, size_t max_bodylen, - int force_complete) -{ - struct evbuffer_ptr crlf, content_length; - size_t headerlen, bodylen, contentlen; - - /* Find the first \r\n\r\n in the buffer */ - crlf = evbuffer_search(buf, "\r\n\r\n", 4, NULL); - if (crlf.pos < 0) { - /* We didn't find one. */ - if (evbuffer_get_length(buf) > max_headerlen) - return -1; /* Headers too long. */ - return 0; /* Headers not here yet. */ - } else if (crlf.pos > (int)max_headerlen) { - return -1; /* Headers too long. */ - } - - headerlen = crlf.pos + 4; /* Skip over the \r\n\r\n */ - bodylen = evbuffer_get_length(buf) - headerlen; - if (bodylen > max_bodylen) - return -1; /* body too long */ - - /* Look for the first occurrence of CONTENT_LENGTH insize buf before the - * crlfcrlf */ - content_length = evbuffer_search_range(buf, CONTENT_LENGTH, - strlen(CONTENT_LENGTH), NULL, &crlf); - - if (content_length.pos >= 0) { - /* We found a content_length: parse it and figure out if the body is here - * yet. */ - struct evbuffer_ptr eol; - char *data = NULL; - int free_data = 0; - int n, i; - n = evbuffer_ptr_set(buf, &content_length, strlen(CONTENT_LENGTH), - EVBUFFER_PTR_ADD); - tor_assert(n == 0); - eol = evbuffer_search_eol(buf, &content_length, NULL, EVBUFFER_EOL_CRLF); - tor_assert(eol.pos > content_length.pos); - tor_assert(eol.pos <= crlf.pos); - inspect_evbuffer(buf, &data, eol.pos - content_length.pos, &free_data, - &content_length); - - i = atoi(data); - if (free_data) - tor_free(data); - if (i < 0) { - log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like " - "someone is trying to crash us."); - return -1; - } - contentlen = i; - /* if content-length is malformed, then our body length is 0. fine. */ - log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen); - if (bodylen < contentlen) { - if (!force_complete) { - log_debug(LD_HTTP,"body not all here yet."); - return 0; /* not all there yet */ - } - } - if (bodylen > contentlen) { - bodylen = contentlen; - log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen); - } - } - - if (headers_out) { - *headers_out = tor_malloc(headerlen+1); - evbuffer_remove(buf, *headers_out, headerlen); - (*headers_out)[headerlen] = '\0'; - } - if (body_out) { - tor_assert(headers_out); - tor_assert(body_used); - *body_used = bodylen; - *body_out = tor_malloc(bodylen+1); - evbuffer_remove(buf, *body_out, bodylen); - (*body_out)[bodylen] = '\0'; - } - return 1; -} -#endif - -/** - * Wait this many seconds before warning the user about using SOCKS unsafely - * again (requires that WarnUnsafeSocks is turned on). */ -#define SOCKS_WARN_INTERVAL 5 - -/** Warn that the user application has made an unsafe socks request using - * protocol socks_protocol on port port. Don't warn more than - * once per SOCKS_WARN_INTERVAL, unless safe_socks is set. */ -static void -log_unsafe_socks_warning(int socks_protocol, const char *address, - uint16_t port, int safe_socks) -{ - static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL); - - const or_options_t *options = get_options(); - if (! options->WarnUnsafeSocks) - return; - if (safe_socks) { - log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP, - "Your application (using socks%d to port %d) is giving " - "Tor only an IP address. Applications that do DNS resolves " - "themselves may leak information. Consider using Socks4A " - "(e.g. via privoxy or socat) instead. For more information, " - "please see https://wiki.torproject.org/TheOnionRouter/" - "TorFAQ#SOCKSAndDNS.%s", - socks_protocol, - (int)port, - safe_socks ? " Rejecting." : ""); - } - control_event_client_status(LOG_WARN, - "DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d", - socks_protocol, address, (int)port); -} - -/** Do not attempt to parse socks messages longer than this. This value is - * actually significantly higher than the longest possible socks message. */ -#define MAX_SOCKS_MESSAGE_LEN 512 - -/** Return a new socks_request_t. */ -socks_request_t * -socks_request_new(void) -{ - return tor_malloc_zero(sizeof(socks_request_t)); -} - -/** Free all storage held in the socks_request_t req. */ -void -socks_request_free(socks_request_t *req) -{ - if (!req) - return; - if (req->username) { - memwipe(req->username, 0x10, req->usernamelen); - tor_free(req->username); - } - if (req->password) { - memwipe(req->password, 0x04, req->passwordlen); - tor_free(req->password); - } - memwipe(req, 0xCC, sizeof(socks_request_t)); - tor_free(req); -} - -/** There is a (possibly incomplete) socks handshake on buf, of one - * of the forms - * - socks4: "socksheader username\\0" - * - socks4a: "socksheader username\\0 destaddr\\0" - * - socks5 phase one: "version #methods methods" - * - socks5 phase two: "version command 0 addresstype..." - * If it's a complete and valid handshake, and destaddr fits in - * MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf, - * assign to req, and return 1. - * - * If it's invalid or too big, return -1. - * - * Else it's not all there yet, leave buf alone and return 0. - * - * If you want to specify the socks reply, write it into req->reply - * and set req->replylen, else leave req->replylen alone. - * - * If log_sockstype is non-zero, then do a notice-level log of whether - * the connection is possibly leaking DNS requests locally or not. - * - * If safe_socks is true, then reject unsafe socks protocols. - * - * If returning 0 or -1, req->address and req->port are - * undefined. - */ -int -fetch_from_buf_socks(buf_t *buf, socks_request_t *req, - int log_sockstype, int safe_socks) -{ - int res; - ssize_t n_drain; - size_t want_length = 128; - - if (buf->datalen < 2) /* version and another byte */ - return 0; - - do { - n_drain = 0; - buf_pullup(buf, want_length, 0); - tor_assert(buf->head && buf->head->datalen >= 2); - want_length = 0; - - res = parse_socks(buf->head->data, buf->head->datalen, req, log_sockstype, - safe_socks, &n_drain, &want_length); - - if (n_drain < 0) - buf_clear(buf); - else if (n_drain > 0) - buf_remove_from_front(buf, n_drain); - - } while (res == 0 && buf->head && want_length < buf->datalen && - buf->datalen >= 2); - - return res; -} - -#ifdef USE_BUFFEREVENTS -/* As fetch_from_buf_socks(), but targets an evbuffer instead. */ -int -fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req, - int log_sockstype, int safe_socks) -{ - char *data; - ssize_t n_drain; - size_t datalen, buflen, want_length; - int res; - - buflen = evbuffer_get_length(buf); - if (buflen < 2) - return 0; - - { - /* See if we can find the socks request in the first chunk of the buffer. - */ - struct evbuffer_iovec v; - int i; - n_drain = 0; - i = evbuffer_peek(buf, -1, NULL, &v, 1); - tor_assert(i == 1); - data = v.iov_base; - datalen = v.iov_len; - want_length = 0; - - res = parse_socks(data, datalen, req, log_sockstype, - safe_socks, &n_drain, &want_length); - - if (n_drain < 0) - evbuffer_drain(buf, evbuffer_get_length(buf)); - else if (n_drain > 0) - evbuffer_drain(buf, n_drain); - - if (res) - return res; - } - - /* Okay, the first chunk of the buffer didn't have a complete socks request. - * That means that either we don't have a whole socks request at all, or - * it's gotten split up. We're going to try passing parse_socks() bigger - * and bigger chunks until either it says "Okay, I got it", or it says it - * will need more data than we currently have. */ - - /* Loop while we have more data that we haven't given parse_socks() yet. */ - do { - int free_data = 0; - const size_t last_wanted = want_length; - n_drain = 0; - data = NULL; - datalen = inspect_evbuffer(buf, &data, want_length, &free_data, NULL); - - want_length = 0; - res = parse_socks(data, datalen, req, log_sockstype, - safe_socks, &n_drain, &want_length); - - if (free_data) - tor_free(data); - - if (n_drain < 0) - evbuffer_drain(buf, evbuffer_get_length(buf)); - else if (n_drain > 0) - evbuffer_drain(buf, n_drain); - - if (res == 0 && n_drain == 0 && want_length <= last_wanted) { - /* If we drained nothing, and we didn't ask for more than last time, - * then we probably wanted more data than the buffer actually had, - * and we're finding out that we're not satisified with it. It's - * time to break until we have more data. */ - break; - } - - buflen = evbuffer_get_length(buf); - } while (res == 0 && want_length <= buflen && buflen >= 2); - - return res; -} -#endif - -/** The size of the header of an Extended ORPort message: 2 bytes for - * COMMAND, 2 bytes for BODYLEN */ -#define EXT_OR_CMD_HEADER_SIZE 4 - -/** Read buf, which should contain an Extended ORPort message - * from a transport proxy. If well-formed, create and populate - * out with the Extended ORport message. Return 0 if the - * buffer was incomplete, 1 if it was well-formed and -1 if we - * encountered an error while parsing it. */ -int -fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out) -{ - char hdr[EXT_OR_CMD_HEADER_SIZE]; - uint16_t len; - - check(); - if (buf->datalen < EXT_OR_CMD_HEADER_SIZE) - return 0; - peek_from_buf(hdr, sizeof(hdr), buf); - len = ntohs(get_uint16(hdr+2)); - if (buf->datalen < (unsigned)len + EXT_OR_CMD_HEADER_SIZE) - return 0; - *out = ext_or_cmd_new(len); - (*out)->cmd = ntohs(get_uint16(hdr)); - (*out)->len = len; - buf_remove_from_front(buf, EXT_OR_CMD_HEADER_SIZE); - fetch_from_buf((*out)->body, len, buf); - return 1; -} - -#ifdef USE_BUFFEREVENTS -/** Read buf, which should contain an Extended ORPort message - * from a transport proxy. If well-formed, create and populate - * out with the Extended ORport message. Return 0 if the - * buffer was incomplete, 1 if it was well-formed and -1 if we - * encountered an error while parsing it. */ -int -fetch_ext_or_command_from_evbuffer(struct evbuffer *buf, ext_or_cmd_t **out) -{ - char hdr[EXT_OR_CMD_HEADER_SIZE]; - uint16_t len; - size_t buf_len = evbuffer_get_length(buf); - - if (buf_len < EXT_OR_CMD_HEADER_SIZE) - return 0; - evbuffer_copyout(buf, hdr, EXT_OR_CMD_HEADER_SIZE); - len = ntohs(get_uint16(hdr+2)); - if (buf_len < (unsigned)len + EXT_OR_CMD_HEADER_SIZE) - return 0; - *out = ext_or_cmd_new(len); - (*out)->cmd = ntohs(get_uint16(hdr)); - (*out)->len = len; - evbuffer_drain(buf, EXT_OR_CMD_HEADER_SIZE); - evbuffer_remove(buf, (*out)->body, len); - return 1; -} -#endif - -/** Implementation helper to implement fetch_from_*_socks. Instead of looking - * at a buffer's contents, we look at the datalen bytes of data in - * data. Instead of removing data from the buffer, we set - * drain_out to the amount of data that should be removed (or -1 if the - * buffer should be cleared). Instead of pulling more data into the first - * chunk of the buffer, we set *want_length_out to the number of bytes - * we'd like to see in the input buffer, if they're available. */ -static int -parse_socks(const char *data, size_t datalen, socks_request_t *req, - int log_sockstype, int safe_socks, ssize_t *drain_out, - size_t *want_length_out) -{ - unsigned int len; - char tmpbuf[TOR_ADDR_BUF_LEN+1]; - tor_addr_t destaddr; - uint32_t destip; - uint8_t socksver; - char *next, *startaddr; - unsigned char usernamelen, passlen; - struct in_addr in; - - if (datalen < 2) { - /* We always need at least 2 bytes. */ - *want_length_out = 2; - return 0; - } - - if (req->socks_version == 5 && !req->got_auth) { - /* See if we have received authentication. Strictly speaking, we should - also check whether we actually negotiated username/password - authentication. But some broken clients will send us authentication - even if we negotiated SOCKS_NO_AUTH. */ - if (*data == 1) { /* username/pass version 1 */ - /* Format is: authversion [1 byte] == 1 - usernamelen [1 byte] - username [usernamelen bytes] - passlen [1 byte] - password [passlen bytes] */ - usernamelen = (unsigned char)*(data + 1); - if (datalen < 2u + usernamelen + 1u) { - *want_length_out = 2u + usernamelen + 1u; - return 0; - } - passlen = (unsigned char)*(data + 2u + usernamelen); - if (datalen < 2u + usernamelen + 1u + passlen) { - *want_length_out = 2u + usernamelen + 1u + passlen; - return 0; - } - req->replylen = 2; /* 2 bytes of response */ - req->reply[0] = 1; /* authversion == 1 */ - req->reply[1] = 0; /* authentication successful */ - log_debug(LD_APP, - "socks5: Accepted username/password without checking."); - if (usernamelen) { - req->username = tor_memdup(data+2u, usernamelen); - req->usernamelen = usernamelen; - } - if (passlen) { - req->password = tor_memdup(data+3u+usernamelen, passlen); - req->passwordlen = passlen; - } - *drain_out = 2u + usernamelen + 1u + passlen; - req->got_auth = 1; - *want_length_out = 7; /* Minimal socks5 sommand. */ - return 0; - } else if (req->auth_type == SOCKS_USER_PASS) { - /* unknown version byte */ - log_warn(LD_APP, "Socks5 username/password version %d not recognized; " - "rejecting.", (int)*data); - return -1; - } - } - - socksver = *data; - - switch (socksver) { /* which version of socks? */ - case 5: /* socks5 */ - - if (req->socks_version != 5) { /* we need to negotiate a method */ - unsigned char nummethods = (unsigned char)*(data+1); - int have_user_pass, have_no_auth; - int r=0; - tor_assert(!req->socks_version); - if (datalen < 2u+nummethods) { - *want_length_out = 2u+nummethods; - return 0; - } - if (!nummethods) - return -1; - req->replylen = 2; /* 2 bytes of response */ - req->reply[0] = 5; /* socks5 reply */ - have_user_pass = (memchr(data+2, SOCKS_USER_PASS, nummethods) !=NULL); - have_no_auth = (memchr(data+2, SOCKS_NO_AUTH, nummethods) !=NULL); - if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) { - req->auth_type = SOCKS_USER_PASS; - req->reply[1] = SOCKS_USER_PASS; /* tell client to use "user/pass" - auth method */ - req->socks_version = 5; /* remember we've already negotiated auth */ - log_debug(LD_APP,"socks5: accepted method 2 (username/password)"); - r=0; - } else if (have_no_auth) { - req->reply[1] = SOCKS_NO_AUTH; /* tell client to use "none" auth - method */ - req->socks_version = 5; /* remember we've already negotiated auth */ - log_debug(LD_APP,"socks5: accepted method 0 (no authentication)"); - r=0; - } else { - log_warn(LD_APP, - "socks5: offered methods don't include 'no auth' or " - "username/password. Rejecting."); - req->reply[1] = '\xFF'; /* reject all methods */ - r=-1; - } - /* Remove packet from buf. Some SOCKS clients will have sent extra - * junk at this point; let's hope it's an authentication message. */ - *drain_out = 2u + nummethods; - - return r; - } - if (req->auth_type != SOCKS_NO_AUTH && !req->got_auth) { - log_warn(LD_APP, - "socks5: negotiated authentication, but none provided"); - return -1; - } - /* we know the method; read in the request */ - log_debug(LD_APP,"socks5: checking request"); - if (datalen < 7) {/* basic info plus >=1 for addr plus 2 for port */ - *want_length_out = 7; - return 0; /* not yet */ - } - req->command = (unsigned char) *(data+1); - if (req->command != SOCKS_COMMAND_CONNECT && - req->command != SOCKS_COMMAND_RESOLVE && - req->command != SOCKS_COMMAND_RESOLVE_PTR) { - /* not a connect or resolve or a resolve_ptr? we don't support it. */ - log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.", - req->command); - return -1; - } - switch (*(data+3)) { /* address type */ - case 1: /* IPv4 address */ - case 4: /* IPv6 address */ { - const int is_v6 = *(data+3) == 4; - const unsigned addrlen = is_v6 ? 16 : 4; - log_debug(LD_APP,"socks5: ipv4 address type"); - if (datalen < 6+addrlen) {/* ip/port there? */ - *want_length_out = 6+addrlen; - return 0; /* not yet */ - } - - if (is_v6) - tor_addr_from_ipv6_bytes(&destaddr, data+4); - else - tor_addr_from_ipv4n(&destaddr, get_uint32(data+4)); - - tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1); - - if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) { - log_warn(LD_APP, - "socks5 IP takes %d bytes, which doesn't fit in %d. " - "Rejecting.", - (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN); - return -1; - } - strlcpy(req->address,tmpbuf,sizeof(req->address)); - req->port = ntohs(get_uint16(data+4+addrlen)); - *drain_out = 6+addrlen; - if (req->command != SOCKS_COMMAND_RESOLVE_PTR && - !addressmap_have_mapping(req->address,0)) { - log_unsafe_socks_warning(5, req->address, req->port, safe_socks); - if (safe_socks) - return -1; - } - return 1; - } - case 3: /* fqdn */ - log_debug(LD_APP,"socks5: fqdn address type"); - if (req->command == SOCKS_COMMAND_RESOLVE_PTR) { - log_warn(LD_APP, "socks5 received RESOLVE_PTR command with " - "hostname type. Rejecting."); - return -1; - } - len = (unsigned char)*(data+4); - if (datalen < 7+len) { /* addr/port there? */ - *want_length_out = 7+len; - return 0; /* not yet */ - } - if (len+1 > MAX_SOCKS_ADDR_LEN) { - log_warn(LD_APP, - "socks5 hostname is %d bytes, which doesn't fit in " - "%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN); - return -1; - } - memcpy(req->address,data+5,len); - req->address[len] = 0; - req->port = ntohs(get_uint16(data+5+len)); - *drain_out = 5+len+2; - if (!tor_strisprint(req->address) || strchr(req->address,'\"')) { - log_warn(LD_PROTOCOL, - "Your application (using socks5 to port %d) gave Tor " - "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped(req->address)); - return -1; - } - if (log_sockstype) - log_notice(LD_APP, - "Your application (using socks5 to port %d) instructed " - "Tor to take care of the DNS resolution itself if " - "necessary. This is good.", req->port); - return 1; - default: /* unsupported */ - log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.", - (int) *(data+3)); - return -1; - } - tor_assert(0); - case 4: { /* socks4 */ - enum {socks4, socks4a} socks4_prot = socks4a; - const char *authstart, *authend; - /* http://ss5.sourceforge.net/socks4.protocol.txt */ - /* http://ss5.sourceforge.net/socks4A.protocol.txt */ - - req->socks_version = 4; - if (datalen < SOCKS4_NETWORK_LEN) {/* basic info available? */ - *want_length_out = SOCKS4_NETWORK_LEN; - return 0; /* not yet */ - } - // buf_pullup(buf, 1280, 0); - req->command = (unsigned char) *(data+1); - if (req->command != SOCKS_COMMAND_CONNECT && - req->command != SOCKS_COMMAND_RESOLVE) { - /* not a connect or resolve? we don't support it. (No resolve_ptr with - * socks4.) */ - log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.", - req->command); - return -1; - } - - req->port = ntohs(get_uint16(data+2)); - destip = ntohl(get_uint32(data+4)); - if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) { - log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting."); - return -1; - } - if (destip >> 8) { - log_debug(LD_APP,"socks4: destip not in form 0.0.0.x."); - in.s_addr = htonl(destip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); - if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) { - log_debug(LD_APP,"socks4 addr (%d bytes) too long. Rejecting.", - (int)strlen(tmpbuf)); - return -1; - } - log_debug(LD_APP, - "socks4: successfully read destip (%s)", - safe_str_client(tmpbuf)); - socks4_prot = socks4; - } - - authstart = data + SOCKS4_NETWORK_LEN; - next = memchr(authstart, 0, - datalen-SOCKS4_NETWORK_LEN); - if (!next) { - if (datalen >= 1024) { - log_debug(LD_APP, "Socks4 user name too long; rejecting."); - return -1; - } - log_debug(LD_APP,"socks4: Username not here yet."); - *want_length_out = datalen+1024; /* More than we need, but safe */ - return 0; - } - authend = next; - tor_assert(next < data+datalen); - - startaddr = NULL; - if (socks4_prot != socks4a && - !addressmap_have_mapping(tmpbuf,0)) { - log_unsafe_socks_warning(4, tmpbuf, req->port, safe_socks); - - if (safe_socks) - return -1; - } - if (socks4_prot == socks4a) { - if (next+1 == data+datalen) { - log_debug(LD_APP,"socks4: No part of destaddr here yet."); - *want_length_out = datalen + 1024; /* More than we need, but safe */ - return 0; - } - startaddr = next+1; - next = memchr(startaddr, 0, data + datalen - startaddr); - if (!next) { - if (datalen >= 1024) { - log_debug(LD_APP,"socks4: Destaddr too long."); - return -1; - } - log_debug(LD_APP,"socks4: Destaddr not all here yet."); - *want_length_out = datalen + 1024; /* More than we need, but safe */ - return 0; - } - if (MAX_SOCKS_ADDR_LEN <= next-startaddr) { - log_warn(LD_APP,"socks4: Destaddr too long. Rejecting."); - return -1; - } - // tor_assert(next < buf->cur+buf->datalen); - - if (log_sockstype) - log_notice(LD_APP, - "Your application (using socks4a to port %d) instructed " - "Tor to take care of the DNS resolution itself if " - "necessary. This is good.", req->port); - } - log_debug(LD_APP,"socks4: Everything is here. Success."); - strlcpy(req->address, startaddr ? startaddr : tmpbuf, - sizeof(req->address)); - if (!tor_strisprint(req->address) || strchr(req->address,'\"')) { - log_warn(LD_PROTOCOL, - "Your application (using socks4 to port %d) gave Tor " - "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped(req->address)); - return -1; - } - if (authend != authstart) { - req->got_auth = 1; - req->usernamelen = authend - authstart; - req->username = tor_memdup(authstart, authend - authstart); - } - /* next points to the final \0 on inbuf */ - *drain_out = next - data + 1; - return 1; - } - case 'G': /* get */ - case 'H': /* head */ - case 'P': /* put/post */ - case 'C': /* connect */ - strlcpy((char*)req->reply, -"HTTP/1.0 501 Tor is not an HTTP Proxy\r\n" -"Content-Type: text/html; charset=iso-8859-1\r\n\r\n" -"\n" -"\n" -"Tor is not an HTTP Proxy\n" -"\n" -"\n" -"

Tor is not an HTTP Proxy

\n" -"

\n" -"It appears you have configured your web browser to use Tor as an HTTP proxy." -"\n" -"This is not correct: Tor is a SOCKS proxy, not an HTTP proxy.\n" -"Please configure your client accordingly.\n" -"

\n" -"

\n" -"See " - "https://www.torproject.org/documentation.html for more " - "information.\n" -"\n" -"

\n" -"\n" -"\n" - , MAX_SOCKS_REPLY_LEN); - req->replylen = strlen((char*)req->reply)+1; - /* fall through */ - default: /* version is not socks4 or socks5 */ - log_warn(LD_APP, - "Socks version %d not recognized. (Tor is not an http proxy.)", - *(data)); - { - /* Tell the controller the first 8 bytes. */ - char *tmp = tor_strndup(data, datalen < 8 ? datalen : 8); - control_event_client_status(LOG_WARN, - "SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"", - escaped(tmp)); - tor_free(tmp); - } - return -1; - } -} - -/** Inspect a reply from SOCKS server stored in buf according - * to state, removing the protocol data upon success. Return 0 on - * incomplete response, 1 on success and -1 on error, in which case - * reason is set to a descriptive message (free() when finished - * with it). - * - * As a special case, 2 is returned when user/pass is required - * during SOCKS5 handshake and user/pass is configured. - */ -int -fetch_from_buf_socks_client(buf_t *buf, int state, char **reason) -{ - ssize_t drain = 0; - int r; - if (buf->datalen < 2) - return 0; - - buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0); - tor_assert(buf->head && buf->head->datalen >= 2); - - r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen, - state, reason, &drain); - if (drain > 0) - buf_remove_from_front(buf, drain); - else if (drain < 0) - buf_clear(buf); - - return r; -} - -#ifdef USE_BUFFEREVENTS -/** As fetch_from_buf_socks_client, buf works on an evbuffer */ -int -fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state, - char **reason) -{ - ssize_t drain = 0; - uint8_t *data; - size_t datalen; - int r; - - /* Linearize the SOCKS response in the buffer, up to 128 bytes. - * (parse_socks_client shouldn't need to see anything beyond that.) */ - datalen = evbuffer_get_length(buf); - if (datalen > MAX_SOCKS_MESSAGE_LEN) - datalen = MAX_SOCKS_MESSAGE_LEN; - data = evbuffer_pullup(buf, datalen); - - r = parse_socks_client(data, datalen, state, reason, &drain); - if (drain > 0) - evbuffer_drain(buf, drain); - else if (drain < 0) - evbuffer_drain(buf, evbuffer_get_length(buf)); - - return r; -} -#endif - -/** Implementation logic for fetch_from_*_socks_client. */ -static int -parse_socks_client(const uint8_t *data, size_t datalen, - int state, char **reason, - ssize_t *drain_out) -{ - unsigned int addrlen; - *drain_out = 0; - if (datalen < 2) - return 0; - - switch (state) { - case PROXY_SOCKS4_WANT_CONNECT_OK: - /* Wait for the complete response */ - if (datalen < 8) - return 0; - - if (data[1] != 0x5a) { - *reason = tor_strdup(socks4_response_code_to_string(data[1])); - return -1; - } - - /* Success */ - *drain_out = 8; - return 1; - - case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE: - /* we don't have any credentials */ - if (data[1] != 0x00) { - *reason = tor_strdup("server doesn't support any of our " - "available authentication methods"); - return -1; - } - - log_info(LD_NET, "SOCKS 5 client: continuing without authentication"); - *drain_out = -1; - return 1; - - case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929: - /* we have a username and password. return 1 if we can proceed without - * providing authentication, or 2 otherwise. */ - switch (data[1]) { - case 0x00: - log_info(LD_NET, "SOCKS 5 client: we have auth details but server " - "doesn't require authentication."); - *drain_out = -1; - return 1; - case 0x02: - log_info(LD_NET, "SOCKS 5 client: need authentication."); - *drain_out = -1; - return 2; - /* fall through */ - } - - *reason = tor_strdup("server doesn't support any of our available " - "authentication methods"); - return -1; - - case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK: - /* handle server reply to rfc1929 authentication */ - if (data[1] != 0x00) { - *reason = tor_strdup("authentication failed"); - return -1; - } - - log_info(LD_NET, "SOCKS 5 client: authentication successful."); - *drain_out = -1; - return 1; - - case PROXY_SOCKS5_WANT_CONNECT_OK: - /* response is variable length. BND.ADDR, etc, isn't needed - * (don't bother with buf_pullup()), but make sure to eat all - * the data used */ - - /* wait for address type field to arrive */ - if (datalen < 4) - return 0; - - switch (data[3]) { - case 0x01: /* ip4 */ - addrlen = 4; - break; - case 0x04: /* ip6 */ - addrlen = 16; - break; - case 0x03: /* fqdn (can this happen here?) */ - if (datalen < 5) - return 0; - addrlen = 1 + data[4]; - break; - default: - *reason = tor_strdup("invalid response to connect request"); - return -1; - } - - /* wait for address and port */ - if (datalen < 6 + addrlen) - return 0; - - if (data[1] != 0x00) { - *reason = tor_strdup(socks5_response_code_to_string(data[1])); - return -1; - } - - *drain_out = 6 + addrlen; - return 1; - } - - /* shouldn't get here... */ - tor_assert(0); - - return -1; -} - -/** Return 1 iff buf looks more like it has an (obsolete) v0 controller - * command on it than any valid v1 controller command. */ -int -peek_buf_has_control0_command(buf_t *buf) -{ - if (buf->datalen >= 4) { - char header[4]; - uint16_t cmd; - peek_from_buf(header, sizeof(header), buf); - cmd = ntohs(get_uint16(header+2)); - if (cmd <= 0x14) - return 1; /* This is definitely not a v1 control command. */ - } - return 0; -} - -#ifdef USE_BUFFEREVENTS -int -peek_evbuffer_has_control0_command(struct evbuffer *buf) -{ - int result = 0; - if (evbuffer_get_length(buf) >= 4) { - int free_out = 0; - char *data = NULL; - size_t n = inspect_evbuffer(buf, &data, 4, &free_out, NULL); - uint16_t cmd; - tor_assert(n >= 4); - cmd = ntohs(get_uint16(data+2)); - if (cmd <= 0x14) - result = 1; - if (free_out) - tor_free(data); - } - return result; -} -#endif - -/** Return the index within buf at which ch first appears, - * or -1 if ch does not appear on buf. */ -static off_t -buf_find_offset_of_char(buf_t *buf, char ch) -{ - chunk_t *chunk; - off_t offset = 0; - for (chunk = buf->head; chunk; chunk = chunk->next) { - char *cp = memchr(chunk->data, ch, chunk->datalen); - if (cp) - return offset + (cp - chunk->data); - else - offset += chunk->datalen; - } - return -1; -} - -/** Try to read a single LF-terminated line from buf, and write it - * (including the LF), NUL-terminated, into the *data_len byte buffer - * at data_out. Set *data_len to the number of bytes in the - * line, not counting the terminating NUL. Return 1 if we read a whole line, - * return 0 if we don't have a whole line yet, and return -1 if the line - * length exceeds *data_len. - */ -int -fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len) -{ - size_t sz; - off_t offset; - - if (!buf->head) - return 0; - - offset = buf_find_offset_of_char(buf, '\n'); - if (offset < 0) - return 0; - sz = (size_t) offset; - if (sz+2 > *data_len) { - *data_len = sz + 2; - return -1; - } - fetch_from_buf(data_out, sz+1, buf); - data_out[sz+1] = '\0'; - *data_len = sz+1; - return 1; -} - -/** Compress on uncompress the data_len bytes in data using the - * zlib state state, appending the result to buf. If - * done is true, flush the data in the state and finish the - * compression/uncompression. Return -1 on failure, 0 on success. */ -int -write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state, - const char *data, size_t data_len, - int done) -{ - char *next; - size_t old_avail, avail; - int over = 0; - do { - int need_new_chunk = 0; - if (!buf->tail || ! CHUNK_REMAINING_CAPACITY(buf->tail)) { - size_t cap = data_len / 4; - buf_add_chunk_with_capacity(buf, cap, 1); - } - next = CHUNK_WRITE_PTR(buf->tail); - avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail); - switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) { - case TOR_ZLIB_DONE: - over = 1; - break; - case TOR_ZLIB_ERR: - return -1; - case TOR_ZLIB_OK: - if (data_len == 0) - over = 1; - break; - case TOR_ZLIB_BUF_FULL: - if (avail) { - /* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk - * automatically, whether were going to or not. */ - need_new_chunk = 1; - } - break; - } - buf->datalen += old_avail - avail; - buf->tail->datalen += old_avail - avail; - if (need_new_chunk) { - buf_add_chunk_with_capacity(buf, data_len/4, 1); - } - - } while (!over); - check(); - return 0; -} - -#ifdef USE_BUFFEREVENTS -int -write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state, - const char *data, size_t data_len, - int done) -{ - char *next; - size_t old_avail, avail; - int over = 0, n; - struct evbuffer_iovec vec[1]; - do { - { - size_t cap = data_len / 4; - if (cap < 128) - cap = 128; - /* XXXX NM this strategy is fragmentation-prone. We should really have - * two iovecs, and write first into the one, and then into the - * second if the first gets full. */ - n = evbuffer_reserve_space(buf, cap, vec, 1); - tor_assert(n == 1); - } - - next = vec[0].iov_base; - avail = old_avail = vec[0].iov_len; - - switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) { - case TOR_ZLIB_DONE: - over = 1; - break; - case TOR_ZLIB_ERR: - return -1; - case TOR_ZLIB_OK: - if (data_len == 0) - over = 1; - break; - case TOR_ZLIB_BUF_FULL: - if (avail) { - /* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk - * automatically, whether were going to or not. */ - } - break; - } - - /* XXXX possible infinite loop on BUF_FULL. */ - vec[0].iov_len = old_avail - avail; - evbuffer_commit_space(buf, vec, 1); - - } while (!over); - check(); - return 0; -} -#endif - -/** Set *output to contain a copy of the data in *input */ -int -generic_buffer_set_to_copy(generic_buffer_t **output, - const generic_buffer_t *input) -{ -#ifdef USE_BUFFEREVENTS - struct evbuffer_ptr ptr; - size_t remaining = evbuffer_get_length(input); - if (*output) { - evbuffer_drain(*output, evbuffer_get_length(*output)); - } else { - if (!(*output = evbuffer_new())) - return -1; - } - evbuffer_ptr_set((struct evbuffer*)input, &ptr, 0, EVBUFFER_PTR_SET); - while (remaining) { - struct evbuffer_iovec v[4]; - int n_used, i; - n_used = evbuffer_peek((struct evbuffer*)input, -1, &ptr, v, 4); - if (n_used < 0) - return -1; - for (i=0;ibuf is corrupted. - */ -void -assert_buf_ok(buf_t *buf) -{ - tor_assert(buf); - tor_assert(buf->magic == BUFFER_MAGIC); - - if (! buf->head) { - tor_assert(!buf->tail); - tor_assert(buf->datalen == 0); - } else { - chunk_t *ch; - size_t total = 0; - tor_assert(buf->tail); - for (ch = buf->head; ch; ch = ch->next) { - total += ch->datalen; - tor_assert(ch->datalen <= ch->memlen); - tor_assert(ch->data >= &ch->mem[0]); - tor_assert(ch->data < &ch->mem[0]+ch->memlen); - tor_assert(ch->data+ch->datalen <= &ch->mem[0] + ch->memlen); - if (!ch->next) - tor_assert(ch == buf->tail); - } - tor_assert(buf->datalen == total); - } -} - -#ifdef ENABLE_BUF_FREELISTS -/** Log an error and exit if fl is corrupted. - */ -static void -assert_freelist_ok(chunk_freelist_t *fl) -{ - chunk_t *ch; - int n; - tor_assert(fl->alloc_size > 0); - n = 0; - for (ch = fl->head; ch; ch = ch->next) { - tor_assert(CHUNK_ALLOC_SIZE(ch->memlen) == fl->alloc_size); - ++n; - } - tor_assert(n == fl->cur_length); - tor_assert(n >= fl->lowest_length); - tor_assert(n <= fl->max_length); -} -#endif - diff --git a/src/tor/buffers.h b/src/tor/buffers.h deleted file mode 100644 index 48b1185..0000000 --- a/src/tor/buffers.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file buffers.h - * \brief Header file for buffers.c. - **/ - -#ifndef TOR_BUFFERS_H -#define TOR_BUFFERS_H - -#include "testsupport.h" - -buf_t *buf_new(void); -buf_t *buf_new_with_capacity(size_t size); -void buf_free(buf_t *buf); -void buf_clear(buf_t *buf); -buf_t *buf_copy(const buf_t *buf); -void buf_shrink(buf_t *buf); -void buf_shrink_freelists(int free_all); -void buf_dump_freelist_sizes(int severity); - -size_t buf_datalen(const buf_t *buf); -size_t buf_allocation(const buf_t *buf); -size_t buf_slack(const buf_t *buf); - -int read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof, - int *socket_error); -int read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf); - -int flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen); -int flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t sz, size_t *buf_flushlen); - -int write_to_buf(const char *string, size_t string_len, buf_t *buf); -int write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state, - const char *data, size_t data_len, int done); -int move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen); -int fetch_from_buf(char *string, size_t string_len, buf_t *buf); -int fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto); -int fetch_from_buf_http(buf_t *buf, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, size_t max_bodylen, - int force_complete); -socks_request_t *socks_request_new(void); -void socks_request_free(socks_request_t *req); -int fetch_from_buf_socks(buf_t *buf, socks_request_t *req, - int log_sockstype, int safe_socks); -int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason); -int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len); - -int peek_buf_has_control0_command(buf_t *buf); - -int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out); - -#ifdef USE_BUFFEREVENTS -int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out, - int linkproto); -int fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req, - int log_sockstype, int safe_socks); -int fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state, - char **reason); -int fetch_from_evbuffer_http(struct evbuffer *buf, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, size_t max_bodylen, - int force_complete); -int peek_evbuffer_has_control0_command(struct evbuffer *buf); -int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state, - const char *data, size_t data_len, - int done); -int fetch_ext_or_command_from_evbuffer(struct evbuffer *buf, - ext_or_cmd_t **out); -#endif - -#ifdef USE_BUFFEREVENTS -#define generic_buffer_new() evbuffer_new() -#define generic_buffer_len(b) evbuffer_get_length((b)) -#define generic_buffer_add(b,dat,len) evbuffer_add((b),(dat),(len)) -#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen)) -#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b))) -#define generic_buffer_free(b) evbuffer_free((b)) -#define generic_buffer_fetch_ext_or_cmd(b, out) \ - fetch_ext_or_command_from_evbuffer((b), (out)) -#else -#define generic_buffer_new() buf_new() -#define generic_buffer_len(b) buf_datalen((b)) -#define generic_buffer_add(b,dat,len) write_to_buf((dat),(len),(b)) -#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b)) -#define generic_buffer_clear(b) buf_clear((b)) -#define generic_buffer_free(b) buf_free((b)) -#define generic_buffer_fetch_ext_or_cmd(b, out) \ - fetch_ext_or_command_from_buf((b), (out)) -#endif -int generic_buffer_set_to_copy(generic_buffer_t **output, - const generic_buffer_t *input); - -void assert_buf_ok(buf_t *buf); - -#ifdef BUFFERS_PRIVATE -STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); -#endif - -#endif - diff --git a/src/tor/build-libtor.sh b/src/tor/build-libtor.sh new file mode 100644 index 0000000..063b5d7 --- /dev/null +++ b/src/tor/build-libtor.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TOR_SRC_DIR="${TOR_SRC_DIR:-$ROOT_DIR/tor-src}" + +if [[ ! -d "$TOR_SRC_DIR" ]]; then + echo "Tor source tree not found at: $TOR_SRC_DIR" >&2 + exit 1 +fi + +cd "$TOR_SRC_DIR" + +if [[ ! -x "./configure" ]]; then + echo "Running autogen.sh" + ./autogen.sh +fi + +echo "Configuring Tor static library build from: $TOR_SRC_DIR" +./configure \ + --enable-static-tor \ + --disable-module-relay \ + --disable-module-dirauth \ + --disable-asciidoc \ + --disable-manpage \ + --disable-html-manual \ + --disable-unittests \ + --disable-tool-name-check + +echo "Building Tor" +make -j"${NPROC:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}" + +echo +echo "Build finished. Inspect these locations for static libraries:" +echo " $TOR_SRC_DIR/src/core" +echo " $TOR_SRC_DIR/src/lib" +echo " $TOR_SRC_DIR/src/trunnel" +echo +echo "Suggested next step for Triangles:" +echo ' make -f src/makefile.unix USE_TOR_EMBEDDED=1' diff --git a/src/tor/channel.c b/src/tor/channel.c deleted file mode 100644 index 62796d9..0000000 --- a/src/tor/channel.c +++ /dev/null @@ -1,4191 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file channel.c - * \brief OR-to-OR channel abstraction layer - **/ - -/* - * Define this so channel.h gives us things only channel_t subclasses - * should touch. - */ - -#define TOR_CHANNEL_INTERNAL_ - -#include "or.h" -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitstats.h" -#include "connection_or.h" /* For var_cell_free() */ -#include "circuitmux.h" -#include "entrynodes.h" -#include "geoip.h" -#include "nodelist.h" -#include "relay.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" - -/* Cell queue structure */ - -typedef struct cell_queue_entry_s cell_queue_entry_t; -struct cell_queue_entry_s { - TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next; - enum { - CELL_QUEUE_FIXED, - CELL_QUEUE_VAR, - CELL_QUEUE_PACKED - } type; - union { - struct { - cell_t *cell; - } fixed; - struct { - var_cell_t *var_cell; - } var; - struct { - packed_cell_t *packed_cell; - } packed; - } u; -}; - -/* Global lists of channels */ - -/* All channel_t instances */ -static smartlist_t *all_channels = NULL; - -/* All channel_t instances not in ERROR or CLOSED states */ -static smartlist_t *active_channels = NULL; - -/* All channel_t instances in ERROR or CLOSED states */ -static smartlist_t *finished_channels = NULL; - -/* All channel_listener_t instances */ -static smartlist_t *all_listeners = NULL; - -/* All channel_listener_t instances in LISTENING state */ -static smartlist_t *active_listeners = NULL; - -/* All channel_listener_t instances in LISTENING state */ -static smartlist_t *finished_listeners = NULL; - -/* Counter for ID numbers */ -static uint64_t n_channels_allocated = 0; - -/* Digest->channel map - * - * Similar to the one used in connection_or.c, this maps from the identity - * digest of a remote endpoint to a channel_t to that endpoint. Channels - * should be placed here when registered and removed when they close or error. - * If more than one channel exists, follow the next_with_same_id pointer - * as a linked list. - */ -HT_HEAD(channel_idmap, channel_idmap_entry_s) channel_identity_map = - HT_INITIALIZER(); - -typedef struct channel_idmap_entry_s { - HT_ENTRY(channel_idmap_entry_s) node; - uint8_t digest[DIGEST_LEN]; - TOR_LIST_HEAD(channel_list_s, channel_s) channel_list; -} channel_idmap_entry_t; - -static INLINE unsigned -channel_idmap_hash(const channel_idmap_entry_t *ent) -{ - const unsigned *a = (const unsigned *)ent->digest; -#if SIZEOF_INT == 4 - return a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4]; -#elif SIZEOF_INT == 8 - return a[0] ^ a[1]; -#endif -} - -static INLINE int -channel_idmap_eq(const channel_idmap_entry_t *a, - const channel_idmap_entry_t *b) -{ - return tor_memeq(a->digest, b->digest, DIGEST_LEN); -} - -HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, - channel_idmap_eq); -HT_GENERATE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, - channel_idmap_eq, 0.5, tor_malloc, tor_realloc, tor_free_); - -static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q); -static void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); -static int cell_queue_entry_is_padding(cell_queue_entry_t *q); -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell); -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell); -static int is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out); - -/* Functions to maintain the digest map */ -static void channel_add_to_digest_map(channel_t *chan); -static void channel_remove_from_digest_map(channel_t *chan); - -/* - * Flush cells from just the outgoing queue without trying to get them - * from circuits; used internall by channel_flush_some_cells(). - */ -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells); -static void channel_force_free(channel_t *chan); -static void -channel_free_list(smartlist_t *channels, int mark_for_close); -static void -channel_listener_free_list(smartlist_t *channels, int mark_for_close); -static void channel_listener_force_free(channel_listener_t *chan_l); -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); - -/*********************************** - * Channel state utility functions * - **********************************/ - -/** - * Indicate whether a given channel state is valid - */ - -int -channel_state_is_valid(channel_state_t state) -{ - int is_valid; - - switch (state) { - case CHANNEL_STATE_CLOSED: - case CHANNEL_STATE_CLOSING: - case CHANNEL_STATE_ERROR: - case CHANNEL_STATE_MAINT: - case CHANNEL_STATE_OPENING: - case CHANNEL_STATE_OPEN: - is_valid = 1; - break; - case CHANNEL_STATE_LAST: - default: - is_valid = 0; - } - - return is_valid; -} - -/** - * Indicate whether a given channel listener state is valid - */ - -int -channel_listener_state_is_valid(channel_listener_state_t state) -{ - int is_valid; - - switch (state) { - case CHANNEL_LISTENER_STATE_CLOSED: - case CHANNEL_LISTENER_STATE_LISTENING: - case CHANNEL_LISTENER_STATE_CLOSING: - case CHANNEL_LISTENER_STATE_ERROR: - is_valid = 1; - break; - case CHANNEL_LISTENER_STATE_LAST: - default: - is_valid = 0; - } - - return is_valid; -} - -/** - * Indicate whether a channel state transition is valid - * - * This function takes two channel states and indicates whether a - * transition between them is permitted (see the state definitions and - * transition table in or.h at the channel_state_t typedef). - */ - -int -channel_state_can_transition(channel_state_t from, channel_state_t to) -{ - int is_valid; - - switch (from) { - case CHANNEL_STATE_CLOSED: - is_valid = (to == CHANNEL_STATE_OPENING); - break; - case CHANNEL_STATE_CLOSING: - is_valid = (to == CHANNEL_STATE_CLOSED || - to == CHANNEL_STATE_ERROR); - break; - case CHANNEL_STATE_ERROR: - is_valid = 0; - break; - case CHANNEL_STATE_MAINT: - is_valid = (to == CHANNEL_STATE_CLOSING || - to == CHANNEL_STATE_ERROR || - to == CHANNEL_STATE_OPEN); - break; - case CHANNEL_STATE_OPENING: - is_valid = (to == CHANNEL_STATE_CLOSING || - to == CHANNEL_STATE_ERROR || - to == CHANNEL_STATE_OPEN); - break; - case CHANNEL_STATE_OPEN: - is_valid = (to == CHANNEL_STATE_CLOSING || - to == CHANNEL_STATE_ERROR || - to == CHANNEL_STATE_MAINT); - break; - case CHANNEL_STATE_LAST: - default: - is_valid = 0; - } - - return is_valid; -} - -/** - * Indicate whether a channel listener state transition is valid - * - * This function takes two channel listener states and indicates whether a - * transition between them is permitted (see the state definitions and - * transition table in or.h at the channel_listener_state_t typedef). - */ - -int -channel_listener_state_can_transition(channel_listener_state_t from, - channel_listener_state_t to) -{ - int is_valid; - - switch (from) { - case CHANNEL_LISTENER_STATE_CLOSED: - is_valid = (to == CHANNEL_LISTENER_STATE_LISTENING); - break; - case CHANNEL_LISTENER_STATE_CLOSING: - is_valid = (to == CHANNEL_LISTENER_STATE_CLOSED || - to == CHANNEL_LISTENER_STATE_ERROR); - break; - case CHANNEL_LISTENER_STATE_ERROR: - is_valid = 0; - break; - case CHANNEL_LISTENER_STATE_LISTENING: - is_valid = (to == CHANNEL_LISTENER_STATE_CLOSING || - to == CHANNEL_LISTENER_STATE_ERROR); - break; - case CHANNEL_LISTENER_STATE_LAST: - default: - is_valid = 0; - } - - return is_valid; -} - -/** - * Return a human-readable description for a channel state - */ - -const char * -channel_state_to_string(channel_state_t state) -{ - const char *descr; - - switch (state) { - case CHANNEL_STATE_CLOSED: - descr = "closed"; - break; - case CHANNEL_STATE_CLOSING: - descr = "closing"; - break; - case CHANNEL_STATE_ERROR: - descr = "channel error"; - break; - case CHANNEL_STATE_MAINT: - descr = "temporarily suspended for maintenance"; - break; - case CHANNEL_STATE_OPENING: - descr = "opening"; - break; - case CHANNEL_STATE_OPEN: - descr = "open"; - break; - case CHANNEL_STATE_LAST: - default: - descr = "unknown or invalid channel state"; - } - - return descr; -} - -/** - * Return a human-readable description for a channel listenier state - */ - -const char * -channel_listener_state_to_string(channel_listener_state_t state) -{ - const char *descr; - - switch (state) { - case CHANNEL_LISTENER_STATE_CLOSED: - descr = "closed"; - break; - case CHANNEL_LISTENER_STATE_CLOSING: - descr = "closing"; - break; - case CHANNEL_LISTENER_STATE_ERROR: - descr = "channel listener error"; - break; - case CHANNEL_LISTENER_STATE_LISTENING: - descr = "listening"; - break; - case CHANNEL_LISTENER_STATE_LAST: - default: - descr = "unknown or invalid channel listener state"; - } - - return descr; -} - -/*************************************** - * Channel registration/unregistration * - ***************************************/ - -/** - * Register a channel - * - * This function registers a newly created channel in the global lists/maps - * of active channels. - */ - -void -channel_register(channel_t *chan) -{ - tor_assert(chan); - - /* No-op if already registered */ - if (chan->registered) return; - - log_debug(LD_CHANNEL, - "Registering channel %p (ID " U64_FORMAT ") " - "in state %s (%d) with digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state, - hex_str(chan->identity_digest, DIGEST_LEN)); - - /* Make sure we have all_channels, then add it */ - if (!all_channels) all_channels = smartlist_new(); - smartlist_add(all_channels, chan); - - /* Is it finished? */ - if (chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) { - /* Put it in the finished list, creating it if necessary */ - if (!finished_channels) finished_channels = smartlist_new(); - smartlist_add(finished_channels, chan); - } else { - /* Put it in the active list, creating it if necessary */ - if (!active_channels) active_channels = smartlist_new(); - smartlist_add(active_channels, chan); - - if (chan->state != CHANNEL_STATE_CLOSING) { - /* It should have a digest set */ - if (!tor_digest_is_zero(chan->identity_digest)) { - /* Yeah, we're good, add it to the map */ - channel_add_to_digest_map(chan); - } else { - log_info(LD_CHANNEL, - "Channel %p (global ID " U64_FORMAT ") " - "in state %s (%d) registered with no identity digest", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state); - } - } - } - - /* Mark it as registered */ - chan->registered = 1; -} - -/** - * Unregister a channel - * - * This function removes a channel from the global lists and maps and is used - * when freeing a closed/errored channel. - */ - -void -channel_unregister(channel_t *chan) -{ - tor_assert(chan); - - /* No-op if not registered */ - if (!(chan->registered)) return; - - /* Is it finished? */ - if (chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) { - /* Get it out of the finished list */ - if (finished_channels) smartlist_remove(finished_channels, chan); - } else { - /* Get it out of the active list */ - if (active_channels) smartlist_remove(active_channels, chan); - } - - /* Get it out of all_channels */ - if (all_channels) smartlist_remove(all_channels, chan); - - /* Mark it as unregistered */ - chan->registered = 0; - - /* Should it be in the digest map? */ - if (!tor_digest_is_zero(chan->identity_digest) && - !(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - /* Remove it */ - channel_remove_from_digest_map(chan); - } -} - -/** - * Register a channel listener - * - * This function registers a newly created channel listner in the global - * lists/maps of active channel listeners. - */ - -void -channel_listener_register(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - /* No-op if already registered */ - if (chan_l->registered) return; - - log_debug(LD_CHANNEL, - "Registering channel listener %p (ID " U64_FORMAT ") " - "in state %s (%d)", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier), - channel_listener_state_to_string(chan_l->state), - chan_l->state); - - /* Make sure we have all_channels, then add it */ - if (!all_listeners) all_listeners = smartlist_new(); - smartlist_add(all_listeners, chan_l); - - /* Is it finished? */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) { - /* Put it in the finished list, creating it if necessary */ - if (!finished_listeners) finished_listeners = smartlist_new(); - smartlist_add(finished_listeners, chan_l); - } else { - /* Put it in the active list, creating it if necessary */ - if (!active_listeners) active_listeners = smartlist_new(); - smartlist_add(active_listeners, chan_l); - } - - /* Mark it as registered */ - chan_l->registered = 1; -} - -/** - * Unregister a channel listener - * - * This function removes a channel listener from the global lists and maps - * and is used when freeing a closed/errored channel listener. - */ - -void -channel_listener_unregister(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - /* No-op if not registered */ - if (!(chan_l->registered)) return; - - /* Is it finished? */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) { - /* Get it out of the finished list */ - if (finished_listeners) smartlist_remove(finished_listeners, chan_l); - } else { - /* Get it out of the active list */ - if (active_listeners) smartlist_remove(active_listeners, chan_l); - } - - /* Get it out of all_channels */ - if (all_listeners) smartlist_remove(all_listeners, chan_l); - - /* Mark it as unregistered */ - chan_l->registered = 0; -} - -/********************************* - * Channel digest map maintenance - *********************************/ - -/** - * Add a channel to the digest map - * - * This function adds a channel to the digest map and inserts it into the - * correct linked list if channels with that remote endpoint identity digest - * already exist. - */ - -static void -channel_add_to_digest_map(channel_t *chan) -{ - channel_idmap_entry_t *ent, search; - - tor_assert(chan); - - /* Assert that the state makes sense */ - tor_assert(!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)); - - /* Assert that there is a digest */ - tor_assert(!tor_digest_is_zero(chan->identity_digest)); - - memcpy(search.digest, chan->identity_digest, DIGEST_LEN); - ent = HT_FIND(channel_idmap, &channel_identity_map, &search); - if (! ent) { - ent = tor_malloc(sizeof(channel_idmap_entry_t)); - memcpy(ent->digest, chan->identity_digest, DIGEST_LEN); - TOR_LIST_INIT(&ent->channel_list); - HT_INSERT(channel_idmap, &channel_identity_map, ent); - } - TOR_LIST_INSERT_HEAD(&ent->channel_list, chan, next_with_same_id); - - log_debug(LD_CHANNEL, - "Added channel %p (global ID " U64_FORMAT ") " - "to identity map in state %s (%d) with digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state, - hex_str(chan->identity_digest, DIGEST_LEN)); -} - -/** - * Remove a channel from the digest map - * - * This function removes a channel from the digest map and the linked list of - * channels for that digest if more than one exists. - */ - -static void -channel_remove_from_digest_map(channel_t *chan) -{ - channel_idmap_entry_t *ent, search; - - tor_assert(chan); - - /* Assert that there is a digest */ - tor_assert(!tor_digest_is_zero(chan->identity_digest)); - -#if 0 - /* Make sure we have a map */ - if (!channel_identity_map) { - /* - * No identity map, so we can't find it by definition. This - * case is similar to digestmap_get() failing below. - */ - log_warn(LD_BUG, - "Trying to remove channel %p (global ID " U64_FORMAT ") " - "with digest %s from identity map, but didn't have any identity " - "map", - chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - /* Clear out its next/prev pointers */ - if (chan->next_with_same_id) { - chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; - } - if (chan->prev_with_same_id) { - chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; - } - chan->next_with_same_id = NULL; - chan->prev_with_same_id = NULL; - - return; - } -#endif - - /* Pull it out of its list, wherever that list is */ - TOR_LIST_REMOVE(chan, next_with_same_id); - - memcpy(search.digest, chan->identity_digest, DIGEST_LEN); - ent = HT_FIND(channel_idmap, &channel_identity_map, &search); - - /* Look for it in the map */ - if (ent) { - /* Okay, it's here */ - - if (TOR_LIST_EMPTY(&ent->channel_list)) { - HT_REMOVE(channel_idmap, &channel_identity_map, ent); - tor_free(ent); - } - - log_debug(LD_CHANNEL, - "Removed channel %p (global ID " U64_FORMAT ") from " - "identity map in state %s (%d) with digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state, - hex_str(chan->identity_digest, DIGEST_LEN)); - } else { - /* Shouldn't happen */ - log_warn(LD_BUG, - "Trying to remove channel %p (global ID " U64_FORMAT ") with " - "digest %s from identity map, but couldn't find any with " - "that digest", - chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - } -} - -/**************************** - * Channel lookup functions * - ***************************/ - -/** - * Find channel by global ID - * - * This function searches for a channel by the global_identifier assigned - * at initialization time. This identifier is unique for the lifetime of the - * Tor process. - */ - -channel_t * -channel_find_by_global_id(uint64_t global_identifier) -{ - channel_t *rv = NULL; - - if (all_channels && smartlist_len(all_channels) > 0) { - SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { - if (curr->global_identifier == global_identifier) { - rv = curr; - break; - } - } SMARTLIST_FOREACH_END(curr); - } - - return rv; -} - -/** - * Find channel by digest of the remote endpoint - * - * This function looks up a channel by the digest of its remote endpoint in - * the channel digest map. It's possible that more than one channel to a - * given endpoint exists. Use channel_next_with_digest() to walk the list. - */ - -channel_t * -channel_find_by_remote_digest(const char *identity_digest) -{ - channel_t *rv = NULL; - channel_idmap_entry_t *ent, search; - - tor_assert(identity_digest); - - memcpy(search.digest, identity_digest, DIGEST_LEN); - ent = HT_FIND(channel_idmap, &channel_identity_map, &search); - if (ent) { - rv = TOR_LIST_FIRST(&ent->channel_list); - } - - return rv; -} - -/** - * Get next channel with digest - * - * This function takes a channel and finds the next channel in the list - * with the same digest. - */ - -channel_t * -channel_next_with_digest(channel_t *chan) -{ - tor_assert(chan); - - return TOR_LIST_NEXT(chan, next_with_same_id); -} - -/** - * Initialize a channel - * - * This function should be called by subclasses to set up some per-channel - * variables. I.e., this is the superclass constructor. Before this, the - * channel should be allocated with tor_malloc_zero(). - */ - -void -channel_init(channel_t *chan) -{ - tor_assert(chan); - - /* Assign an ID and bump the counter */ - chan->global_identifier = n_channels_allocated++; - - /* Init timestamp */ - chan->timestamp_last_added_nonpadding = time(NULL); - - /* Init next_circ_id */ - chan->next_circ_id = crypto_rand_int(1 << 15); - - /* Initialize queues. */ - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - - /* Initialize list entries. */ - memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id)); - - /* Timestamp it */ - channel_timestamp_created(chan); - - /* It hasn't been open yet. */ - chan->has_been_open = 0; -} - -/** - * Initialize a channel listener - * - * This function should be called by subclasses to set up some per-channel - * variables. I.e., this is the superclass constructor. Before this, the - * channel listener should be allocated with tor_malloc_zero(). - */ - -void -channel_init_listener(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - /* Assign an ID and bump the counter */ - chan_l->global_identifier = n_channels_allocated++; - - /* Timestamp it */ - channel_listener_timestamp_created(chan_l); -} - -/** - * Free a channel; nothing outside of channel.c and subclasses should call - * this - it frees channels after they have closed and been unregistered. - */ - -void -channel_free(channel_t *chan) -{ - if (!chan) return; - - /* It must be closed or errored */ - tor_assert(chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - /* It must be deregistered */ - tor_assert(!(chan->registered)); - - log_debug(LD_CHANNEL, - "Freeing channel " U64_FORMAT " at %p", - U64_PRINTF_ARG(chan->global_identifier), chan); - - /* - * Get rid of cmux policy before we do anything, so cmux policies don't - * see channels in weird half-freed states. - */ - if (chan->cmux) { - circuitmux_set_policy(chan->cmux, NULL); - } - - /* Call a free method if there is one */ - if (chan->free) chan->free(chan); - - channel_clear_remote_end(chan); - - /* Get rid of cmux */ - if (chan->cmux) { - circuitmux_detach_all_circuits(chan->cmux); - circuitmux_mark_destroyed_circids_usable(chan->cmux, chan); - circuitmux_free(chan->cmux); - chan->cmux = NULL; - } - - /* We're in CLOSED or ERROR, so the cell queue is already empty */ - - tor_free(chan); -} - -/** - * Free a channel listener; nothing outside of channel.c and subclasses - * should call this - it frees channel listeners after they have closed and - * been unregistered. - */ - -void -channel_listener_free(channel_listener_t *chan_l) -{ - if (!chan_l) return; - - log_debug(LD_CHANNEL, - "Freeing channel_listener_t " U64_FORMAT " at %p", - U64_PRINTF_ARG(chan_l->global_identifier), - chan_l); - - /* It must be closed or errored */ - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR); - /* It must be deregistered */ - tor_assert(!(chan_l->registered)); - - /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); - - /* - * We're in CLOSED or ERROR, so the incoming channel queue is already - * empty. - */ - - tor_free(chan_l); -} - -/** - * Free a channel and skip the state/registration asserts; this internal- - * use-only function should be called only from channel_free_all() when - * shutting down the Tor process. - */ - -static void -channel_force_free(channel_t *chan) -{ - cell_queue_entry_t *cell, *cell_tmp; - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Force-freeing channel " U64_FORMAT " at %p", - U64_PRINTF_ARG(chan->global_identifier), chan); - - /* - * Get rid of cmux policy before we do anything, so cmux policies don't - * see channels in weird half-freed states. - */ - if (chan->cmux) { - circuitmux_set_policy(chan->cmux, NULL); - } - - /* Call a free method if there is one */ - if (chan->free) chan->free(chan); - - channel_clear_remote_end(chan); - - /* Get rid of cmux */ - if (chan->cmux) { - circuitmux_free(chan->cmux); - chan->cmux = NULL; - } - - /* We might still have a cell queue; kill it */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - - /* Outgoing cell queue is similar, but we can have to free packed cells */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - - tor_free(chan); -} - -/** - * Free a channel listener and skip the state/reigstration asserts; this - * internal-use-only function should be called only from channel_free_all() - * when shutting down the Tor process. - */ - -static void -channel_listener_force_free(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - log_debug(LD_CHANNEL, - "Force-freeing channel_listener_t " U64_FORMAT " at %p", - U64_PRINTF_ARG(chan_l->global_identifier), - chan_l); - - /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); - - /* - * The incoming list just gets emptied and freed; we request close on - * any channels we find there, but since we got called while shutting - * down they will get deregistered and freed elsewhere anyway. - */ - if (chan_l->incoming_list) { - SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list, - channel_t *, qchan) { - channel_mark_for_close(qchan); - } SMARTLIST_FOREACH_END(qchan); - - smartlist_free(chan_l->incoming_list); - chan_l->incoming_list = NULL; - } - - tor_free(chan_l); -} - -/** - * Return the current registered listener for a channel listener - * - * This function returns a function pointer to the current registered - * handler for new incoming channels on a channel listener. - */ - -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING) - return chan_l->listener; - - return NULL; -} - -/** - * Set the listener for a channel listener - * - * This function sets the handler for new incoming channels on a channel - * listener. - */ - -void -channel_listener_set_listener_fn(channel_listener_t *chan_l, - channel_listener_fn_ptr listener) -{ - tor_assert(chan_l); - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_LISTENING); - - log_debug(LD_CHANNEL, - "Setting listener callback for channel listener %p " - "(global ID " U64_FORMAT ") to %p", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier), - listener); - - chan_l->listener = listener; - if (chan_l->listener) channel_listener_process_incoming(chan_l); -} - -/** - * Return the fixed-length cell handler for a channel - * - * This function gets the handler for incoming fixed-length cells installed - * on a channel. - */ - -channel_cell_handler_fn_ptr -channel_get_cell_handler(channel_t *chan) -{ - tor_assert(chan); - - if (chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) - return chan->cell_handler; - - return NULL; -} - -/** - * Return the variable-length cell handler for a channel - * - * This function gets the handler for incoming variable-length cells - * installed on a channel. - */ - -channel_var_cell_handler_fn_ptr -channel_get_var_cell_handler(channel_t *chan) -{ - tor_assert(chan); - - if (chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) - return chan->var_cell_handler; - - return NULL; -} - -/** - * Set both cell handlers for a channel - * - * This function sets both the fixed-length and variable length cell handlers - * for a channel and processes any incoming cells that had been blocked in the - * queue because none were available. - */ - -void -channel_set_cell_handlers(channel_t *chan, - channel_cell_handler_fn_ptr cell_handler, - channel_var_cell_handler_fn_ptr - var_cell_handler) -{ - int try_again = 0; - - tor_assert(chan); - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); - - log_debug(LD_CHANNEL, - "Setting cell_handler callback for channel %p to %p", - chan, cell_handler); - log_debug(LD_CHANNEL, - "Setting var_cell_handler callback for channel %p to %p", - chan, var_cell_handler); - - /* Should we try the queue? */ - if (cell_handler && - cell_handler != chan->cell_handler) try_again = 1; - if (var_cell_handler && - var_cell_handler != chan->var_cell_handler) try_again = 1; - - /* Change them */ - chan->cell_handler = cell_handler; - chan->var_cell_handler = var_cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) && - try_again && - (chan->cell_handler || - chan->var_cell_handler)) channel_process_cells(chan); -} - -/* - * On closing channels - * - * There are three functions that close channels, for use in - * different circumstances: - * - * - Use channel_mark_for_close() for most cases - * - Use channel_close_from_lower_layer() if you are connection_or.c - * and the other end closes the underlying connection. - * - Use channel_close_for_error() if you are connection_or.c and - * some sort of error has occurred. - */ - -/** - * Mark a channel for closure - * - * This function tries to close a channel_t; it will go into the CLOSING - * state, and eventually the lower layer should put it into the CLOSED or - * ERROR state. Then, channel_run_cleanup() will eventually free it. - */ - -void -channel_mark_for_close(channel_t *chan) -{ - tor_assert(chan != NULL); - tor_assert(chan->close != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel %p (global ID " U64_FORMAT ") " - "by request", - chan, U64_PRINTF_ARG(chan->global_identifier)); - - /* Note closing by request from above */ - chan->reason_for_closing = CHANNEL_CLOSE_REQUESTED; - - /* Change state to CLOSING */ - channel_change_state(chan, CHANNEL_STATE_CLOSING); - - /* Tell the lower layer */ - chan->close(chan); - - /* - * It's up to the lower layer to change state to CLOSED or ERROR when we're - * ready; we'll try to free channels that are in the finished list from - * channel_run_cleanup(). The lower layer should do this by calling - * channel_closed(). - */ -} - -/** - * Mark a channel listener for closure - * - * This function tries to close a channel_listener_t; it will go into the - * CLOSING state, and eventually the lower layer should put it into the CLOSED - * or ERROR state. Then, channel_run_cleanup() will eventually free it. - */ - -void -channel_listener_mark_for_close(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - tor_assert(chan_l->close != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "by request", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by request from above */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_REQUESTED; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); - - /* Tell the lower layer */ - chan_l->close(chan_l); - - /* - * It's up to the lower layer to change state to CLOSED or ERROR when we're - * ready; we'll try to free channels that are in the finished list from - * channel_run_cleanup(). The lower layer should do this by calling - * channel_listener_closed(). - */ -} - -/** - * Close a channel from the lower layer - * - * Notify the channel code that the channel is being closed due to a non-error - * condition in the lower layer. This does not call the close() method, since - * the lower layer already knows. - */ - -void -channel_close_from_lower_layer(channel_t *chan) -{ - tor_assert(chan != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel %p (global ID " U64_FORMAT ") " - "due to lower-layer event", - chan, U64_PRINTF_ARG(chan->global_identifier)); - - /* Note closing by event from below */ - chan->reason_for_closing = CHANNEL_CLOSE_FROM_BELOW; - - /* Change state to CLOSING */ - channel_change_state(chan, CHANNEL_STATE_CLOSING); -} - -/** - * Close a channel listener from the lower layer - * - * Notify the channel code that the channel listener is being closed due to a - * non-error condition in the lower layer. This does not call the close() - * method, since the lower layer already knows. - */ - -void -channel_listener_close_from_lower_layer(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer event", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** - * Notify that the channel is being closed due to an error condition - * - * This function is called by the lower layer implementing the transport - * when a channel must be closed due to an error condition. This does not - * call the channel's close method, since the lower layer already knows. - */ - -void -channel_close_for_error(channel_t *chan) -{ - tor_assert(chan != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel %p due to lower-layer error", - chan); - - /* Note closing by event from below */ - chan->reason_for_closing = CHANNEL_CLOSE_FOR_ERROR; - - /* Change state to CLOSING */ - channel_change_state(chan, CHANNEL_STATE_CLOSING); -} - -/** - * Notify that the channel listener is being closed due to an error condition - * - * This function is called by the lower layer implementing the transport - * when a channel listener must be closed due to an error condition. This - * does not call the channel listener's close method, since the lower layer - * already knows. - */ - -void -channel_listener_close_for_error(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer error", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** - * Notify that the lower layer is finished closing the channel - * - * This function should be called by the lower layer when a channel - * is finished closing and it should be regarded as inactive and - * freed by the channel code. - */ - -void -channel_closed(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - - /* No-op if already inactive */ - if (chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) return; - - /* Inform any pending (not attached) circs that they should - * give up. */ - if (! chan->has_been_open) - circuit_n_chan_done(chan, 0); - - /* Now close all the attached circuits on it. */ - circuit_unlink_all_from_channel(chan, END_CIRC_REASON_CHANNEL_CLOSED); - - if (chan->reason_for_closing != CHANNEL_CLOSE_FOR_ERROR) { - channel_change_state(chan, CHANNEL_STATE_CLOSED); - } else { - channel_change_state(chan, CHANNEL_STATE_ERROR); - } -} - -/** - * Notify that the lower layer is finished closing the channel listener - * - * This function should be called by the lower layer when a channel listener - * is finished closing and it should be regarded as inactive and - * freed by the channel code. - */ - -void -channel_listener_closed(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR); - - /* No-op if already inactive */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); - } else { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR); - } -} - -/** - * Clear the identity_digest of a channel - * - * This function clears the identity digest of the remote endpoint for a - * channel; this is intended for use by the lower layer. - */ - -void -channel_clear_identity_digest(channel_t *chan) -{ - int state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Clearing remote endpoint digest on channel %p with " - "global ID " U64_FORMAT, - chan, U64_PRINTF_ARG(chan->global_identifier)); - - state_not_in_map = - (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - - if (!state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->identity_digest)) - /* if it's registered get it out of the digest map */ - channel_remove_from_digest_map(chan); - - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); -} - -/** - * Set the identity_digest of a channel - * - * This function sets the identity digest of the remote endpoint for a - * channel; this is intended for use by the lower layer. - */ - -void -channel_set_identity_digest(channel_t *chan, - const char *identity_digest) -{ - int was_in_digest_map, should_be_in_digest_map, state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Setting remote endpoint digest on channel %p with " - "global ID " U64_FORMAT " to digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - identity_digest ? - hex_str(identity_digest, DIGEST_LEN) : "(null)"); - - state_not_in_map = - (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - was_in_digest_map = - !state_not_in_map && - chan->registered && - !tor_digest_is_zero(chan->identity_digest); - should_be_in_digest_map = - !state_not_in_map && - chan->registered && - (identity_digest && - !tor_digest_is_zero(identity_digest)); - - if (was_in_digest_map) - /* We should always remove it; we'll add it back if we're writing - * in a new digest. - */ - channel_remove_from_digest_map(chan); - - if (identity_digest) { - memcpy(chan->identity_digest, - identity_digest, - sizeof(chan->identity_digest)); - } else { - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - } - - /* Put it in the digest map if we should */ - if (should_be_in_digest_map) - channel_add_to_digest_map(chan); -} - -/** - * Clear the remote end metadata (identity_digest/nickname) of a channel - * - * This function clears all the remote end info from a channel; this is - * intended for use by the lower layer. - */ - -void -channel_clear_remote_end(channel_t *chan) -{ - int state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Clearing remote endpoint identity on channel %p with " - "global ID " U64_FORMAT, - chan, U64_PRINTF_ARG(chan->global_identifier)); - - state_not_in_map = - (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - - if (!state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->identity_digest)) - /* if it's registered get it out of the digest map */ - channel_remove_from_digest_map(chan); - - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - tor_free(chan->nickname); -} - -/** - * Set the remote end metadata (identity_digest/nickname) of a channel - * - * This function sets new remote end info on a channel; this is intended - * for use by the lower layer. - */ - -void -channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname) -{ - int was_in_digest_map, should_be_in_digest_map, state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Setting remote endpoint identity on channel %p with " - "global ID " U64_FORMAT " to nickname %s, digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - nickname ? nickname : "(null)", - identity_digest ? - hex_str(identity_digest, DIGEST_LEN) : "(null)"); - - state_not_in_map = - (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR); - was_in_digest_map = - !state_not_in_map && - chan->registered && - !tor_digest_is_zero(chan->identity_digest); - should_be_in_digest_map = - !state_not_in_map && - chan->registered && - (identity_digest && - !tor_digest_is_zero(identity_digest)); - - if (was_in_digest_map) - /* We should always remove it; we'll add it back if we're writing - * in a new digest. - */ - channel_remove_from_digest_map(chan); - - if (identity_digest) { - memcpy(chan->identity_digest, - identity_digest, - sizeof(chan->identity_digest)); - - } else { - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - } - - tor_free(chan->nickname); - if (nickname) - chan->nickname = tor_strdup(nickname); - - /* Put it in the digest map if we should */ - if (should_be_in_digest_map) - channel_add_to_digest_map(chan); -} - -/** - * Duplicate a cell queue entry; this is a shallow copy intended for use - * in channel_write_cell_queue_entry(). - */ - -static cell_queue_entry_t * -cell_queue_entry_dup(cell_queue_entry_t *q) -{ - cell_queue_entry_t *rv = NULL; - - tor_assert(q); - - rv = tor_malloc(sizeof(*rv)); - memcpy(rv, q, sizeof(*rv)); - - return rv; -} - -/** - * Free a cell_queue_entry_t; the handed_off parameter indicates whether - * the contents were passed to the lower layer (it is responsible for - * them) or not (we should free). - */ - -static void -cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) -{ - if (!q) return; - - if (!handed_off) { - /* - * If we handed it off, the recipient becomes responsible (or - * with packed cells the channel_t subclass calls packed_cell - * free after writing out its contents; see, e.g., - * channel_tls_write_packed_cell_method(). Otherwise, we have - * to take care of it here if possible. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - /* - * There doesn't seem to be a cell_free() function anywhere in the - * pre-channel code; just use tor_free() - */ - tor_free(q->u.fixed.cell); - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - /* - * This one's in connection_or.c; it'd be nice to figure out the - * whole flow of cells from one end to the other and factor the - * cell memory management functions like this out of the specific - * TLS lower layer. - */ - var_cell_free(q->u.var.var_cell); - } - break; - default: - /* - * Nothing we can do if we don't know the type; this will - * have been warned about elsewhere. - */ - break; - } - } - tor_free(q); -} - -/** - * Check whether a cell queue entry is padding; this is a helper function - * for channel_write_cell_queue_entry() - */ - -static int -cell_queue_entry_is_padding(cell_queue_entry_t *q) -{ - tor_assert(q); - - if (q->type == CELL_QUEUE_FIXED) { - if (q->u.fixed.cell) { - if (q->u.fixed.cell->command == CELL_PADDING || - q->u.fixed.cell->command == CELL_VPADDING) { - return 1; - } - } - } else if (q->type == CELL_QUEUE_VAR) { - if (q->u.var.var_cell) { - if (q->u.var.var_cell->command == CELL_PADDING || - q->u.var.var_cell->command == CELL_VPADDING) { - return 1; - } - } - } - - return 0; -} - -/** - * Allocate a new cell queue entry for a fixed-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; - - return q; -} - -/** - * Allocate a new cell queue entry for a variable-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(var_cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; - - return q; -} - -/** - * Write to a channel based on a cell_queue_entry_t - * - * Given a cell_queue_entry_t filled out by the caller, try to send the cell - * and queue it if we can't. - */ - -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) -{ - int result = 0, sent = 0; - cell_queue_entry_t *tmp = NULL; - - tor_assert(chan); - tor_assert(q); - - /* Assert that the state makes sense for a cell write */ - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); - - /* Increment the timestamp unless it's padding */ - if (!cell_queue_entry_is_padding(q)) { - chan->timestamp_last_added_nonpadding = approx_time(); - } - - { - circid_t circ_id; - if (is_destroy_cell(chan, q, &circ_id)) { - channel_note_destroy_not_pending(chan, circ_id); - } - } - - /* Can we send it right out? If so, try */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) && - chan->state == CHANNEL_STATE_OPEN) { - /* Pick the right write function for this cell type and save the result */ - switch (q->type) { - case CELL_QUEUE_FIXED: - tor_assert(chan->write_cell); - tor_assert(q->u.fixed.cell); - result = chan->write_cell(chan, q->u.fixed.cell); - break; - case CELL_QUEUE_PACKED: - tor_assert(chan->write_packed_cell); - tor_assert(q->u.packed.packed_cell); - result = chan->write_packed_cell(chan, q->u.packed.packed_cell); - break; - case CELL_QUEUE_VAR: - tor_assert(chan->write_var_cell); - tor_assert(q->u.var.var_cell); - result = chan->write_var_cell(chan, q->u.var.var_cell); - break; - default: - tor_assert(1); - } - - /* Check if we got it out */ - if (result > 0) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->n_cells_xmitted); - } - } - - if (!sent) { - /* Not sent, queue it */ - /* - * We have to copy the queue entry passed in, since the caller probably - * used the stack. - */ - tmp = cell_queue_entry_dup(q); - TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next); - /* Try to process the queue? */ - if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); - } -} - -/** - * Write a cell to a channel - * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); - * it is called by the transport-independent code to deliver a cell to a - * channel for transmission. - */ - -void -channel_write_cell(channel_t *chan, cell_t *cell) -{ - cell_queue_entry_t q; - - tor_assert(chan); - tor_assert(cell); - - if (chan->state == CHANNEL_STATE_CLOSING) { - log_debug(LD_CHANNEL, "Discarding cell_t %p on closing channel %p with " - "global ID "U64_FORMAT, cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - tor_free(cell); - return; - } - - log_debug(LD_CHANNEL, - "Writing cell_t %p to channel %p with global ID " - U64_FORMAT, - cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - - q.type = CELL_QUEUE_FIXED; - q.u.fixed.cell = cell; - channel_write_cell_queue_entry(chan, &q); -} - -/** - * Write a packed cell to a channel - * - * Write a packed cell to a channel using the write_cell() method. This is - * called by the transport-independent code to deliver a packed cell to a - * channel for transmission. - */ - -void -channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) -{ - cell_queue_entry_t q; - - tor_assert(chan); - tor_assert(packed_cell); - - if (chan->state == CHANNEL_STATE_CLOSING) { - log_debug(LD_CHANNEL, "Discarding packed_cell_t %p on closing channel %p " - "with global ID "U64_FORMAT, packed_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - packed_cell_free(packed_cell); - return; - } - - log_debug(LD_CHANNEL, - "Writing packed_cell_t %p to channel %p with global ID " - U64_FORMAT, - packed_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - - q.type = CELL_QUEUE_PACKED; - q.u.packed.packed_cell = packed_cell; - channel_write_cell_queue_entry(chan, &q); -} - -/** - * Write a variable-length cell to a channel - * - * Write a variable-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels - * connection_or_write_var_cell_to_buf(); it's called by the transport- - * independent code to deliver a var_cell to a channel for transmission. - */ - -void -channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - cell_queue_entry_t q; - - tor_assert(chan); - tor_assert(var_cell); - - if (chan->state == CHANNEL_STATE_CLOSING) { - log_debug(LD_CHANNEL, "Discarding var_cell_t %p on closing channel %p " - "with global ID "U64_FORMAT, var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - var_cell_free(var_cell); - return; - } - - log_debug(LD_CHANNEL, - "Writing var_cell_t %p to channel %p with global ID " - U64_FORMAT, - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - - q.type = CELL_QUEUE_VAR; - q.u.var.var_cell = var_cell; - channel_write_cell_queue_entry(chan, &q); -} - -/** - * Change channel state - * - * This internal and subclass use only function is used to change channel - * state, performing all transition validity checks and whatever actions - * are appropriate to the state transition in question. - */ - -void -channel_change_state(channel_t *chan, channel_state_t to_state) -{ - channel_state_t from_state; - unsigned char was_active, is_active; - unsigned char was_in_id_map, is_in_id_map; - - tor_assert(chan); - from_state = chan->state; - - tor_assert(channel_state_is_valid(from_state)); - tor_assert(channel_state_is_valid(to_state)); - tor_assert(channel_state_can_transition(chan->state, to_state)); - - /* Check for no-op transitions */ - if (from_state == to_state) { - log_debug(LD_CHANNEL, - "Got no-op transition from \"%s\" to itself on channel %p" - "(global ID " U64_FORMAT ")", - channel_state_to_string(to_state), - chan, U64_PRINTF_ARG(chan->global_identifier)); - return; - } - - /* If we're going to a closing or closed state, we must have a reason set */ - if (to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) { - tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING); - } - - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - - log_debug(LD_CHANNEL, - "Changing state of channel %p (global ID " U64_FORMAT - ") from \"%s\" to \"%s\"", - chan, - U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), - channel_state_to_string(to_state)); - - chan->state = to_state; - - /* Need to add to the right lists if the channel is registered */ - if (chan->registered) { - was_active = !(from_state == CHANNEL_STATE_CLOSED || - from_state == CHANNEL_STATE_ERROR); - is_active = !(to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR); - - /* Need to take off active list and put on finished list? */ - if (was_active && !is_active) { - if (active_channels) smartlist_remove(active_channels, chan); - if (!finished_channels) finished_channels = smartlist_new(); - smartlist_add(finished_channels, chan); - } - /* Need to put on active list? */ - else if (!was_active && is_active) { - if (finished_channels) smartlist_remove(finished_channels, chan); - if (!active_channels) active_channels = smartlist_new(); - smartlist_add(active_channels, chan); - } - - if (!tor_digest_is_zero(chan->identity_digest)) { - /* Now we need to handle the identity map */ - was_in_id_map = !(from_state == CHANNEL_STATE_CLOSING || - from_state == CHANNEL_STATE_CLOSED || - from_state == CHANNEL_STATE_ERROR); - is_in_id_map = !(to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR); - - if (!was_in_id_map && is_in_id_map) channel_add_to_digest_map(chan); - else if (was_in_id_map && !is_in_id_map) - channel_remove_from_digest_map(chan); - } - } - - /* Tell circuits if we opened and stuff */ - if (to_state == CHANNEL_STATE_OPEN) { - channel_do_open_actions(chan); - chan->has_been_open = 1; - - /* Check for queued cells to process */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - channel_process_cells(chan); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) - channel_flush_cells(chan); - } else if (to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) { - /* Assert that all queues are empty */ - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)); - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)); - } -} - -/** - * Change channel listener state - * - * This internal and subclass use only function is used to change channel - * listener state, performing all transition validity checks and whatever - * actions are appropriate to the state transition in question. - */ - -void -channel_listener_change_state(channel_listener_t *chan_l, - channel_listener_state_t to_state) -{ - channel_listener_state_t from_state; - unsigned char was_active, is_active; - - tor_assert(chan_l); - from_state = chan_l->state; - - tor_assert(channel_listener_state_is_valid(from_state)); - tor_assert(channel_listener_state_is_valid(to_state)); - tor_assert(channel_listener_state_can_transition(chan_l->state, to_state)); - - /* Check for no-op transitions */ - if (from_state == to_state) { - log_debug(LD_CHANNEL, - "Got no-op transition from \"%s\" to itself on channel " - "listener %p (global ID " U64_FORMAT ")", - channel_listener_state_to_string(to_state), - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - return; - } - - /* If we're going to a closing or closed state, we must have a reason set */ - if (to_state == CHANNEL_LISTENER_STATE_CLOSING || - to_state == CHANNEL_LISTENER_STATE_CLOSED || - to_state == CHANNEL_LISTENER_STATE_ERROR) { - tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING); - } - - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - - log_debug(LD_CHANNEL, - "Changing state of channel listener %p (global ID " U64_FORMAT - "from \"%s\" to \"%s\"", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier), - channel_listener_state_to_string(chan_l->state), - channel_listener_state_to_string(to_state)); - - chan_l->state = to_state; - - /* Need to add to the right lists if the channel listener is registered */ - if (chan_l->registered) { - was_active = !(from_state == CHANNEL_LISTENER_STATE_CLOSED || - from_state == CHANNEL_LISTENER_STATE_ERROR); - is_active = !(to_state == CHANNEL_LISTENER_STATE_CLOSED || - to_state == CHANNEL_LISTENER_STATE_ERROR); - - /* Need to take off active list and put on finished list? */ - if (was_active && !is_active) { - if (active_listeners) smartlist_remove(active_listeners, chan_l); - if (!finished_listeners) finished_listeners = smartlist_new(); - smartlist_add(finished_listeners, chan_l); - } - /* Need to put on active list? */ - else if (!was_active && is_active) { - if (finished_listeners) smartlist_remove(finished_listeners, chan_l); - if (!active_listeners) active_listeners = smartlist_new(); - smartlist_add(active_listeners, chan_l); - } - } - - if (to_state == CHANNEL_LISTENER_STATE_CLOSED || - to_state == CHANNEL_LISTENER_STATE_ERROR) { - /* Assert that the queue is empty */ - tor_assert(!(chan_l->incoming_list) || - smartlist_len(chan_l->incoming_list) == 0); - } -} - -/** - * Try to flush cells to the lower layer - * - * this is called by the lower layer to indicate that it wants more cells; - * it will try to write up to num_cells cells from the channel's cell queue or - * from circuits active on that channel, or as many as it has available if - * num_cells == -1. - */ - -#define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 - -ssize_t -channel_flush_some_cells(channel_t *chan, ssize_t num_cells) -{ - unsigned int unlimited = 0; - ssize_t flushed = 0; - int num_cells_from_circs, clamped_num_cells; - - tor_assert(chan); - - if (num_cells < 0) unlimited = 1; - if (!unlimited && num_cells <= flushed) goto done; - - /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ - if (chan->state == CHANNEL_STATE_OPEN) { - /* Try to flush as much as we can that's already queued */ - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - if (!unlimited && num_cells <= flushed) goto done; - - if (circuitmux_num_cells(chan->cmux) > 0) { - /* Calculate number of cells, including clamp */ - if (unlimited) { - clamped_num_cells = MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED; - } else { - if (num_cells - flushed > - MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED) { - clamped_num_cells = MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED; - } else { - clamped_num_cells = (int)(num_cells - flushed); - } - } - /* Try to get more cells from any active circuits */ - num_cells_from_circs = channel_flush_from_first_active_circuit( - chan, clamped_num_cells); - - /* If it claims we got some, process the queue again */ - if (num_cells_from_circs > 0) { - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - } - } - } - - done: - return flushed; -} - -/** - * Flush cells from just the channel's outgoing cell queue - * - * This gets called from channel_flush_some_cells() above to flush cells - * just from the queue without trying for active_circuits. - */ - -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells) -{ - unsigned int unlimited = 0; - ssize_t flushed = 0; - cell_queue_entry_t *q = NULL; - - tor_assert(chan); - tor_assert(chan->write_cell); - tor_assert(chan->write_packed_cell); - tor_assert(chan->write_var_cell); - - if (num_cells < 0) unlimited = 1; - if (!unlimited && num_cells <= flushed) return 0; - - /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ - if (chan->state == CHANNEL_STATE_OPEN) { - while ((unlimited || num_cells > flushed) && - NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) { - - if (1) { - /* - * Okay, we have a good queue entry, try to give it to the lower - * layer. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - if (chan->write_cell(chan, - q->u.fixed.cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - cell_queue_entry_free(q, 1); - q = NULL; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - cell_queue_entry_free(q, 0); - q = NULL; - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - if (chan->write_packed_cell(chan, - q->u.packed.packed_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - cell_queue_entry_free(q, 1); - q = NULL; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - cell_queue_entry_free(q, 0); - q = NULL; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - if (chan->write_var_cell(chan, - q->u.var.var_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - cell_queue_entry_free(q, 1); - q = NULL; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - cell_queue_entry_free(q, 0); - q = NULL; - } - break; - default: - /* Unknown type, log and free it */ - log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p " - "(global ID " U64_FORMAT "; ignoring it." - " Someone should fix this.", - q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); - cell_queue_entry_free(q, 0); - q = NULL; - } - - /* if q got NULLed out, we used it and should remove the queue entry */ - if (!q) TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); - /* No cell removed from list, so we can't go on any further */ - else break; - } - } - } - - /* Did we drain the queue? */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - channel_timestamp_drained(chan); - } - - return flushed; -} - -/** - * Flush as many cells as we possibly can from the queue - * - * This tries to flush as many cells from the queue as the lower layer - * will take. It just calls channel_flush_some_cells_from_outgoing_queue() - * in unlimited mode. - */ - -void -channel_flush_cells(channel_t *chan) -{ - channel_flush_some_cells_from_outgoing_queue(chan, -1); -} - -/** - * Check if any cells are available - * - * This gets used from the lower layer to check if any more cells are - * available. - */ - -int -channel_more_to_flush(channel_t *chan) -{ - tor_assert(chan); - - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - - /* Check if any circuits would like to queue some */ - if (circuitmux_num_cells(chan->cmux) > 0) return 1; - - /* Else no */ - return 0; -} - -/** - * Notify the channel we're done flushing the output in the lower layer - * - * Connection.c will call this when we've flushed the output; there's some - * dirreq-related maintenance to do. - */ - -void -channel_notify_flushed(channel_t *chan) -{ - tor_assert(chan); - - if (chan->dirreq_id != 0) - geoip_change_dirreq_state(chan->dirreq_id, - DIRREQ_TUNNELED, - DIRREQ_CHANNEL_BUFFER_FLUSHED); -} - -/** - * Process the queue of incoming channels on a listener - * - * Use a listener's registered callback to process as many entries in the - * queue of incoming channels as possible. - */ - -void -channel_listener_process_incoming(channel_listener_t *listener) -{ - tor_assert(listener); - - /* - * CHANNEL_LISTENER_STATE_CLOSING permitted because we drain the queue - * while closing a listener. - */ - tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING || - listener->state == CHANNEL_LISTENER_STATE_CLOSING); - tor_assert(listener->listener); - - log_debug(LD_CHANNEL, - "Processing queue of incoming connections for channel " - "listener %p (global ID " U64_FORMAT ")", - listener, U64_PRINTF_ARG(listener->global_identifier)); - - if (!(listener->incoming_list)) return; - - SMARTLIST_FOREACH_BEGIN(listener->incoming_list, - channel_t *, chan) { - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Handling incoming channel %p (" U64_FORMAT ") " - "for listener %p (" U64_FORMAT ")", - chan, - U64_PRINTF_ARG(chan->global_identifier), - listener, - U64_PRINTF_ARG(listener->global_identifier)); - /* Make sure this is set correctly */ - channel_mark_incoming(chan); - listener->listener(listener, chan); - } SMARTLIST_FOREACH_END(chan); - - smartlist_free(listener->incoming_list); - listener->incoming_list = NULL; -} - -/** - * Take actions required when a channel becomes open - * - * Handle actions we should do when we know a channel is open; a lot of - * this comes from the old connection_or_set_state_open() of connection_or.c. - * - * Because of this mechanism, future channel_t subclasses should take care - * not to change a channel to from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN - * until there is positive confirmation that the network is operational. - * In particular, anything UDP-based should not make this transition until a - * packet is received from the other side. - */ - -void -channel_do_open_actions(channel_t *chan) -{ - tor_addr_t remote_addr; - int started_here, not_using = 0; - time_t now = time(NULL); - - tor_assert(chan); - - started_here = channel_is_outgoing(chan); - - if (started_here) { - circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - rep_hist_note_connect_succeeded(chan->identity_digest, now); - if (entry_guard_register_connect_status( - chan->identity_digest, 1, 0, now) < 0) { - /* Close any circuits pending on this channel. We leave it in state - * 'open' though, because it didn't actually *fail* -- we just - * chose not to use it. */ - log_debug(LD_OR, - "New entry guard was reachable, but closing this " - "connection so we can retry the earlier entry guards."); - circuit_n_chan_done(chan, 0); - not_using = 1; - } - router_set_status(chan->identity_digest, 1); - } else { - /* only report it to the geoip module if it's not a known router */ - if (!router_get_by_id_digest(chan->identity_digest)) { - if (channel_get_addr_if_possible(chan, &remote_addr)) { - char *transport_name = NULL; - if (chan->get_transport_name(chan, &transport_name) < 0) - transport_name = NULL; - - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, - &remote_addr, transport_name, - now); - tor_free(transport_name); - } - /* Otherwise the underlying transport can't tell us this, so skip it */ - } - } - - if (!not_using) circuit_n_chan_done(chan, 1); -} - -/** - * Queue an incoming channel on a listener - * - * Internal and subclass use only function to queue an incoming channel from - * a listener. A subclass of channel_listener_t should call this when a new - * incoming channel is created. - */ - -void -channel_listener_queue_incoming(channel_listener_t *listener, - channel_t *incoming) -{ - int need_to_queue = 0; - - tor_assert(listener); - tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING); - tor_assert(incoming); - - log_debug(LD_CHANNEL, - "Queueing incoming channel %p (global ID " U64_FORMAT ") on " - "channel listener %p (global ID " U64_FORMAT ")", - incoming, U64_PRINTF_ARG(incoming->global_identifier), - listener, U64_PRINTF_ARG(listener->global_identifier)); - - /* Do we need to queue it, or can we just call the listener right away? */ - if (!(listener->listener)) need_to_queue = 1; - if (listener->incoming_list && - (smartlist_len(listener->incoming_list) > 0)) - need_to_queue = 1; - - /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(listener->incoming_list)) { - listener->incoming_list = smartlist_new(); - } - - /* Bump the counter and timestamp it */ - channel_listener_timestamp_active(listener); - channel_listener_timestamp_accepted(listener); - ++(listener->n_accepted); - - /* If we don't need to queue, process it right away */ - if (!need_to_queue) { - tor_assert(listener->listener); - listener->listener(listener, incoming); - } - /* - * Otherwise, we need to queue; queue and then process the queue if - * we can. - */ - else { - tor_assert(listener->incoming_list); - smartlist_add(listener->incoming_list, incoming); - if (listener->listener) channel_listener_process_incoming(listener); - } -} - -/** - * Process queued incoming cells - * - * Process as many queued cells as we can from the incoming - * cell queue. - */ - -void -channel_process_cells(channel_t *chan) -{ - cell_queue_entry_t *q; - tor_assert(chan); - tor_assert(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_MAINT || - chan->state == CHANNEL_STATE_OPEN); - - log_debug(LD_CHANNEL, - "Processing as many incoming cells as we can for channel %p", - chan); - - /* Nothing we can do if we have no registered cell handlers */ - if (!(chan->cell_handler || - chan->var_cell_handler)) return; - /* Nothing we can do if we have no cells */ - if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return; - - /* - * Process cells until we're done or find one we have no current handler - * for. - */ - while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) { - tor_assert(q); - tor_assert(q->type == CELL_QUEUE_FIXED || - q->type == CELL_QUEUE_VAR); - - if (q->type == CELL_QUEUE_FIXED && - chan->cell_handler) { - /* Handle a fixed-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.fixed.cell); - log_debug(LD_CHANNEL, - "Processing incoming cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.fixed.cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, q->u.fixed.cell); - tor_free(q); - } else if (q->type == CELL_QUEUE_VAR && - chan->var_cell_handler) { - /* Handle a variable-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.var.var_cell); - log_debug(LD_CHANNEL, - "Processing incoming var_cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.var.var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, q->u.var.var_cell); - tor_free(q); - } else { - /* Can't handle this one */ - break; - } - } -} - -/** - * Queue incoming cell - * - * This should be called by a channel_t subclass to queue an incoming fixed- - * length cell for processing, and process it if possible. - */ - -void -channel_queue_cell(channel_t *chan, cell_t *cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - - tor_assert(chan); - tor_assert(cell); - tor_assert(chan->state == CHANNEL_STATE_OPEN); - - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; - - /* Timestamp for receiving */ - channel_timestamp_recv(chan); - - /* Update the counter */ - ++(chan->n_cells_recved); - - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, cell); - } else { - /* Otherwise queue it and then process the queue if possible. */ - q = cell_queue_entry_new_fixed(cell); - log_debug(LD_CHANNEL, - "Queueing incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } -} - -/** - * Queue incoming variable-length cell - * - * This should be called by a channel_t subclass to queue an incoming - * variable-length cell for processing, and process it if possible. - */ - -void -channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - - tor_assert(chan); - tor_assert(var_cell); - tor_assert(chan->state == CHANNEL_STATE_OPEN); - - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->var_cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; - - /* Timestamp for receiving */ - channel_timestamp_recv(chan); - - /* Update the counter */ - ++(chan->n_cells_recved); - - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->var_cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, var_cell); - } else { - /* Otherwise queue it and then process the queue if possible. */ - q = cell_queue_entry_new_var(var_cell); - log_debug(LD_CHANNEL, - "Queueing incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } -} - -/** If packed_cell on chan is a destroy cell, then set - * *circid_out to its circuit ID, and return true. Otherwise, return - * false. */ -/* XXXX Move this function. */ -int -packed_cell_is_destroy(channel_t *chan, - const packed_cell_t *packed_cell, - circid_t *circid_out) -{ - if (chan->wide_circ_ids) { - if (packed_cell->body[4] == CELL_DESTROY) { - *circid_out = ntohl(get_uint32(packed_cell->body)); - return 1; - } - } else { - if (packed_cell->body[2] == CELL_DESTROY) { - *circid_out = ntohs(get_uint16(packed_cell->body)); - return 1; - } - } - return 0; -} - -/** DOCDOC */ -static int -is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out) -{ - *circid_out = 0; - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell->command == CELL_DESTROY) { - *circid_out = q->u.fixed.cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell->command == CELL_DESTROY) { - *circid_out = q->u.var.var_cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_PACKED: - return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out); - } - return 0; -} - -/** - * Send destroy cell on a channel - * - * Write a destroy cell with circ ID circ_id and reason reason - * onto channel chan. Don't perform range-checking on reason: - * we may want to propagate reasons from other cells. - */ - -int -channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) -{ - tor_assert(chan); - - /* Check to make sure we can send on this channel first */ - if (!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) && - chan->cmux) { - channel_note_destroy_pending(chan, circ_id); - circuitmux_append_destroy_cell(chan, chan->cmux, circ_id, reason); - log_debug(LD_OR, - "Sending destroy (circID %u) on channel %p " - "(global ID " U64_FORMAT ")", - (unsigned)circ_id, chan, - U64_PRINTF_ARG(chan->global_identifier)); - } else { - log_warn(LD_BUG, - "Someone called channel_send_destroy() for circID %u " - "on a channel " U64_FORMAT " at %p in state %s (%d)", - (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier), - chan, channel_state_to_string(chan->state), - chan->state); - } - - return 0; -} - -/** - * Dump channel statistics to the log - * - * This is called from dumpstats() in onion_main.c and spams the log with - * statistics on channels. - */ - -void -channel_dumpstats(int severity) -{ - if (all_channels && smartlist_len(all_channels) > 0) { - tor_log(severity, LD_GENERAL, - "Dumping statistics about %d channels:", - smartlist_len(all_channels)); - tor_log(severity, LD_GENERAL, - "%d are active, and %d are done and waiting for cleanup", - (active_channels != NULL) ? - smartlist_len(active_channels) : 0, - (finished_channels != NULL) ? - smartlist_len(finished_channels) : 0); - - SMARTLIST_FOREACH(all_channels, channel_t *, chan, - channel_dump_statistics(chan, severity)); - - tor_log(severity, LD_GENERAL, - "Done spamming about channels now"); - } else { - tor_log(severity, LD_GENERAL, - "No channels to dump"); - } -} - -/** - * Dump channel listener statistics to the log - * - * This is called from dumpstats() in onion_main.c and spams the log with - * statistics on channel listeners. - */ - -void -channel_listener_dumpstats(int severity) -{ - if (all_listeners && smartlist_len(all_listeners) > 0) { - tor_log(severity, LD_GENERAL, - "Dumping statistics about %d channel listeners:", - smartlist_len(all_listeners)); - tor_log(severity, LD_GENERAL, - "%d are active and %d are done and waiting for cleanup", - (active_listeners != NULL) ? - smartlist_len(active_listeners) : 0, - (finished_listeners != NULL) ? - smartlist_len(finished_listeners) : 0); - - SMARTLIST_FOREACH(all_listeners, channel_listener_t *, chan_l, - channel_listener_dump_statistics(chan_l, severity)); - - tor_log(severity, LD_GENERAL, - "Done spamming about channel listeners now"); - } else { - tor_log(severity, LD_GENERAL, - "No channel listeners to dump"); - } -} - -/** - * Set the cmux policy on all active channels - */ - -void -channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol) -{ - if (!active_channels) return; - - SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) { - if (curr->cmux) { - circuitmux_set_policy(curr->cmux, pol); - } - } SMARTLIST_FOREACH_END(curr); -} - -/** - * Clean up channels - * - * This gets called periodically from run_scheduled_events() in onion_main.c; - * it cleans up after closed channels. - */ - -void -channel_run_cleanup(void) -{ - channel_t *tmp = NULL; - - /* Check if we need to do anything */ - if (!finished_channels || smartlist_len(finished_channels) == 0) return; - - /* Iterate through finished_channels and get rid of them */ - SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(finished_channels, curr); - /* Also unregister it */ - channel_unregister(tmp); - /* ... and free it */ - channel_free(tmp); - } SMARTLIST_FOREACH_END(curr); -} - -/** - * Clean up channel listeners - * - * This gets called periodically from run_scheduled_events() in onion_main.c; - * it cleans up after closed channel listeners. - */ - -void -channel_listener_run_cleanup(void) -{ - channel_listener_t *tmp = NULL; - - /* Check if we need to do anything */ - if (!finished_listeners || smartlist_len(finished_listeners) == 0) return; - - /* Iterate through finished_channels and get rid of them */ - SMARTLIST_FOREACH_BEGIN(finished_listeners, channel_listener_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(finished_listeners, curr); - /* Also unregister it */ - channel_listener_unregister(tmp); - /* ... and free it */ - channel_listener_free(tmp); - } SMARTLIST_FOREACH_END(curr); -} - -/** - * Free a list of channels for channel_free_all() - */ - -static void -channel_free_list(smartlist_t *channels, int mark_for_close) -{ - if (!channels) return; - - SMARTLIST_FOREACH_BEGIN(channels, channel_t *, curr) { - /* Deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up channel %p (global ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_state_to_string(curr->state), curr->state); - /* Detach circuits early so they can find the channel */ - if (curr->cmux) { - circuitmux_detach_all_circuits(curr->cmux); - } - channel_unregister(curr); - if (mark_for_close) { - if (!(curr->state == CHANNEL_STATE_CLOSING || - curr->state == CHANNEL_STATE_CLOSED || - curr->state == CHANNEL_STATE_ERROR)) { - channel_mark_for_close(curr); - } - channel_force_free(curr); - } else channel_free(curr); - } SMARTLIST_FOREACH_END(curr); -} - -/** - * Free a list of channel listeners for channel_free_all() - */ - -static void -channel_listener_free_list(smartlist_t *listeners, int mark_for_close) -{ - if (!listeners) return; - - SMARTLIST_FOREACH_BEGIN(listeners, channel_listener_t *, curr) { - /* Deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up channel listener %p (global ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_listener_state_to_string(curr->state), curr->state); - channel_listener_unregister(curr); - if (mark_for_close) { - if (!(curr->state == CHANNEL_LISTENER_STATE_CLOSING || - curr->state == CHANNEL_LISTENER_STATE_CLOSED || - curr->state == CHANNEL_LISTENER_STATE_ERROR)) { - channel_listener_mark_for_close(curr); - } - channel_listener_force_free(curr); - } else channel_listener_free(curr); - } SMARTLIST_FOREACH_END(curr); -} - -/** - * Close all channels and free everything - * - * This gets called from tor_free_all() in onion_main.c to clean up on exit. - * It will close all registered channels and free associated storage, - * then free the all_channels, active_channels, listening_channels and - * finished_channels lists and also channel_identity_map. - */ - -void -channel_free_all(void) -{ - log_debug(LD_CHANNEL, - "Shutting down channels..."); - - /* First, let's go for finished channels */ - if (finished_channels) { - channel_free_list(finished_channels, 0); - smartlist_free(finished_channels); - finished_channels = NULL; - } - - /* Now the finished listeners */ - if (finished_listeners) { - channel_listener_free_list(finished_listeners, 0); - smartlist_free(finished_listeners); - finished_listeners = NULL; - } - - /* Now all active channels */ - if (active_channels) { - channel_free_list(active_channels, 1); - smartlist_free(active_channels); - active_channels = NULL; - } - - /* Now all active listeners */ - if (active_listeners) { - channel_listener_free_list(active_listeners, 1); - smartlist_free(active_listeners); - active_listeners = NULL; - } - - /* Now all channels, in case any are left over */ - if (all_channels) { - channel_free_list(all_channels, 1); - smartlist_free(all_channels); - all_channels = NULL; - } - - /* Now all listeners, in case any are left over */ - if (all_listeners) { - channel_listener_free_list(all_listeners, 1); - smartlist_free(all_listeners); - all_listeners = NULL; - } - - /* Now free channel_identity_map */ - log_debug(LD_CHANNEL, - "Freeing channel_identity_map"); - /* Geez, anything still left over just won't die ... let it leak then */ - HT_CLEAR(channel_idmap, &channel_identity_map); - - log_debug(LD_CHANNEL, - "Done cleaning up after channels"); -} - -/** - * Connect to a given addr/port/digest - * - * This sets up a new outgoing channel; in the future if multiple - * channel_t subclasses are available, this is where the selection policy - * should go. It may also be desirable to fold port into tor_addr_t - * or make a new type including a tor_addr_t and port, so we have a - * single abstract object encapsulating all the protocol details of - * how to contact an OR. - */ - -channel_t * -channel_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest) -{ - return channel_tls_connect(addr, port, id_digest); -} - -/** - * Decide which of two channels to prefer for extending a circuit - * - * This function is called while extending a circuit and returns true iff - * a is 'better' than b. The most important criterion here is that a - * canonical channel is always better than a non-canonical one, but the - * number of circuits and the age are used as tie-breakers. - * - * This is based on the former connection_or_is_better() of connection_or.c - */ - -int -channel_is_better(time_t now, channel_t *a, channel_t *b, - int forgive_new_connections) -{ - int a_grace, b_grace; - int a_is_canonical, b_is_canonical; - int a_has_circs, b_has_circs; - - /* - * Do not definitively deprecate a new channel with no circuits on it - * until this much time has passed. - */ -#define NEW_CHAN_GRACE_PERIOD (15*60) - - tor_assert(a); - tor_assert(b); - - /* Check if one is canonical and the other isn't first */ - a_is_canonical = channel_is_canonical(a); - b_is_canonical = channel_is_canonical(b); - - if (a_is_canonical && !b_is_canonical) return 1; - if (!a_is_canonical && b_is_canonical) return 0; - - /* - * Okay, if we're here they tied on canonicity. Next we check if - * they have any circuits, and if one does and the other doesn't, - * we prefer the one that does, unless we are forgiving and the - * one that has no circuits is in its grace period. - */ - - a_has_circs = (channel_num_circuits(a) > 0); - b_has_circs = (channel_num_circuits(b) > 0); - a_grace = (forgive_new_connections && - (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD)); - b_grace = (forgive_new_connections && - (now < channel_when_created(b) + NEW_CHAN_GRACE_PERIOD)); - - if (a_has_circs && !b_has_circs && !b_grace) return 1; - if (!a_has_circs && b_has_circs && !a_grace) return 0; - - /* They tied on circuits too; just prefer whichever is newer */ - - if (channel_when_created(a) > channel_when_created(b)) return 1; - else return 0; -} - -/** - * Get a channel to extend a circuit - * - * Pick a suitable channel to extend a circuit to given the desired digest - * the address we believe is correct for that digest; this tries to see - * if we already have one for the requested endpoint, but if there is no good - * channel, set *msg_out to a message describing the channel's state - * and our next action, and set *launch_out to a boolean indicated whether - * the caller should try to launch a new channel with channel_connect(). - */ - -channel_t * -channel_get_for_extend(const char *digest, - const tor_addr_t *target_addr, - const char **msg_out, - int *launch_out) -{ - channel_t *chan, *best = NULL; - int n_inprogress_goodaddr = 0, n_old = 0; - int n_noncanonical = 0, n_possible = 0; - time_t now = approx_time(); - - tor_assert(msg_out); - tor_assert(launch_out); - - chan = channel_find_by_remote_digest(digest); - - /* Walk the list, unrefing the old one and refing the new at each - * iteration. - */ - for (; chan; chan = channel_next_with_digest(chan)) { - tor_assert(tor_memeq(chan->identity_digest, - digest, DIGEST_LEN)); - - if (chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR) - continue; - - /* Never return a channel on which the other end appears to be - * a client. */ - if (channel_is_client(chan)) { - continue; - } - - /* Never return a non-open connection. */ - if (chan->state != CHANNEL_STATE_OPEN) { - /* If the address matches, don't launch a new connection for this - * circuit. */ - if (channel_matches_target_addr_for_extend(chan, target_addr)) - ++n_inprogress_goodaddr; - continue; - } - - /* Never return a connection that shouldn't be used for circs. */ - if (channel_is_bad_for_new_circs(chan)) { - ++n_old; - continue; - } - - /* Never return a non-canonical connection using a recent link protocol - * if the address is not what we wanted. - * - * The channel_is_canonical_is_reliable() function asks the lower layer - * if we should trust channel_is_canonical(). The below is from the - * comments of the old circuit_or_get_for_extend() and applies when - * the lower-layer transport is channel_tls_t. - * - * (For old link protocols, we can't rely on is_canonical getting - * set properly if we're talking to the right address, since we might - * have an out-of-date descriptor, and we will get no NETINFO cell to - * tell us about the right address.) - */ - if (!channel_is_canonical(chan) && - channel_is_canonical_is_reliable(chan) && - !channel_matches_target_addr_for_extend(chan, target_addr)) { - ++n_noncanonical; - continue; - } - - ++n_possible; - - if (!best) { - best = chan; /* If we have no 'best' so far, this one is good enough. */ - continue; - } - - if (channel_is_better(now, chan, best, 0)) - best = chan; - } - - if (best) { - *msg_out = "Connection is fine; using it."; - *launch_out = 0; - return best; - } else if (n_inprogress_goodaddr) { - *msg_out = "Connection in progress; waiting."; - *launch_out = 0; - return NULL; - } else if (n_old || n_noncanonical) { - *msg_out = "Connections all too old, or too non-canonical. " - " Launching a new one."; - *launch_out = 1; - return NULL; - } else { - *msg_out = "Not connected. Connecting."; - *launch_out = 1; - return NULL; - } -} - -/** - * Describe the transport subclass for a channel - * - * Invoke a method to get a string description of the lower-layer - * transport for this channel. - */ - -const char * -channel_describe_transport(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->describe_transport); - - return chan->describe_transport(chan); -} - -/** - * Describe the transport subclass for a channel listener - * - * Invoke a method to get a string description of the lower-layer - * transport for this channel listener. - */ - -const char * -channel_listener_describe_transport(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - tor_assert(chan_l->describe_transport); - - return chan_l->describe_transport(chan_l); -} - -/** - * Return the number of entries in queue - */ -static int -chan_cell_queue_len(const chan_cell_queue_t *queue) -{ - int r = 0; - cell_queue_entry_t *cell; - TOR_SIMPLEQ_FOREACH(cell, queue, next) - ++r; - return r; -} - -/** - * Dump channel statistics - * - * Dump statistics for one channel to the log - */ - -void -channel_dump_statistics(channel_t *chan, int severity) -{ - double avg, interval, age; - time_t now = time(NULL); - tor_addr_t remote_addr; - int have_remote_addr; - char *remote_addr_str; - - tor_assert(chan); - - age = (double)(now - chan->timestamp_created); - - tor_log(severity, LD_GENERAL, - "Channel " U64_FORMAT " (at %p) with transport %s is in state " - "%s (%d)", - U64_PRINTF_ARG(chan->global_identifier), chan, - channel_describe_transport(chan), - channel_state_to_string(chan->state), chan->state); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was created at " U64_FORMAT - " (" U64_FORMAT " seconds ago) " - "and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_created), - U64_PRINTF_ARG(now - chan->timestamp_created), - U64_PRINTF_ARG(chan->timestamp_active), - U64_PRINTF_ARG(now - chan->timestamp_active)); - - /* Handle digest and nickname */ - if (!tor_digest_is_zero(chan->identity_digest)) { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and nickname %s", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and no known nickname", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - } - } else { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " of the OR it is connected to, but reports its nickname is %s", - U64_PRINTF_ARG(chan->global_identifier), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " or the nickname of the OR it is connected to", - U64_PRINTF_ARG(chan->global_identifier)); - } - } - - /* Handle remote address and descriptions */ - have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr); - if (have_remote_addr) { - char *actual = tor_strdup(channel_get_actual_remote_descr(chan)); - remote_addr_str = tor_dup_addr(&remote_addr); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says its remote address" - " is %s, and gives a canonical description of \"%s\" and an " - "actual description of \"%s\"", - U64_PRINTF_ARG(chan->global_identifier), - remote_addr_str, - channel_get_canonical_remote_descr(chan), - actual); - tor_free(remote_addr_str); - tor_free(actual); - } else { - char *actual = tor_strdup(channel_get_actual_remote_descr(chan)); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know its remote " - "address, but gives a canonical description of \"%s\" and an " - "actual description of \"%s\"", - U64_PRINTF_ARG(chan->global_identifier), - channel_get_canonical_remote_descr(chan), - actual); - tor_free(actual); - } - - /* Handle marks */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has these marks: %s %s %s " - "%s %s %s", - U64_PRINTF_ARG(chan->global_identifier), - channel_is_bad_for_new_circs(chan) ? - "bad_for_new_circs" : "!bad_for_new_circs", - channel_is_canonical(chan) ? - "canonical" : "!canonical", - channel_is_canonical_is_reliable(chan) ? - "is_canonical_is_reliable" : - "!is_canonical_is_reliable", - channel_is_client(chan) ? - "client" : "!client", - channel_is_local(chan) ? - "local" : "!local", - channel_is_incoming(chan) ? - "incoming" : "outgoing"); - - /* Describe queues */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has %d queued incoming cells" - " and %d queued outgoing cells", - U64_PRINTF_ARG(chan->global_identifier), - chan_cell_queue_len(&chan->incoming_queue), - chan_cell_queue_len(&chan->outgoing_queue)); - - /* Describe circuits */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has %d active circuits out of" - " %d in total", - U64_PRINTF_ARG(chan->global_identifier), - (chan->cmux != NULL) ? - circuitmux_num_active_circuits(chan->cmux) : 0, - (chan->cmux != NULL) ? - circuitmux_num_circuits(chan->cmux) : 0); - - /* Describe timestamps */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was last used by a " - "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_client), - U64_PRINTF_ARG(now - chan->timestamp_client)); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was last drained at " - U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_drained), - U64_PRINTF_ARG(now - chan->timestamp_drained)); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " last received a cell " - "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_recv), - U64_PRINTF_ARG(now - chan->timestamp_recv)); - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " last trasmitted a cell " - "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_xmit), - U64_PRINTF_ARG(now - chan->timestamp_xmit)); - - /* Describe counters and rates */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has received " - U64_FORMAT " cells and transmitted " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->n_cells_recved), - U64_PRINTF_ARG(chan->n_cells_xmitted)); - if (now > chan->timestamp_created && - chan->timestamp_created > 0) { - if (chan->n_cells_recved > 0) { - avg = (double)(chan->n_cells_recved) / age; - if (avg >= 1.0) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has averaged %f " - "cells received per second", - U64_PRINTF_ARG(chan->global_identifier), avg); - } else if (avg >= 0.0) { - interval = 1.0 / avg; - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has averaged %f " - "seconds between received cells", - U64_PRINTF_ARG(chan->global_identifier), interval); - } - } - if (chan->n_cells_xmitted > 0) { - avg = (double)(chan->n_cells_xmitted) / age; - if (avg >= 1.0) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has averaged %f " - "cells transmitted per second", - U64_PRINTF_ARG(chan->global_identifier), avg); - } else if (avg >= 0.0) { - interval = 1.0 / avg; - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has averaged %f " - "seconds between transmitted cells", - U64_PRINTF_ARG(chan->global_identifier), interval); - } - } - } - - /* Dump anything the lower layer has to say */ - channel_dump_transport_statistics(chan, severity); -} - -/** - * Dump channel listener statistics - * - * Dump statistics for one channel listener to the log - */ - -void -channel_listener_dump_statistics(channel_listener_t *chan_l, int severity) -{ - double avg, interval, age; - time_t now = time(NULL); - - tor_assert(chan_l); - - age = (double)(now - chan_l->timestamp_created); - - tor_log(severity, LD_GENERAL, - "Channel listener " U64_FORMAT " (at %p) with transport %s is in " - "state %s (%d)", - U64_PRINTF_ARG(chan_l->global_identifier), chan_l, - channel_listener_describe_transport(chan_l), - channel_listener_state_to_string(chan_l->state), chan_l->state); - tor_log(severity, LD_GENERAL, - " * Channel listener " U64_FORMAT " was created at " U64_FORMAT - " (" U64_FORMAT " seconds ago) " - "and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan_l->global_identifier), - U64_PRINTF_ARG(chan_l->timestamp_created), - U64_PRINTF_ARG(now - chan_l->timestamp_created), - U64_PRINTF_ARG(chan_l->timestamp_active), - U64_PRINTF_ARG(now - chan_l->timestamp_active)); - - tor_log(severity, LD_GENERAL, - " * Channel listener " U64_FORMAT " last accepted an incoming " - "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) " - "and has accepted " U64_FORMAT " channels in total", - U64_PRINTF_ARG(chan_l->global_identifier), - U64_PRINTF_ARG(chan_l->timestamp_accepted), - U64_PRINTF_ARG(now - chan_l->timestamp_accepted), - U64_PRINTF_ARG(chan_l->n_accepted)); - - /* - * If it's sensible to do so, get the rate of incoming channels on this - * listener - */ - if (now > chan_l->timestamp_created && - chan_l->timestamp_created > 0 && - chan_l->n_accepted > 0) { - avg = (double)(chan_l->n_accepted) / age; - if (avg >= 1.0) { - tor_log(severity, LD_GENERAL, - " * Channel listener " U64_FORMAT " has averaged %f incoming " - "channels per second", - U64_PRINTF_ARG(chan_l->global_identifier), avg); - } else if (avg >= 0.0) { - interval = 1.0 / avg; - tor_log(severity, LD_GENERAL, - " * Channel listener " U64_FORMAT " has averaged %f seconds " - "between incoming channels", - U64_PRINTF_ARG(chan_l->global_identifier), interval); - } - } - - /* Dump anything the lower layer has to say */ - channel_listener_dump_transport_statistics(chan_l, severity); -} - -/** - * Invoke transport-specific stats dump for channel - * - * If there is a lower-layer statistics dump method, invoke it - */ - -void -channel_dump_transport_statistics(channel_t *chan, int severity) -{ - tor_assert(chan); - - if (chan->dumpstats) chan->dumpstats(chan, severity); -} - -/** - * Invoke transport-specific stats dump for channel listener - * - * If there is a lower-layer statistics dump method, invoke it - */ - -void -channel_listener_dump_transport_statistics(channel_listener_t *chan_l, - int severity) -{ - tor_assert(chan_l); - - if (chan_l->dumpstats) chan_l->dumpstats(chan_l, severity); -} - -/** - * Return text description of the remote endpoint - * - * This function return a test provided by the lower layer of the remote - * endpoint for this channel; it should specify the actual address connected - * to/from. - * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. - */ -const char * -channel_get_actual_remote_descr(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->get_remote_descr); - - /* Param 1 indicates the actual description */ - return chan->get_remote_descr(chan, GRD_FLAG_ORIGINAL); -} - -/** - * Return the text address of the remote endpoint. - * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. - */ -const char * -channel_get_actual_remote_address(channel_t *chan) -{ - /* Param 1 indicates the actual description */ - return chan->get_remote_descr(chan, GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY); -} - -/** - * Return text description of the remote endpoint canonical address - * - * This function return a test provided by the lower layer of the remote - * endpoint for this channel; it should use the known canonical address for - * this OR's identity digest if possible. - * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. - */ -const char * -channel_get_canonical_remote_descr(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->get_remote_descr); - - /* Param 0 indicates the canonicalized description */ - return chan->get_remote_descr(chan, 0); -} - -/** - * Get remote address if possible. - * - * Write the remote address out to a tor_addr_t if the underlying transport - * supports this operation, and return 1. Return 0 if the underlying transport - * doesn't let us do this. - */ -int -channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) -{ - tor_assert(chan); - tor_assert(addr_out); - - if (chan->get_remote_addr) - return chan->get_remote_addr(chan, addr_out); - /* Else no support, method not implemented */ - else return 0; -} - -/** - * Check if there are outgoing queue writes on this channel - * - * Indicate if either we have queued cells, or if not, whether the underlying - * lower-layer transport thinks it has an output queue. - */ - -int -channel_has_queued_writes(channel_t *chan) -{ - int has_writes = 0; - - tor_assert(chan); - tor_assert(chan->has_queued_writes); - - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - has_writes = 1; - } else { - /* Check with the lower layer */ - has_writes = chan->has_queued_writes(chan); - } - - return has_writes; -} - -/** - * Check the is_bad_for_new_circs flag - * - * This function returns the is_bad_for_new_circs flag of the specified - * channel. - */ - -int -channel_is_bad_for_new_circs(channel_t *chan) -{ - tor_assert(chan); - - return chan->is_bad_for_new_circs; -} - -/** - * Mark a channel as bad for new circuits - * - * Set the is_bad_for_new_circs_flag on chan. - */ - -void -channel_mark_bad_for_new_circs(channel_t *chan) -{ - tor_assert(chan); - - chan->is_bad_for_new_circs = 1; -} - -/** - * Get the client flag - * - * This returns the client flag of a channel, which will be set if - * command_process_create_cell() in command.c thinks this is a connection - * from a client. - */ - -int -channel_is_client(channel_t *chan) -{ - tor_assert(chan); - - return chan->is_client; -} - -/** - * Set the client flag - * - * Mark a channel as being from a client - */ - -void -channel_mark_client(channel_t *chan) -{ - tor_assert(chan); - - chan->is_client = 1; -} - -/** - * Get the canonical flag for a channel - * - * This returns the is_canonical for a channel; this flag is determined by - * the lower layer and can't be set in a transport-independent way. - */ - -int -channel_is_canonical(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->is_canonical); - - return chan->is_canonical(chan, 0); -} - -/** - * Test if the canonical flag is reliable - * - * This function asks if the lower layer thinks it's safe to trust the - * result of channel_is_canonical() - */ - -int -channel_is_canonical_is_reliable(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->is_canonical); - - return chan->is_canonical(chan, 1); -} - -/** - * Test incoming flag - * - * This function gets the incoming flag; this is set when a listener spawns - * a channel. If this returns true the channel was remotely initiated. - */ - -int -channel_is_incoming(channel_t *chan) -{ - tor_assert(chan); - - return chan->is_incoming; -} - -/** - * Set the incoming flag - * - * This function is called when a channel arrives on a listening channel - * to mark it as incoming. - */ - -void -channel_mark_incoming(channel_t *chan) -{ - tor_assert(chan); - - chan->is_incoming = 1; -} - -/** - * Test local flag - * - * This function gets the local flag; the lower layer should set this when - * setting up the channel if is_local_addr() is true for all of the - * destinations it will communicate with on behalf of this channel. It's - * used to decide whether to declare the network reachable when seeing incoming - * traffic on the channel. - */ - -int -channel_is_local(channel_t *chan) -{ - tor_assert(chan); - - return chan->is_local; -} - -/** - * Set the local flag - * - * This internal-only function should be called by the lower layer if the - * channel is to a local address. See channel_is_local() above or the - * description of the is_local bit in channel.h - */ - -void -channel_mark_local(channel_t *chan) -{ - tor_assert(chan); - - chan->is_local = 1; -} - -/** - * Test outgoing flag - * - * This function gets the outgoing flag; this is the inverse of the incoming - * bit set when a listener spawns a channel. If this returns true the channel - * was locally initiated. - */ - -int -channel_is_outgoing(channel_t *chan) -{ - tor_assert(chan); - - return !(chan->is_incoming); -} - -/** - * Mark a channel as outgoing - * - * This function clears the incoming flag and thus marks a channel as - * outgoing. - */ - -void -channel_mark_outgoing(channel_t *chan) -{ - tor_assert(chan); - - chan->is_incoming = 0; -} - -/********************* - * Timestamp updates * - ********************/ - -/** - * Update the created timestamp for a channel - * - * This updates the channel's created timestamp and should only be called - * from channel_init(). - */ - -void -channel_timestamp_created(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_created = now; -} - -/** - * Update the created timestamp for a channel listener - * - * This updates the channel listener's created timestamp and should only be - * called from channel_init_listener(). - */ - -void -channel_listener_timestamp_created(channel_listener_t *chan_l) -{ - time_t now = time(NULL); - - tor_assert(chan_l); - - chan_l->timestamp_created = now; -} - -/** - * Update the last active timestamp for a channel - * - * This function updates the channel's last active timestamp; it should be - * called by the lower layer whenever there is activity on the channel which - * does not lead to a cell being transmitted or received; the active timestamp - * is also updated from channel_timestamp_recv() and channel_timestamp_xmit(), - * but it should be updated for things like the v3 handshake and stuff that - * produce activity only visible to the lower layer. - */ - -void -channel_timestamp_active(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; -} - -/** - * Update the last active timestamp for a channel listener - */ - -void -channel_listener_timestamp_active(channel_listener_t *chan_l) -{ - time_t now = time(NULL); - - tor_assert(chan_l); - - chan_l->timestamp_active = now; -} - -/** - * Update the last accepted timestamp. - * - * This function updates the channel listener's last accepted timestamp; it - * should be called whenever a new incoming channel is accepted on a - * listener. - */ - -void -channel_listener_timestamp_accepted(channel_listener_t *chan_l) -{ - time_t now = time(NULL); - - tor_assert(chan_l); - - chan_l->timestamp_active = now; - chan_l->timestamp_accepted = now; -} - -/** - * Update client timestamp - * - * This function is called by relay.c to timestamp a channel that appears to - * be used as a client. - */ - -void -channel_timestamp_client(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_client = now; -} - -/** - * Update the last drained timestamp - * - * This is called whenever we transmit a cell which leaves the outgoing cell - * queue completely empty. It also updates the xmit time and the active time. - */ - -void -channel_timestamp_drained(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_drained = now; - chan->timestamp_xmit = now; -} - -/** - * Update the recv timestamp - * - * This is called whenever we get an incoming cell from the lower layer. - * This also updates the active timestamp. - */ - -void -channel_timestamp_recv(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_recv = now; -} - -/** - * Update the xmit timestamp - * This is called whenever we pass an outgoing cell to the lower layer. This - * also updates the active timestamp. - */ - -void -channel_timestamp_xmit(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_xmit = now; -} - -/*************************************************************** - * Timestamp queries - see above for definitions of timestamps * - **************************************************************/ - -/** - * Query created timestamp for a channel - */ - -time_t -channel_when_created(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_created; -} - -/** - * Query created timestamp for a channel listener - */ - -time_t -channel_listener_when_created(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_created; -} - -/** - * Query last active timestamp for a channel - */ - -time_t -channel_when_last_active(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_active; -} - -/** - * Query last active timestamp for a channel listener - */ - -time_t -channel_listener_when_last_active(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_active; -} - -/** - * Query last accepted timestamp for a channel listener - */ - -time_t -channel_listener_when_last_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_accepted; -} - -/** - * Query client timestamp - */ - -time_t -channel_when_last_client(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_client; -} - -/** - * Query drained timestamp - */ - -time_t -channel_when_last_drained(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_drained; -} - -/** - * Query recv timestamp - */ - -time_t -channel_when_last_recv(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_recv; -} - -/** - * Query xmit timestamp - */ - -time_t -channel_when_last_xmit(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_xmit; -} - -/** - * Query accepted counter - */ - -uint64_t -channel_listener_count_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->n_accepted; -} - -/** - * Query received cell counter - */ - -uint64_t -channel_count_recved(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_recved; -} - -/** - * Query transmitted cell counter - */ - -uint64_t -channel_count_xmitted(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_xmitted; -} - -/** - * Check if a channel matches an extend_info_t - * - * This function calls the lower layer and asks if this channel matches a - * given extend_info_t. - */ - -int -channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) -{ - tor_assert(chan); - tor_assert(chan->matches_extend_info); - tor_assert(extend_info); - - return chan->matches_extend_info(chan, extend_info); -} - -/** - * Check if a channel matches a given target address; return true iff we do. - * - * This function calls into the lower layer and asks if this channel thinks - * it matches a given target address for circuit extension purposes. - */ - -int -channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target) -{ - tor_assert(chan); - tor_assert(chan->matches_target); - tor_assert(target); - - return chan->matches_target(chan, target); -} - -/** - * Return the total number of circuits used by a channel - * - * @param chan Channel to query - * @return Number of circuits using this as n_chan or p_chan - */ - -unsigned int -channel_num_circuits(channel_t *chan) -{ - tor_assert(chan); - - return chan->num_n_circuits + - chan->num_p_circuits; -} - -/** - * Set up circuit ID generation - * - * This is called when setting up a channel and replaces the old - * connection_or_set_circid_type() - */ -void -channel_set_circid_type(channel_t *chan, - crypto_pk_t *identity_rcvd, - int consider_identity) -{ - int started_here; - crypto_pk_t *our_identity; - - tor_assert(chan); - - started_here = channel_is_outgoing(chan); - - if (! consider_identity) { - if (started_here) - chan->circ_id_type = CIRC_ID_TYPE_HIGHER; - else - chan->circ_id_type = CIRC_ID_TYPE_LOWER; - return; - } - - our_identity = started_here ? - get_tlsclient_identity_key() : get_server_identity_key(); - - if (identity_rcvd) { - if (crypto_pk_cmp_keys(our_identity, identity_rcvd) < 0) { - chan->circ_id_type = CIRC_ID_TYPE_LOWER; - } else { - chan->circ_id_type = CIRC_ID_TYPE_HIGHER; - } - } else { - chan->circ_id_type = CIRC_ID_TYPE_NEITHER; - } -} - diff --git a/src/tor/channel.h b/src/tor/channel.h deleted file mode 100644 index a9ade3f..0000000 --- a/src/tor/channel.h +++ /dev/null @@ -1,489 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file channel.h - * \brief Header file for channel.c - **/ - -#ifndef TOR_CHANNEL_H -#define TOR_CHANNEL_H - -#include "or.h" -#include "circuitmux.h" - -/* Channel handler function pointer typedefs */ -typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); -typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); -typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); - -struct cell_queue_entry_s; -TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s) incoming_queue; -typedef struct chan_cell_queue chan_cell_queue_t; - -/* - * Channel struct; see the channel_t typedef in or.h. A channel is an - * abstract interface for the OR-to-OR connection, similar to connection_or_t, - * but without the strong coupling to the underlying TLS implementation. They - * are constructed by calling a protocol-specific function to open a channel - * to a particular node, and once constructed support the abstract operations - * defined below. - */ - -struct channel_s { - /* Magic number for type-checking cast macros */ - uint32_t magic; - - /* Current channel state */ - channel_state_t state; - - /* Globally unique ID number for a channel over the lifetime of a Tor - * process. - */ - uint64_t global_identifier; - - /* Should we expect to see this channel in the channel lists? */ - unsigned char registered:1; - - /** has this channel ever been open? */ - unsigned int has_been_open:1; - - /** Why did we close? - */ - enum { - CHANNEL_NOT_CLOSING = 0, - CHANNEL_CLOSE_REQUESTED, - CHANNEL_CLOSE_FROM_BELOW, - CHANNEL_CLOSE_FOR_ERROR - } reason_for_closing; - - /* Timestamps for both cell channels and listeners */ - time_t timestamp_created; /* Channel created */ - time_t timestamp_active; /* Any activity */ - - /* Methods implemented by the lower layer */ - - /* Free a channel */ - void (*free)(channel_t *); - /* Close an open channel */ - void (*close)(channel_t *); - /* Describe the transport subclass for this channel */ - const char * (*describe_transport)(channel_t *); - /* Optional method to dump transport-specific statistics on the channel */ - void (*dumpstats)(channel_t *, int); - - /* Registered handlers for incoming cells */ - channel_cell_handler_fn_ptr cell_handler; - channel_var_cell_handler_fn_ptr var_cell_handler; - - /* Methods implemented by the lower layer */ - - /* - * Ask the underlying transport what the remote endpoint address is, in - * a tor_addr_t. This is optional and subclasses may leave this NULL. - * If they implement it, they should write the address out to the - * provided tor_addr_t *, and return 1 if successful or 0 if no address - * available. - */ - int (*get_remote_addr)(channel_t *, tor_addr_t *); - int (*get_transport_name)(channel_t *chan, char **transport_out); - -#define GRD_FLAG_ORIGINAL 1 -#define GRD_FLAG_ADDR_ONLY 2 - /* - * Get a text description of the remote endpoint; canonicalized if the flag - * GRD_FLAG_ORIGINAL is not set, or the one we originally connected - * to/received from if it is. If GRD_FLAG_ADDR_ONLY is set, we return only - * the original address. - */ - const char * (*get_remote_descr)(channel_t *, int); - /* Check if the lower layer has queued writes */ - int (*has_queued_writes)(channel_t *); - /* - * If the second param is zero, ask the lower layer if this is - * 'canonical', for a transport-specific definition of canonical; if - * it is 1, ask if the answer to the preceding query is safe to rely - * on. - */ - int (*is_canonical)(channel_t *, int); - /* Check if this channel matches a specified extend_info_t */ - int (*matches_extend_info)(channel_t *, extend_info_t *); - /* Check if this channel matches a target address when extending */ - int (*matches_target)(channel_t *, const tor_addr_t *); - /* Write a cell to an open channel */ - int (*write_cell)(channel_t *, cell_t *); - /* Write a packed cell to an open channel */ - int (*write_packed_cell)(channel_t *, packed_cell_t *); - /* Write a variable-length cell to an open channel */ - int (*write_var_cell)(channel_t *, var_cell_t *); - - /* - * Hash of the public RSA key for the other side's identity key, or - * zeroes if the other side hasn't shown us a valid identity key. - */ - char identity_digest[DIGEST_LEN]; - /* Nickname of the OR on the other side, or NULL if none. */ - char *nickname; - - /* - * Linked list of channels with the same identity digest, for the - * digest->channel map - */ - TOR_LIST_ENTRY(channel_s) next_with_same_id; - - /* List of incoming cells to handle */ - chan_cell_queue_t incoming_queue; - - /* List of queued outgoing cells */ - chan_cell_queue_t outgoing_queue; - - /* Circuit mux for circuits sending on this channel */ - circuitmux_t *cmux; - - /* Circuit ID generation stuff for use by circuitbuild.c */ - - /* - * When we send CREATE cells along this connection, which half of the - * space should we use? - */ - ENUM_BF(circ_id_type_t) circ_id_type:2; - /** DOCDOC*/ - unsigned wide_circ_ids:1; - /* - * Which circ_id do we try to use next on this connection? This is - * always in the range 0..1<<15-1. - */ - circid_t next_circ_id; - - /* For how many circuits are we n_chan? What about p_chan? */ - unsigned int num_n_circuits, num_p_circuits; - - /* - * True iff this channel shouldn't get any new circs attached to it, - * because the connection is too old, or because there's a better one. - * More generally, this flag is used to note an unhealthy connection; - * for example, if a bad connection fails we shouldn't assume that the - * router itself has a problem. - */ - unsigned int is_bad_for_new_circs:1; - - /** True iff we have decided that the other end of this connection - * is a client. Channels with this flag set should never be used - * to satisfy an EXTEND request. */ - unsigned int is_client:1; - - /** Set if the channel was initiated remotely (came from a listener) */ - unsigned int is_incoming:1; - - /** Set by lower layer if this is local; i.e., everything it communicates - * with for this channel returns true for is_local_addr(). This is used - * to decide whether to declare reachability when we receive something on - * this channel in circuitbuild.c - */ - unsigned int is_local:1; - - /** Channel timestamps for cell channels */ - time_t timestamp_client; /* Client used this, according to relay.c */ - time_t timestamp_drained; /* Output queue empty */ - time_t timestamp_recv; /* Cell received from lower layer */ - time_t timestamp_xmit; /* Cell sent to lower layer */ - - /* Timestamp for relay.c */ - time_t timestamp_last_added_nonpadding; - - /** Unique ID for measuring direct network status requests;vtunneled ones - * come over a circuit_t, which has a dirreq_id field as well, but is a - * distinct namespace. */ - uint64_t dirreq_id; - - /** Channel counters for cell channels */ - uint64_t n_cells_recved; - uint64_t n_cells_xmitted; -}; - -struct channel_listener_s { - /* Current channel listener state */ - channel_listener_state_t state; - - /* Globally unique ID number for a channel over the lifetime of a Tor - * process. - */ - uint64_t global_identifier; - - /* Should we expect to see this channel in the channel lists? */ - unsigned char registered:1; - - /** Why did we close? - */ - enum { - CHANNEL_LISTENER_NOT_CLOSING = 0, - CHANNEL_LISTENER_CLOSE_REQUESTED, - CHANNEL_LISTENER_CLOSE_FROM_BELOW, - CHANNEL_LISTENER_CLOSE_FOR_ERROR - } reason_for_closing; - - /* Timestamps for both cell channels and listeners */ - time_t timestamp_created; /* Channel created */ - time_t timestamp_active; /* Any activity */ - - /* Methods implemented by the lower layer */ - - /* Free a channel */ - void (*free)(channel_listener_t *); - /* Close an open channel */ - void (*close)(channel_listener_t *); - /* Describe the transport subclass for this channel */ - const char * (*describe_transport)(channel_listener_t *); - /* Optional method to dump transport-specific statistics on the channel */ - void (*dumpstats)(channel_listener_t *, int); - - /* Registered listen handler to call on incoming connection */ - channel_listener_fn_ptr listener; - - /* List of pending incoming connections */ - smartlist_t *incoming_list; - - /* Timestamps for listeners */ - time_t timestamp_accepted; - - /* Counters for listeners */ - uint64_t n_accepted; -}; - -/* Channel state manipulations */ - -int channel_state_is_valid(channel_state_t state); -int channel_listener_state_is_valid(channel_listener_state_t state); - -int channel_state_can_transition(channel_state_t from, channel_state_t to); -int channel_listener_state_can_transition(channel_listener_state_t from, - channel_listener_state_t to); - -const char * channel_state_to_string(channel_state_t state); -const char * -channel_listener_state_to_string(channel_listener_state_t state); - -/* Abstract channel operations */ - -void channel_mark_for_close(channel_t *chan); -void channel_write_cell(channel_t *chan, cell_t *cell); -void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); -void channel_write_var_cell(channel_t *chan, var_cell_t *cell); - -void channel_listener_mark_for_close(channel_listener_t *chan_l); - -/* Channel callback registrations */ - -/* Listener callback */ -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan); - -void channel_listener_set_listener_fn(channel_listener_t *chan, - channel_listener_fn_ptr listener); - -/* Incoming cell callbacks */ -channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan); - -channel_var_cell_handler_fn_ptr -channel_get_var_cell_handler(channel_t *chan); - -void channel_set_cell_handlers(channel_t *chan, - channel_cell_handler_fn_ptr cell_handler, - channel_var_cell_handler_fn_ptr - var_cell_handler); - -/* Clean up closed channels and channel listeners periodically; these are - * called from run_scheduled_events() in onion_main.c. - */ -void channel_run_cleanup(void); -void channel_listener_run_cleanup(void); - -/* Close all channels and deallocate everything */ -void channel_free_all(void); - -/* Dump some statistics in the log */ -void channel_dumpstats(int severity); -void channel_listener_dumpstats(int severity); - -/* Set the cmux policy on all active channels */ -void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol); - -#ifdef TOR_CHANNEL_INTERNAL_ - -/* Channel operations for subclasses and internal use only */ - -/* Initialize a newly allocated channel - do this first in subclass - * constructors. - */ - -void channel_init(channel_t *chan); -void channel_init_listener(channel_listener_t *chan); - -/* Channel registration/unregistration */ -void channel_register(channel_t *chan); -void channel_unregister(channel_t *chan); - -/* Channel listener registration/unregistration */ -void channel_listener_register(channel_listener_t *chan_l); -void channel_listener_unregister(channel_listener_t *chan_l); - -/* Close from below */ -void channel_close_from_lower_layer(channel_t *chan); -void channel_close_for_error(channel_t *chan); -void channel_closed(channel_t *chan); - -void channel_listener_close_from_lower_layer(channel_listener_t *chan_l); -void channel_listener_close_for_error(channel_listener_t *chan_l); -void channel_listener_closed(channel_listener_t *chan_l); - -/* Free a channel */ -void channel_free(channel_t *chan); -void channel_listener_free(channel_listener_t *chan_l); - -/* State/metadata setters */ - -void channel_change_state(channel_t *chan, channel_state_t to_state); -void channel_clear_identity_digest(channel_t *chan); -void channel_clear_remote_end(channel_t *chan); -void channel_mark_local(channel_t *chan); -void channel_mark_incoming(channel_t *chan); -void channel_mark_outgoing(channel_t *chan); -void channel_set_identity_digest(channel_t *chan, - const char *identity_digest); -void channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname); - -void channel_listener_change_state(channel_listener_t *chan_l, - channel_listener_state_t to_state); - -/* Timestamp updates */ -void channel_timestamp_created(channel_t *chan); -void channel_timestamp_active(channel_t *chan); -void channel_timestamp_drained(channel_t *chan); -void channel_timestamp_recv(channel_t *chan); -void channel_timestamp_xmit(channel_t *chan); - -void channel_listener_timestamp_created(channel_listener_t *chan_l); -void channel_listener_timestamp_active(channel_listener_t *chan_l); -void channel_listener_timestamp_accepted(channel_listener_t *chan_l); - -/* Incoming channel handling */ -void channel_listener_process_incoming(channel_listener_t *listener); -void channel_listener_queue_incoming(channel_listener_t *listener, - channel_t *incoming); - -/* Incoming cell handling */ -void channel_process_cells(channel_t *chan); -void channel_queue_cell(channel_t *chan, cell_t *cell); -void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell); - -/* Outgoing cell handling */ -void channel_flush_cells(channel_t *chan); - -/* Request from lower layer for more cells if available */ -ssize_t channel_flush_some_cells(channel_t *chan, ssize_t num_cells); - -/* Query if data available on this channel */ -int channel_more_to_flush(channel_t *chan); - -/* Notify flushed outgoing for dirreq handling */ -void channel_notify_flushed(channel_t *chan); - -/* Handle stuff we need to do on open like notifying circuits */ -void channel_do_open_actions(channel_t *chan); - -#endif - -/* Helper functions to perform operations on channels */ - -int channel_send_destroy(circid_t circ_id, channel_t *chan, - int reason); - -/* - * Outside abstract interfaces that should eventually get turned into - * something transport/address format independent. - */ - -channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest); - -channel_t * channel_get_for_extend(const char *digest, - const tor_addr_t *target_addr, - const char **msg_out, - int *launch_out); - -/* Ask which of two channels is better for circuit-extension purposes */ -int channel_is_better(time_t now, - channel_t *a, channel_t *b, - int forgive_new_connections); - -/** Channel lookups - */ - -channel_t * channel_find_by_global_id(uint64_t global_identifier); -channel_t * channel_find_by_remote_digest(const char *identity_digest); - -/** For things returned by channel_find_by_remote_digest(), walk the list. - */ -channel_t * channel_next_with_digest(channel_t *chan); - -/* - * Metadata queries/updates - */ - -const char * channel_describe_transport(channel_t *chan); -void channel_dump_statistics(channel_t *chan, int severity); -void channel_dump_transport_statistics(channel_t *chan, int severity); -const char * channel_get_actual_remote_descr(channel_t *chan); -const char * channel_get_actual_remote_address(channel_t *chan); -int channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out); -const char * channel_get_canonical_remote_descr(channel_t *chan); -int channel_has_queued_writes(channel_t *chan); -int channel_is_bad_for_new_circs(channel_t *chan); -void channel_mark_bad_for_new_circs(channel_t *chan); -int channel_is_canonical(channel_t *chan); -int channel_is_canonical_is_reliable(channel_t *chan); -int channel_is_client(channel_t *chan); -int channel_is_local(channel_t *chan); -int channel_is_incoming(channel_t *chan); -int channel_is_outgoing(channel_t *chan); -void channel_mark_client(channel_t *chan); -int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); -int channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target); -unsigned int channel_num_circuits(channel_t *chan); -void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd, - int consider_identity); -void channel_timestamp_client(channel_t *chan); - -const char * channel_listener_describe_transport(channel_listener_t *chan_l); -void channel_listener_dump_statistics(channel_listener_t *chan_l, - int severity); -void channel_listener_dump_transport_statistics(channel_listener_t *chan_l, - int severity); - -/* Timestamp queries */ -time_t channel_when_created(channel_t *chan); -time_t channel_when_last_active(channel_t *chan); -time_t channel_when_last_client(channel_t *chan); -time_t channel_when_last_drained(channel_t *chan); -time_t channel_when_last_recv(channel_t *chan); -time_t channel_when_last_xmit(channel_t *chan); - -time_t channel_listener_when_created(channel_listener_t *chan_l); -time_t channel_listener_when_last_active(channel_listener_t *chan_l); -time_t channel_listener_when_last_accepted(channel_listener_t *chan_l); - -/* Counter queries */ -uint64_t channel_count_recved(channel_t *chan); -uint64_t channel_count_xmitted(channel_t *chan); - -uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); - -int packed_cell_is_destroy(channel_t *chan, - const packed_cell_t *packed_cell, - circid_t *circid_out); - -#endif - diff --git a/src/tor/channeltls.c b/src/tor/channeltls.c deleted file mode 100644 index 959ec47..0000000 --- a/src/tor/channeltls.c +++ /dev/null @@ -1,2074 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file channeltls.c - * \brief channel_t concrete subclass using or_connection_t - **/ - -/* - * Define this so channel.h gives us things only channel_t subclasses - * should touch. - */ - -#define TOR_CHANNEL_INTERNAL_ - -#include "or.h" -#include "channel.h" -#include "channeltls.h" -#include "circuitmux.h" -#include "circuitmux_ewma.h" -#include "config.h" -#include "connection.h" -#include "connection_or.h" -#include "control.h" -#include "relay.h" -#include "router.h" -#include "routerlist.h" - -/** How many CELL_PADDING cells have we received, ever? */ -uint64_t stats_n_padding_cells_processed = 0; -/** How many CELL_VERSIONS cells have we received, ever? */ -uint64_t stats_n_versions_cells_processed = 0; -/** How many CELL_NETINFO cells have we received, ever? */ -uint64_t stats_n_netinfo_cells_processed = 0; -/** How many CELL_VPADDING cells have we received, ever? */ -uint64_t stats_n_vpadding_cells_processed = 0; -/** How many CELL_CERTS cells have we received, ever? */ -uint64_t stats_n_certs_cells_processed = 0; -/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */ -uint64_t stats_n_auth_challenge_cells_processed = 0; -/** How many CELL_AUTHENTICATE cells have we received, ever? */ -uint64_t stats_n_authenticate_cells_processed = 0; -/** How many CELL_AUTHORIZE cells have we received, ever? */ -uint64_t stats_n_authorize_cells_processed = 0; - -/** Active listener, if any */ -channel_listener_t *channel_tls_listener = NULL; - -/* Utility function declarations */ -static void channel_tls_common_init(channel_tls_t *tlschan); - -/* channel_tls_t method declarations */ - -static void channel_tls_close_method(channel_t *chan); -static const char * channel_tls_describe_transport_method(channel_t *chan); -static void channel_tls_free_method(channel_t *chan); -static int -channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out); -static int -channel_tls_get_transport_name_method(channel_t *chan, char **transport_out); -static const char * -channel_tls_get_remote_descr_method(channel_t *chan, int flags); -static int channel_tls_has_queued_writes_method(channel_t *chan); -static int channel_tls_is_canonical_method(channel_t *chan, int req); -static int -channel_tls_matches_extend_info_method(channel_t *chan, - extend_info_t *extend_info); -static int channel_tls_matches_target_method(channel_t *chan, - const tor_addr_t *target); -static int channel_tls_write_cell_method(channel_t *chan, - cell_t *cell); -static int channel_tls_write_packed_cell_method(channel_t *chan, - packed_cell_t *packed_cell); -static int channel_tls_write_var_cell_method(channel_t *chan, - var_cell_t *var_cell); - -/* channel_listener_tls_t method declarations */ - -static void channel_tls_listener_close_method(channel_listener_t *chan_l); -static const char * -channel_tls_listener_describe_transport_method(channel_listener_t *chan_l); - -/** Handle incoming cells for the handshake stuff here rather than - * passing them on up. */ - -static void channel_tls_process_versions_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_netinfo_cell(cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_certs_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_auth_challenge_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_authenticate_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static int command_allowed_before_handshake(uint8_t command); -static int enter_v3_handshake_with_cell(var_cell_t *cell, - channel_tls_t *tlschan); - -/** - * Do parts of channel_tls_t initialization common to channel_tls_connect() - * and channel_tls_handle_incoming(). - */ - -static void -channel_tls_common_init(channel_tls_t *tlschan) -{ - channel_t *chan; - - tor_assert(tlschan); - - chan = &(tlschan->base_); - channel_init(chan); - chan->magic = TLS_CHAN_MAGIC; - chan->state = CHANNEL_STATE_OPENING; - chan->close = channel_tls_close_method; - chan->describe_transport = channel_tls_describe_transport_method; - chan->free = channel_tls_free_method; - chan->get_remote_addr = channel_tls_get_remote_addr_method; - chan->get_remote_descr = channel_tls_get_remote_descr_method; - chan->get_transport_name = channel_tls_get_transport_name_method; - chan->has_queued_writes = channel_tls_has_queued_writes_method; - chan->is_canonical = channel_tls_is_canonical_method; - chan->matches_extend_info = channel_tls_matches_extend_info_method; - chan->matches_target = channel_tls_matches_target_method; - chan->write_cell = channel_tls_write_cell_method; - chan->write_packed_cell = channel_tls_write_packed_cell_method; - chan->write_var_cell = channel_tls_write_var_cell_method; - - chan->cmux = circuitmux_alloc(); - if (cell_ewma_enabled()) { - circuitmux_set_policy(chan->cmux, &ewma_policy); - } -} - -/** - * Start a new TLS channel - * - * Launch a new OR connection to addr:port and expect to - * handshake with an OR with identity digest id_digest, and wrap - * it in a channel_tls_t. - */ - -channel_t * -channel_tls_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest) -{ - channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); - channel_t *chan = &(tlschan->base_); - - channel_tls_common_init(tlschan); - - log_debug(LD_CHANNEL, - "In channel_tls_connect() for channel %p " - "(global id " U64_FORMAT ")", - tlschan, - U64_PRINTF_ARG(chan->global_identifier)); - - if (is_local_addr(addr)) channel_mark_local(chan); - channel_mark_outgoing(chan); - - /* Set up or_connection stuff */ - tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); - /* connection_or_connect() will fill in tlschan->conn */ - if (!(tlschan->conn)) { - chan->reason_for_closing = CHANNEL_CLOSE_FOR_ERROR; - channel_change_state(chan, CHANNEL_STATE_ERROR); - goto err; - } - - log_debug(LD_CHANNEL, - "Got orconn %p for channel with global id " U64_FORMAT, - tlschan->conn, U64_PRINTF_ARG(chan->global_identifier)); - - goto done; - - err: - circuitmux_free(chan->cmux); - tor_free(tlschan); - chan = NULL; - - done: - /* If we got one, we should register it */ - if (chan) channel_register(chan); - - return chan; -} - -/** - * Return the current channel_tls_t listener - * - * Returns the current channel listener for incoming TLS connections, or - * NULL if none has been established - */ - -channel_listener_t * -channel_tls_get_listener(void) -{ - return channel_tls_listener; -} - -/** - * Start a channel_tls_t listener if necessary - * - * Return the current channel_tls_t listener, or start one if we haven't yet, - * and return that. - */ - -channel_listener_t * -channel_tls_start_listener(void) -{ - channel_listener_t *listener; - - if (!channel_tls_listener) { - listener = tor_malloc_zero(sizeof(*listener)); - channel_init_listener(listener); - listener->state = CHANNEL_LISTENER_STATE_LISTENING; - listener->close = channel_tls_listener_close_method; - listener->describe_transport = - channel_tls_listener_describe_transport_method; - - channel_tls_listener = listener; - - log_debug(LD_CHANNEL, - "Starting TLS channel listener %p with global id " U64_FORMAT, - listener, U64_PRINTF_ARG(listener->global_identifier)); - - channel_listener_register(listener); - } else listener = channel_tls_listener; - - return listener; -} - -/** - * Free everything on shutdown - * - * Not much to do here, since channel_free_all() takes care of a lot, but let's - * get rid of the listener. - */ - -void -channel_tls_free_all(void) -{ - channel_listener_t *old_listener = NULL; - - log_debug(LD_CHANNEL, - "Shutting down TLS channels..."); - - if (channel_tls_listener) { - /* - * When we close it, channel_tls_listener will get nulled out, so save - * a pointer so we can free it. - */ - old_listener = channel_tls_listener; - log_debug(LD_CHANNEL, - "Closing channel_tls_listener with ID " U64_FORMAT - " at %p.", - U64_PRINTF_ARG(old_listener->global_identifier), - old_listener); - channel_listener_unregister(old_listener); - channel_listener_mark_for_close(old_listener); - channel_listener_free(old_listener); - tor_assert(channel_tls_listener == NULL); - } - - log_debug(LD_CHANNEL, - "Done shutting down TLS channels"); -} - -/** - * Create a new channel around an incoming or_connection_t - */ - -channel_t * -channel_tls_handle_incoming(or_connection_t *orconn) -{ - channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); - channel_t *chan = &(tlschan->base_); - - tor_assert(orconn); - tor_assert(!(orconn->chan)); - - channel_tls_common_init(tlschan); - - /* Link the channel and orconn to each other */ - tlschan->conn = orconn; - orconn->chan = tlschan; - - if (is_local_addr(&(TO_CONN(orconn)->addr))) channel_mark_local(chan); - channel_mark_incoming(chan); - - /* Register it */ - channel_register(chan); - - return chan; -} - -/********* - * Casts * - ********/ - -/** - * Cast a channel_tls_t to a channel_t. - */ - -channel_t * -channel_tls_to_base(channel_tls_t *tlschan) -{ - if (!tlschan) return NULL; - - return &(tlschan->base_); -} - -/** - * Cast a channel_t to a channel_tls_t, with appropriate type-checking - * asserts. - */ - -channel_tls_t * -channel_tls_from_base(channel_t *chan) -{ - if (!chan) return NULL; - - tor_assert(chan->magic == TLS_CHAN_MAGIC); - - return (channel_tls_t *)(chan); -} - -/******************************************** - * Method implementations for channel_tls_t * - *******************************************/ - -/** - * Close a channel_tls_t - * - * This implements the close method for channel_tls_t - */ - -static void -channel_tls_close_method(channel_t *chan) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - - if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1); - else { - /* Weird - we'll have to change the state ourselves, I guess */ - log_info(LD_CHANNEL, - "Tried to close channel_tls_t %p with NULL conn", - tlschan); - channel_change_state(chan, CHANNEL_STATE_ERROR); - } -} - -/** - * Describe the transport for a channel_tls_t - * - * This returns the string "TLS channel on connection " to the upper - * layer. - */ - -static const char * -channel_tls_describe_transport_method(channel_t *chan) -{ - static char *buf = NULL; - uint64_t id; - channel_tls_t *tlschan; - const char *rv = NULL; - - tor_assert(chan); - - tlschan = BASE_CHAN_TO_TLS(chan); - - if (tlschan->conn) { - id = TO_CONN(tlschan->conn)->global_identifier; - - if (buf) tor_free(buf); - tor_asprintf(&buf, - "TLS channel (connection " U64_FORMAT ")", - U64_PRINTF_ARG(id)); - - rv = buf; - } else { - rv = "TLS channel (no connection)"; - } - - return rv; -} - -/** - * Free a channel_tls_t - * - * This is called by the generic channel layer when freeing a channel_tls_t; - * this happens either on a channel which has already reached - * CHANNEL_STATE_CLOSED or CHANNEL_STATE_ERROR from channel_run_cleanup() or - * on shutdown from channel_free_all(). In the latter case we might still - * have an orconn active (which connection_free_all() will get to later), - * so we should null out its channel pointer now. - */ - -static void -channel_tls_free_method(channel_t *chan) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - - if (tlschan->conn) { - tlschan->conn->chan = NULL; - tlschan->conn = NULL; - } -} - -/** - * Get the remote address of a channel_tls_t - * - * This implements the get_remote_addr method for channel_tls_t; copy the - * remote endpoint of the channel to addr_out and return 1 (always - * succeeds for this transport). - */ - -static int -channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) -{ - int rv = 0; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - tor_assert(addr_out); - - if (tlschan->conn) { - tor_addr_copy(addr_out, &(TO_CONN(tlschan->conn)->addr)); - rv = 1; - } else tor_addr_make_unspec(addr_out); - - return rv; -} - -/** - * Get the name of the pluggable transport used by a channel_tls_t. - * - * This implements the get_transport_name for channel_tls_t. If the - * channel uses a pluggable transport, copy its name to - * transport_out and return 0. If the channel did not use a - * pluggable transport, return -1. */ - -static int -channel_tls_get_transport_name_method(channel_t *chan, char **transport_out) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - tor_assert(transport_out); - tor_assert(tlschan->conn); - - if (!tlschan->conn->ext_or_transport) - return -1; - - *transport_out = tor_strdup(tlschan->conn->ext_or_transport); - return 0; -} - -/** - * Get endpoint description of a channel_tls_t - * - * This implements the get_remote_descr method for channel_tls_t; it returns - * a text description of the remote endpoint of the channel suitable for use - * in log messages. The req parameter is 0 for the canonical address or 1 for - * the actual address seen. - */ - -static const char * -channel_tls_get_remote_descr_method(channel_t *chan, int flags) -{ -#define MAX_DESCR_LEN 32 - - static char buf[MAX_DESCR_LEN + 1]; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - connection_t *conn; - const char *answer = NULL; - char *addr_str; - - tor_assert(tlschan); - - if (tlschan->conn) { - conn = TO_CONN(tlschan->conn); - switch (flags) { - case 0: - /* Canonical address with port*/ - tor_snprintf(buf, MAX_DESCR_LEN + 1, - "%s:%u", conn->address, conn->port); - answer = buf; - break; - case GRD_FLAG_ORIGINAL: - /* Actual address with port */ - addr_str = tor_dup_addr(&(tlschan->conn->real_addr)); - tor_snprintf(buf, MAX_DESCR_LEN + 1, - "%s:%u", addr_str, conn->port); - tor_free(addr_str); - answer = buf; - break; - case GRD_FLAG_ADDR_ONLY: - /* Canonical address, no port */ - strlcpy(buf, conn->address, sizeof(buf)); - answer = buf; - break; - case GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY: - /* Actual address, no port */ - addr_str = tor_dup_addr(&(tlschan->conn->real_addr)); - strlcpy(buf, addr_str, sizeof(buf)); - tor_free(addr_str); - answer = buf; - break; - default: - /* Something's broken in channel.c */ - tor_assert(1); - } - } else { - strlcpy(buf, "(No connection)", sizeof(buf)); - answer = buf; - } - - return answer; -} - -/** - * Tell the upper layer if we have queued writes - * - * This implements the has_queued_writes method for channel_tls t_; it returns - * 1 iff we have queued writes on the outbuf of the underlying or_connection_t. - */ - -static int -channel_tls_has_queued_writes_method(channel_t *chan) -{ - size_t outbuf_len; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - if (!(tlschan->conn)) { - log_info(LD_CHANNEL, - "something called has_queued_writes on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - } - - outbuf_len = (tlschan->conn != NULL) ? - connection_get_outbuf_len(TO_CONN(tlschan->conn)) : - 0; - - return (outbuf_len > 0); -} - -/** - * Tell the upper layer if we're canonical - * - * This implements the is_canonical method for channel_tls_t; if req is zero, - * it returns whether this is a canonical channel, and if it is one it returns - * whether that can be relied upon. - */ - -static int -channel_tls_is_canonical_method(channel_t *chan, int req) -{ - int answer = 0; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - - if (tlschan->conn) { - switch (req) { - case 0: - answer = tlschan->conn->is_canonical; - break; - case 1: - /* - * Is the is_canonical bit reliable? In protocols version 2 and up - * we get the canonical address from a NETINFO cell, but in older - * versions it might be based on an obsolete descriptor. - */ - answer = (tlschan->conn->link_proto >= 2); - break; - default: - /* This shouldn't happen; channel.c is broken if it does */ - tor_assert(1); - } - } - /* else return 0 for tlschan->conn == NULL */ - - return answer; -} - -/** - * Check if we match an extend_info_t - * - * This implements the matches_extend_info method for channel_tls_t; the upper - * layer wants to know if this channel matches an extend_info_t. - */ - -static int -channel_tls_matches_extend_info_method(channel_t *chan, - extend_info_t *extend_info) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - tor_assert(extend_info); - - /* Never match if we have no conn */ - if (!(tlschan->conn)) { - log_info(LD_CHANNEL, - "something called matches_extend_info on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - return 0; - } - - return (tor_addr_eq(&(extend_info->addr), - &(TO_CONN(tlschan->conn)->addr)) && - (extend_info->port == TO_CONN(tlschan->conn)->port)); -} - -/** - * Check if we match a target address; return true iff we do. - * - * This implements the matches_target method for channel_tls t_; the upper - * layer wants to know if this channel matches a target address when extending - * a circuit. - */ - -static int -channel_tls_matches_target_method(channel_t *chan, - const tor_addr_t *target) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - - tor_assert(tlschan); - tor_assert(target); - - /* Never match if we have no conn */ - if (!(tlschan->conn)) { - log_info(LD_CHANNEL, - "something called matches_target on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - return 0; - } - - return tor_addr_eq(&(tlschan->conn->real_addr), target); -} - -/** - * Write a cell to a channel_tls_t - * - * This implements the write_cell method for channel_tls_t; given a - * channel_tls_t and a cell_t, transmit the cell_t. - */ - -static int -channel_tls_write_cell_method(channel_t *chan, cell_t *cell) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - int written = 0; - - tor_assert(tlschan); - tor_assert(cell); - - if (tlschan->conn) { - connection_or_write_cell_to_buf(cell, tlschan->conn); - ++written; - } else { - log_info(LD_CHANNEL, - "something called write_cell on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - } - - return written; -} - -/** - * Write a packed cell to a channel_tls_t - * - * This implements the write_packed_cell method for channel_tls_t; given a - * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. - */ - -static int -channel_tls_write_packed_cell_method(channel_t *chan, - packed_cell_t *packed_cell) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids); - int written = 0; - - tor_assert(tlschan); - tor_assert(packed_cell); - - if (tlschan->conn) { - connection_write_to_buf(packed_cell->body, cell_network_size, - TO_CONN(tlschan->conn)); - - /* This is where the cell is finished; used to be done from relay.c */ - packed_cell_free(packed_cell); - ++written; - } else { - log_info(LD_CHANNEL, - "something called write_packed_cell on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - } - - return written; -} - -/** - * Write a variable-length cell to a channel_tls_t - * - * This implements the write_var_cell method for channel_tls_t; given a - * channel_tls_t and a var_cell_t, transmit the var_cell_t. - */ - -static int -channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) -{ - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - int written = 0; - - tor_assert(tlschan); - tor_assert(var_cell); - - if (tlschan->conn) { - connection_or_write_var_cell_to_buf(var_cell, tlschan->conn); - ++written; - } else { - log_info(LD_CHANNEL, - "something called write_var_cell on a tlschan " - "(%p with ID " U64_FORMAT " but no conn", - chan, U64_PRINTF_ARG(chan->global_identifier)); - } - - return written; -} - -/************************************************* - * Method implementations for channel_listener_t * - ************************************************/ - -/** - * Close a channel_listener_t - * - * This implements the close method for channel_listener_t - */ - -static void -channel_tls_listener_close_method(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - /* - * Listeners we just go ahead and change state through to CLOSED, but - * make sure to check if they're channel_tls_listener to NULL it out. - */ - if (chan_l == channel_tls_listener) - channel_tls_listener = NULL; - - if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); - } - - if (chan_l->incoming_list) { - SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list, - channel_t *, ichan) { - channel_mark_for_close(ichan); - } SMARTLIST_FOREACH_END(ichan); - - smartlist_free(chan_l->incoming_list); - chan_l->incoming_list = NULL; - } - - if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); - } -} - -/** - * Describe the transport for a channel_listener_t - * - * This returns the string "TLS channel (listening)" to the upper - * layer. - */ - -static const char * -channel_tls_listener_describe_transport_method(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return "TLS channel (listening)"; -} - -/******************************************************* - * Functions for handling events on an or_connection_t * - ******************************************************/ - -/** - * Handle an orconn state change - * - * This function will be called by connection_or.c when the or_connection_t - * associated with this channel_tls_t changes state. - */ - -void -channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, - or_connection_t *conn, - uint8_t old_state, - uint8_t state) -{ - channel_t *base_chan; - - tor_assert(chan); - tor_assert(conn); - tor_assert(conn->chan == chan); - tor_assert(chan->conn == conn); - /* -Werror appeasement */ - tor_assert(old_state == old_state); - - base_chan = TLS_CHAN_TO_BASE(chan); - - /* Make sure the base connection state makes sense - shouldn't be error, - * closed or listening. */ - - tor_assert(base_chan->state == CHANNEL_STATE_OPENING || - base_chan->state == CHANNEL_STATE_OPEN || - base_chan->state == CHANNEL_STATE_MAINT || - base_chan->state == CHANNEL_STATE_CLOSING); - - /* Did we just go to state open? */ - if (state == OR_CONN_STATE_OPEN) { - /* - * We can go to CHANNEL_STATE_OPEN from CHANNEL_STATE_OPENING or - * CHANNEL_STATE_MAINT on this. - */ - channel_change_state(base_chan, CHANNEL_STATE_OPEN); - } else { - /* - * Not open, so from CHANNEL_STATE_OPEN we go to CHANNEL_STATE_MAINT, - * otherwise no change. - */ - if (base_chan->state == CHANNEL_STATE_OPEN) { - channel_change_state(base_chan, CHANNEL_STATE_MAINT); - } - } -} - -/** - * Flush cells from a channel_tls_t - * - * Try to flush up to about num_cells cells, and return how many we flushed. - */ - -ssize_t -channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells) -{ - ssize_t flushed = 0; - - tor_assert(chan); - - if (flushed >= num_cells) goto done; - - /* - * If channel_tls_t ever buffers anything below the channel_t layer, flush - * that first here. - */ - - flushed += channel_flush_some_cells(TLS_CHAN_TO_BASE(chan), - num_cells - flushed); - - /* - * If channel_tls_t ever buffers anything below the channel_t layer, check - * how much we actually got and push it on down here. - */ - - done: - return flushed; -} - -/** - * Check if a channel_tls_t has anything to flush - * - * Return true if there is any more to flush on this channel (cells in queue - * or active circuits). - */ - -int -channel_tls_more_to_flush(channel_tls_t *chan) -{ - tor_assert(chan); - - /* - * If channel_tls_t ever buffers anything below channel_t, the - * check for that should go here first. - */ - - return channel_more_to_flush(TLS_CHAN_TO_BASE(chan)); -} - -#ifdef KEEP_TIMING_STATS - -/** - * Timing states wrapper - * - * This is a wrapper function around the actual function that processes the - * cell that just arrived on chan. Increment *time - * by the number of microseconds used by the call to *func(cell, chan). - */ - -static void -channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, - void (*func)(cell_t *, channel_tls_t *)) -{ - struct timeval start, end; - long time_passed; - - tor_gettimeofday(&start); - - (*func)(cell, chan); - - tor_gettimeofday(&end); - time_passed = tv_udiff(&start, &end) ; - - if (time_passed > 10000) { /* more than 10ms */ - log_debug(LD_OR,"That call just took %ld ms.",time_passed/1000); - } - - if (time_passed < 0) { - log_info(LD_GENERAL,"That call took us back in time!"); - time_passed = 0; - } - - *time += time_passed; -} -#endif - -/** - * Handle an incoming cell on a channel_tls_t - * - * This is called from connection_or.c to handle an arriving cell; it checks - * for cell types specific to the handshake for this transport protocol and - * handles them, and queues all other cells to the channel_t layer, which - * eventually will hand them off to command.c. - */ - -void -channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) -{ - channel_tls_t *chan; - int handshaking; - -#ifdef KEEP_TIMING_STATS -#define PROCESS_CELL(tp, cl, cn) STMT_BEGIN { \ - ++num ## tp; \ - channel_tls_time_process_cell(cl, cn, & tp ## time , \ - channel_tls_process_ ## tp ## _cell); \ - } STMT_END -#else -#define PROCESS_CELL(tp, cl, cn) channel_tls_process_ ## tp ## _cell(cl, cn) -#endif - - tor_assert(cell); - tor_assert(conn); - - chan = conn->chan; - - if (!chan) { - log_warn(LD_CHANNEL, - "Got a cell_t on an OR connection with no channel"); - return; - } - - handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); - - if (conn->base_.marked_for_close) - return; - - /* Reject all but VERSIONS and NETINFO when handshaking. */ - /* (VERSIONS should actually be impossible; it's variable-length.) */ - if (handshaking && cell->command != CELL_VERSIONS && - cell->command != CELL_NETINFO) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received unexpected cell command %d in chan state %s / " - "conn state %s; closing the connection.", - (int)cell->command, - channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), - conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state)); - connection_or_close_for_error(conn, 0); - return; - } - - if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) - or_handshake_state_record_cell(conn, conn->handshake_state, cell, 1); - - switch (cell->command) { - case CELL_PADDING: - ++stats_n_padding_cells_processed; - /* do nothing */ - break; - case CELL_VERSIONS: - tor_fragile_assert(); - break; - case CELL_NETINFO: - ++stats_n_netinfo_cells_processed; - PROCESS_CELL(netinfo, cell, chan); - break; - case CELL_CREATE: - case CELL_CREATE_FAST: - case CELL_CREATED: - case CELL_CREATED_FAST: - case CELL_RELAY: - case CELL_RELAY_EARLY: - case CELL_DESTROY: - case CELL_CREATE2: - case CELL_CREATED2: - /* - * These are all transport independent and we pass them up through the - * channel_t mechanism. They are ultimately handled in command.c. - */ - channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell); - break; - default: - log_fn(LOG_INFO, LD_PROTOCOL, - "Cell of unknown type (%d) received in channeltls.c. " - "Dropping.", - cell->command); - break; - } -} - -/** - * Handle an incoming variable-length cell on a channel_tls_t - * - * Process a var_cell that was just received on conn. Keep - * internal statistics about how many of each cell we've processed so far - * this second, and the total number of microseconds it took to - * process each type of cell. All the var_cell commands are handshake- - * related and live below the channel_t layer, so no variable-length - * cells ever get delivered in the current implementation, but I've left - * the mechanism in place for future use. - */ - -void -channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) -{ - channel_tls_t *chan; - -#ifdef KEEP_TIMING_STATS - /* how many of each cell have we seen so far this second? needs better - * name. */ - static int num_versions = 0, num_certs = 0; - static time_t current_second = 0; /* from previous calls to time */ - time_t now = time(NULL); - - if (current_second == 0) current_second = now; - if (now > current_second) { /* the second has rolled over */ - /* print stats */ - log_info(LD_OR, - "At end of second: %d versions (%d ms), %d certs (%d ms)", - num_versions, versions_time / ((now - current_second) * 1000), - num_certs, certs_time / ((now - current_second) * 1000)); - - num_versions = num_certs = 0; - versions_time = certs_time = 0; - - /* remember which second it is, for next time */ - current_second = now; - } -#endif - - tor_assert(var_cell); - tor_assert(conn); - - chan = conn->chan; - - if (!chan) { - log_warn(LD_CHANNEL, - "Got a var_cell_t on an OR connection with no channel"); - return; - } - - if (TO_CONN(conn)->marked_for_close) - return; - - switch (TO_CONN(conn)->state) { - case OR_CONN_STATE_OR_HANDSHAKING_V2: - if (var_cell->command != CELL_VERSIONS) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a cell with command %d in unexpected " - "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " - "closing the connection.", - (int)(var_cell->command), - conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), - TO_CONN(conn)->state, - channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), - (int)(TLS_CHAN_TO_BASE(chan)->state)); - /* - * The code in connection_or.c will tell channel_t to close for - * error; it will go to CHANNEL_STATE_CLOSING, and then to - * CHANNEL_STATE_ERROR when conn is closed. - */ - connection_or_close_for_error(conn, 0); - return; - } - break; - case OR_CONN_STATE_TLS_HANDSHAKING: - /* If we're using bufferevents, it's entirely possible for us to - * notice "hey, data arrived!" before we notice "hey, the handshake - * finished!" And we need to be accepting both at once to handle both - * the v2 and v3 handshakes. */ - - /* fall through */ - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - if (!(command_allowed_before_handshake(var_cell->command))) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a cell with command %d in unexpected " - "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " - "closing the connection.", - (int)(var_cell->command), - conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), - (int)(TO_CONN(conn)->state), - channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), - (int)(TLS_CHAN_TO_BASE(chan)->state)); - /* see above comment about CHANNEL_STATE_ERROR */ - connection_or_close_for_error(conn, 0); - return; - } else { - if (enter_v3_handshake_with_cell(var_cell, chan) < 0) - return; - } - break; - case OR_CONN_STATE_OR_HANDSHAKING_V3: - if (var_cell->command != CELL_AUTHENTICATE) - or_handshake_state_record_var_cell(conn, conn->handshake_state, - var_cell, 1); - break; /* Everything is allowed */ - case OR_CONN_STATE_OPEN: - if (conn->link_proto < 3) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a variable-length cell with command %d in orconn " - "state %s [%d], channel state %s [%d] with link protocol %d; " - "ignoring it.", - (int)(var_cell->command), - conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), - (int)(TO_CONN(conn)->state), - channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), - (int)(TLS_CHAN_TO_BASE(chan)->state), - (int)(conn->link_proto)); - return; - } - break; - default: - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received var-length cell with command %d in unexpected " - "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " - "ignoring it.", - (int)(var_cell->command), - conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), - (int)(TO_CONN(conn)->state), - channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), - (int)(TLS_CHAN_TO_BASE(chan)->state)); - return; - } - - /* Now handle the cell */ - - switch (var_cell->command) { - case CELL_VERSIONS: - ++stats_n_versions_cells_processed; - PROCESS_CELL(versions, var_cell, chan); - break; - case CELL_VPADDING: - ++stats_n_vpadding_cells_processed; - /* Do nothing */ - break; - case CELL_CERTS: - ++stats_n_certs_cells_processed; - PROCESS_CELL(certs, var_cell, chan); - break; - case CELL_AUTH_CHALLENGE: - ++stats_n_auth_challenge_cells_processed; - PROCESS_CELL(auth_challenge, var_cell, chan); - break; - case CELL_AUTHENTICATE: - ++stats_n_authenticate_cells_processed; - PROCESS_CELL(authenticate, var_cell, chan); - break; - case CELL_AUTHORIZE: - ++stats_n_authorize_cells_processed; - /* Ignored so far. */ - break; - default: - log_fn(LOG_INFO, LD_PROTOCOL, - "Variable-length cell of unknown type (%d) received.", - (int)(var_cell->command)); - break; - } -} - -/** - * Check if this cell type is allowed before the handshake is finished - * - * Return true if command is a cell command that's allowed to start a - * V3 handshake. - */ - -static int -command_allowed_before_handshake(uint8_t command) -{ - switch (command) { - case CELL_VERSIONS: - case CELL_VPADDING: - case CELL_AUTHORIZE: - return 1; - default: - return 0; - } -} - -/** - * Start a V3 handshake on an incoming connection - * - * Called when we as a server receive an appropriate cell while waiting - * either for a cell or a TLS handshake. Set the connection's state to - * "handshaking_v3', initializes the or_handshake_state field as needed, - * and add the cell to the hash of incoming cells.) - */ - -static int -enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) -{ - int started_here = 0; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - - started_here = connection_or_nonopen_was_started_here(chan->conn); - - tor_assert(TO_CONN(chan->conn)->state == OR_CONN_STATE_TLS_HANDSHAKING || - TO_CONN(chan->conn)->state == - OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); - - if (started_here) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a cell while TLS-handshaking, not in " - "OR_HANDSHAKING_V3, on a connection we originated."); - } - connection_or_block_renegotiation(chan->conn); - chan->conn->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; - if (connection_init_or_handshake_state(chan->conn, started_here) < 0) { - connection_or_close_for_error(chan->conn, 0); - return -1; - } - or_handshake_state_record_var_cell(chan->conn, - chan->conn->handshake_state, cell, 1); - return 0; -} - -/** - * Process a 'versions' cell. - * - * This function is called to handle an incoming VERSIONS cell; the current - * link protocol version must be 0 to indicate that no version has yet been - * negotiated. We compare the versions in the cell to the list of versions - * we support, pick the highest version we have in common, and continue the - * negotiation from there. - */ - -static void -channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) -{ - int highest_supported_version = 0; - const uint8_t *cp, *end; - int started_here = 0; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - - if ((cell->payload_len % 2) == 1) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a VERSION cell with odd payload length %d; " - "closing connection.",cell->payload_len); - connection_or_close_for_error(chan->conn, 0); - return; - } - - started_here = connection_or_nonopen_was_started_here(chan->conn); - - if (chan->conn->link_proto != 0 || - (chan->conn->handshake_state && - chan->conn->handshake_state->received_versions)) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a VERSIONS cell on a connection with its version " - "already set to %d; dropping", - (int)(chan->conn->link_proto)); - return; - } - switch (chan->conn->base_.state) - { - case OR_CONN_STATE_OR_HANDSHAKING_V2: - case OR_CONN_STATE_OR_HANDSHAKING_V3: - break; - case OR_CONN_STATE_TLS_HANDSHAKING: - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - default: - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "VERSIONS cell while in unexpected state"); - return; - } - - tor_assert(chan->conn->handshake_state); - end = cell->payload + cell->payload_len; - for (cp = cell->payload; cp+1 < end; cp += 2) { - uint16_t v = ntohs(get_uint16(cp)); - if (is_or_protocol_version_known(v) && v > highest_supported_version) - highest_supported_version = v; - } - if (!highest_supported_version) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Couldn't find a version in common between my version list and the " - "list in the VERSIONS cell; closing connection."); - connection_or_close_for_error(chan->conn, 0); - return; - } else if (highest_supported_version == 1) { - /* Negotiating version 1 makes no sense, since version 1 has no VERSIONS - * cells. */ - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Used version negotiation protocol to negotiate a v1 connection. " - "That's crazily non-compliant. Closing connection."); - connection_or_close_for_error(chan->conn, 0); - return; - } else if (highest_supported_version < 3 && - chan->conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Negotiated link protocol 2 or lower after doing a v3 TLS " - "handshake. Closing connection."); - connection_or_close_for_error(chan->conn, 0); - return; - } else if (highest_supported_version != 2 && - chan->conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V2) { - /* XXXX This should eventually be a log_protocol_warn */ - log_fn(LOG_WARN, LD_OR, - "Negotiated link with non-2 protocol after doing a v2 TLS " - "handshake with %s. Closing connection.", - fmt_addr(&chan->conn->base_.addr)); - connection_or_close_for_error(chan->conn, 0); - return; - } - - chan->conn->link_proto = highest_supported_version; - chan->conn->handshake_state->received_versions = 1; - - if (chan->conn->link_proto == 2) { - log_info(LD_OR, - "Negotiated version %d with %s:%d; sending NETINFO.", - highest_supported_version, - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port); - - if (connection_or_send_netinfo(chan->conn) < 0) { - connection_or_close_for_error(chan->conn, 0); - return; - } - } else { - const int send_versions = !started_here; - /* If we want to authenticate, send a CERTS cell */ - const int send_certs = !started_here || public_server_mode(get_options()); - /* If we're a host that got a connection, ask for authentication. */ - const int send_chall = !started_here; - /* If our certs cell will authenticate us, we can send a netinfo cell - * right now. */ - const int send_netinfo = !started_here; - const int send_any = - send_versions || send_certs || send_chall || send_netinfo; - tor_assert(chan->conn->link_proto >= 3); - - log_info(LD_OR, - "Negotiated version %d with %s:%d; %s%s%s%s%s", - highest_supported_version, - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port, - send_any ? "Sending cells:" : "Waiting for CERTS cell", - send_versions ? " VERSIONS" : "", - send_certs ? " CERTS" : "", - send_chall ? " AUTH_CHALLENGE" : "", - send_netinfo ? " NETINFO" : ""); - -#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE - if (1) { - connection_or_close_normally(chan->conn, 1); - return; - } -#endif - - if (send_versions) { - if (connection_or_send_versions(chan->conn, 1) < 0) { - log_warn(LD_OR, "Couldn't send versions cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - } - - /* We set this after sending the verions cell. */ - /*XXXXX symbolic const.*/ - chan->base_.wide_circ_ids = - chan->conn->link_proto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS; - chan->conn->wide_circ_ids = chan->base_.wide_circ_ids; - - if (send_certs) { - if (connection_or_send_certs_cell(chan->conn) < 0) { - log_warn(LD_OR, "Couldn't send certs cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - } - if (send_chall) { - if (connection_or_send_auth_challenge_cell(chan->conn) < 0) { - log_warn(LD_OR, "Couldn't send auth_challenge cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - } - if (send_netinfo) { - if (connection_or_send_netinfo(chan->conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - } - } -} - -/** - * Process a 'netinfo' cell - * - * This function is called to handle an incoming NETINFO cell; read and act - * on its contents, and set the connection state to "open". - */ - -static void -channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) -{ - time_t timestamp; - uint8_t my_addr_type; - uint8_t my_addr_len; - const uint8_t *my_addr_ptr; - const uint8_t *cp, *end; - uint8_t n_other_addrs; - time_t now = time(NULL); - - long apparent_skew = 0; - tor_addr_t my_apparent_addr = TOR_ADDR_NULL; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - - if (chan->conn->link_proto < 2) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on %s connection; dropping.", - chan->conn->link_proto == 0 ? "non-versioned" : "a v1"); - return; - } - if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && - chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on non-handshaking connection; dropping."); - return; - } - tor_assert(chan->conn->handshake_state && - chan->conn->handshake_state->received_versions); - - if (chan->conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { - tor_assert(chan->conn->link_proto >= 3); - if (chan->conn->handshake_state->started_here) { - if (!(chan->conn->handshake_state->authenticated)) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Got a NETINFO cell from server, " - "but no authentication. Closing the connection."); - connection_or_close_for_error(chan->conn, 0); - return; - } - } else { - /* we're the server. If the client never authenticated, we have - some housekeeping to do.*/ - if (!(chan->conn->handshake_state->authenticated)) { - tor_assert(tor_digest_is_zero( - (const char*)(chan->conn->handshake_state-> - authenticated_peer_id))); - channel_set_circid_type(TLS_CHAN_TO_BASE(chan), NULL, - chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); - - connection_or_init_conn_from_address(chan->conn, - &(chan->conn->base_.addr), - chan->conn->base_.port, - (const char*)(chan->conn->handshake_state-> - authenticated_peer_id), - 0); - } - } - } - - /* Decode the cell. */ - timestamp = ntohl(get_uint32(cell->payload)); - if (labs(now - chan->conn->handshake_state->sent_versions_at) < 180) { - apparent_skew = now - timestamp; - } - - my_addr_type = (uint8_t) cell->payload[4]; - my_addr_len = (uint8_t) cell->payload[5]; - my_addr_ptr = (uint8_t*) cell->payload + 6; - end = cell->payload + CELL_PAYLOAD_SIZE; - cp = cell->payload + 6 + my_addr_len; - - /* We used to check: - * if (my_addr_len >= CELL_PAYLOAD_SIZE - 6) { - * - * This is actually never going to happen, since my_addr_len is at most 255, - * and CELL_PAYLOAD_LEN - 6 is 503. So we know that cp is < end. */ - - if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) { - 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++; - while (n_other_addrs && cp < end-2) { - /* Consider all the other addresses; if any matches, this connection is - * "canonical." */ - tor_addr_t addr; - const uint8_t *next = - decode_address_from_payload(&addr, cp, (int)(end-cp)); - if (next == NULL) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Bad address in netinfo cell; closing connection."); - connection_or_close_for_error(chan->conn, 0); - return; - } - if (tor_addr_eq(&addr, &(chan->conn->real_addr))) { - chan->conn->is_canonical = 1; - break; - } - cp = next; - --n_other_addrs; - } - - /* Act on apparent skew. */ - /** Warn when we get a netinfo skew with at least this value. */ -#define NETINFO_NOTICE_SKEW 3600 - if (labs(apparent_skew) > NETINFO_NOTICE_SKEW && - router_get_by_id_digest(chan->conn->identity_digest)) { - char dbuf[64]; - int severity; - /*XXXX be smarter about when everybody says we are skewed. */ - if (router_digest_is_trusted_dir(chan->conn->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_INFO; - format_time_interval(dbuf, sizeof(dbuf), apparent_skew); - log_fn(severity, LD_GENERAL, - "Received NETINFO cell with skewed time from " - "server at %s:%d. It seems that our clock is %s by %s, or " - "that theirs is %s. Tor requires an accurate clock to work: " - "please check your time and date settings.", - chan->conn->base_.address, - (int)(chan->conn->base_.port), - apparent_skew > 0 ? "ahead" : "behind", - dbuf, - apparent_skew > 0 ? "behind" : "ahead"); - if (severity == LOG_WARN) /* only tell the controller if an authority */ - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", - apparent_skew, - chan->conn->base_.address, - chan->conn->base_.port); - } - - /* XXX maybe act on my_apparent_addr, if the source is sufficiently - * trustworthy. */ - - if (! chan->conn->handshake_state->sent_netinfo) { - /* If we were prepared to authenticate, but we never got an AUTH_CHALLENGE - * cell, then we would not previously have sent a NETINFO cell. Do so - * now. */ - if (connection_or_send_netinfo(chan->conn) < 0) { - connection_or_close_for_error(chan->conn, 0); - return; - } - } - - if (connection_or_set_state_open(chan->conn) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Got good NETINFO cell from %s:%d; but " - "was unable to make the OR connection become open.", - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port); - connection_or_close_for_error(chan->conn, 0); - } 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. " - "Our address is apparently %s.", - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port, - (int)(chan->conn->link_proto), - hex_str(TLS_CHAN_TO_BASE(chan)->identity_digest, - DIGEST_LEN), - tor_addr_is_null(&my_apparent_addr) ? - "" : fmt_and_decorate_addr(&my_apparent_addr)); - } - assert_connection_ok(TO_CONN(chan->conn),time(NULL)); -} - -/** - * Process a CERTS cell from a channel. - * - * This function is called to process an incoming CERTS cell on a - * channel_tls_t: - * - * If the other side should not have sent us a CERTS cell, or the cell is - * malformed, or it is supposed to authenticate the TLS key but it doesn't, - * then mark the connection. - * - * If the cell has a good cert chain and we're doing a v3 handshake, then - * store the certificates in or_handshake_state. If this is the client side - * of the connection, we then authenticate the server or mark the connection. - * If it's the server side, wait for an AUTHENTICATE cell. - */ - -static void -channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) -{ - tor_cert_t *link_cert = NULL; - tor_cert_t *id_cert = NULL; - tor_cert_t *auth_cert = NULL; - uint8_t *ptr; - int n_certs, i; - int send_netinfo = 0; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad CERTS cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ - connection_or_close_for_error(chan->conn, 0); \ - goto err; \ - } while (0) - - if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not doing a v3 handshake!"); - if (chan->conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (chan->conn->handshake_state->received_certs_cell) - ERR("We already got one"); - if (chan->conn->handshake_state->authenticated) { - /* Should be unreachable, but let's make sure. */ - ERR("We're already authenticated!"); - } - if (cell->payload_len < 1) - ERR("It had no body"); - if (cell->circ_id) - ERR("It had a nonzero circuit ID"); - - n_certs = cell->payload[0]; - ptr = cell->payload + 1; - for (i = 0; i < n_certs; ++i) { - uint8_t cert_type; - uint16_t cert_len; - if (ptr + 3 > cell->payload + cell->payload_len) { - goto truncated; - } - cert_type = *ptr; - cert_len = ntohs(get_uint16(ptr+1)); - if (ptr + 3 + cert_len > cell->payload + cell->payload_len) { - goto truncated; - } - if (cert_type == OR_CERT_TYPE_TLS_LINK || - cert_type == OR_CERT_TYPE_ID_1024 || - cert_type == OR_CERT_TYPE_AUTH_1024) { - tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); - if (!cert) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received undecodable certificate in CERTS cell from %s:%d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); - } else { - if (cert_type == OR_CERT_TYPE_TLS_LINK) { - if (link_cert) { - tor_cert_free(cert); - ERR("Too many TLS_LINK certificates"); - } - link_cert = cert; - } else if (cert_type == OR_CERT_TYPE_ID_1024) { - if (id_cert) { - tor_cert_free(cert); - ERR("Too many ID_1024 certificates"); - } - id_cert = cert; - } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { - if (auth_cert) { - tor_cert_free(cert); - ERR("Too many AUTH_1024 certificates"); - } - auth_cert = cert; - } else { - tor_cert_free(cert); - } - } - } - ptr += 3 + cert_len; - continue; - - truncated: - ERR("It ends in the middle of a certificate"); - } - - if (chan->conn->handshake_state->started_here) { - int severity; - if (! (id_cert && link_cert)) - ERR("The certs we wanted were missing"); - /* Okay. We should be able to check the certificates now. */ - if (! tor_tls_cert_matches_key(chan->conn->tls, link_cert)) { - ERR("The link certificate didn't match the TLS public key"); - } - /* Note that this warns more loudly about time and validity if we were - * _trying_ to connect to an authority, not necessarily if we _did_ connect - * to one. */ - if (router_digest_is_trusted_dir( - TLS_CHAN_TO_BASE(chan)->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_PROTOCOL_WARN; - - if (! tor_tls_cert_is_valid(severity, link_cert, id_cert, 0)) - ERR("The link certificate was not valid"); - if (! tor_tls_cert_is_valid(severity, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); - - chan->conn->handshake_state->authenticated = 1; - { - const digests_t *id_digests = tor_cert_get_id_digests(id_cert); - crypto_pk_t *identity_rcvd; - if (!id_digests) - ERR("Couldn't compute digests for key in ID cert"); - - identity_rcvd = tor_tls_cert_get_key(id_cert); - if (!identity_rcvd) - ERR("Internal error: Couldn't get RSA key from ID cert."); - memcpy(chan->conn->handshake_state->authenticated_peer_id, - id_digests->d[DIGEST_SHA1], DIGEST_LEN); - channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd, - chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); - crypto_pk_free(identity_rcvd); - } - - if (connection_or_client_learned_peer_id(chan->conn, - chan->conn->handshake_state->authenticated_peer_id) < 0) - ERR("Problem setting or checking peer id"); - - log_info(LD_OR, - "Got some good certificates from %s:%d: Authenticated it.", - safe_str(chan->conn->base_.address), chan->conn->base_.port); - - chan->conn->handshake_state->id_cert = id_cert; - id_cert = NULL; - - if (!public_server_mode(get_options())) { - /* If we initiated the connection and we are not a public server, we - * aren't planning to authenticate at all. At this point we know who we - * are talking to, so we can just send a netinfo now. */ - send_netinfo = 1; - } - } else { - if (! (id_cert && auth_cert)) - ERR("The certs we wanted were missing"); - - /* Remember these certificates so we can check an AUTHENTICATE cell */ - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, auth_cert, id_cert, 1)) - ERR("The authentication certificate was not valid"); - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); - - log_info(LD_OR, - "Got some good certificates from %s:%d: " - "Waiting for AUTHENTICATE.", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); - /* XXXX check more stuff? */ - - chan->conn->handshake_state->id_cert = id_cert; - chan->conn->handshake_state->auth_cert = auth_cert; - id_cert = auth_cert = NULL; - } - - chan->conn->handshake_state->received_certs_cell = 1; - - if (send_netinfo) { - if (connection_or_send_netinfo(chan->conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_or_close_for_error(chan->conn, 0); - goto err; - } - } - - err: - tor_cert_free(id_cert); - tor_cert_free(link_cert); - tor_cert_free(auth_cert); -#undef ERR -} - -/** - * Process an AUTH_CHALLENGE cell from a channel_tls_t - * - * This function is called to handle an incoming AUTH_CHALLENGE cell on a - * channel_tls_t; if we weren't supposed to get one (for example, because we're - * not the originator of the channel), or it's ill-formed, or we aren't doing - * a v3 handshake, mark the channel. If the cell is well-formed but we don't - * want to authenticate, just drop it. If the cell is well-formed *and* we - * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. - */ - -static void -channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) -{ - int n_types, i, use_type = -1; - uint8_t *cp; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ - connection_or_close_for_error(chan->conn, 0); \ - return; \ - } while (0) - - if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not currently doing a v3 handshake"); - if (chan->conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (!(chan->conn->handshake_state->started_here)) - ERR("We didn't originate this connection"); - if (chan->conn->handshake_state->received_auth_challenge) - ERR("We already received one"); - if (!(chan->conn->handshake_state->received_certs_cell)) - ERR("We haven't gotten a CERTS cell yet"); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) - ERR("It was too short"); - if (cell->circ_id) - ERR("It had a nonzero circuit ID"); - - n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) - ERR("It looks truncated"); - - /* Now see if there is an authentication type we can use */ - cp = cell->payload+OR_AUTH_CHALLENGE_LEN + 2; - for (i = 0; i < n_types; ++i, cp += 2) { - uint16_t authtype = ntohs(get_uint16(cp)); - if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) - use_type = authtype; - } - - chan->conn->handshake_state->received_auth_challenge = 1; - - if (! public_server_mode(get_options())) { - /* If we're not a public server then we don't want to authenticate on a - connection we originated, and we already sent a NETINFO cell when we - got the CERTS cell. We have nothing more to do. */ - return; - } - - if (use_type >= 0) { - log_info(LD_OR, - "Got an AUTH_CHALLENGE cell from %s:%d: Sending " - "authentication", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); - - if (connection_or_send_authenticate_cell(chan->conn, use_type) < 0) { - log_warn(LD_OR, - "Couldn't send authenticate cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - } else { - log_info(LD_OR, - "Got an AUTH_CHALLENGE cell from %s:%d, but we don't " - "know any of its authentication types. Not authenticating.", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); - } - - if (connection_or_send_netinfo(chan->conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_or_close_for_error(chan->conn, 0); - return; - } - -#undef ERR -} - -/** - * Process an AUTHENTICATE cell from a channel_tls_t - * - * If it's ill-formed or we weren't supposed to get one or we're not doing a - * v3 handshake, then mark the connection. If it does not authenticate the - * other side of the connection successfully (because it isn't signed right, - * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept - * the identity of the router on the other side of the connection. - */ - -static void -channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) -{ - uint8_t expected[V3_AUTH_FIXED_PART_LEN]; - const uint8_t *auth; - int authlen; - - tor_assert(cell); - tor_assert(chan); - tor_assert(chan->conn); - -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTHENTICATE cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ - connection_or_close_for_error(chan->conn, 0); \ - return; \ - } while (0) - - if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not doing a v3 handshake"); - if (chan->conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (chan->conn->handshake_state->started_here) - ERR("We originated this connection"); - if (chan->conn->handshake_state->received_authenticate) - ERR("We already got one!"); - if (chan->conn->handshake_state->authenticated) { - /* Should be impossible given other checks */ - ERR("The peer is already authenticated"); - } - if (!(chan->conn->handshake_state->received_certs_cell)) - ERR("We never got a certs cell"); - if (chan->conn->handshake_state->auth_cert == NULL) - ERR("We never got an authentication certificate"); - if (chan->conn->handshake_state->id_cert == NULL) - ERR("We never got an identity certificate"); - if (cell->payload_len < 4) - ERR("Cell was way too short"); - - auth = cell->payload; - { - uint16_t type = ntohs(get_uint16(auth)); - uint16_t len = ntohs(get_uint16(auth+2)); - if (4 + len > cell->payload_len) - ERR("Authenticator was truncated"); - - if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) - ERR("Authenticator type was not recognized"); - - auth += 4; - authlen = len; - } - - if (authlen < V3_AUTH_BODY_LEN + 1) - ERR("Authenticator was too short"); - - if (connection_or_compute_authenticate_cell_body( - chan->conn, expected, sizeof(expected), NULL, 1) < 0) - ERR("Couldn't compute expected AUTHENTICATE cell body"); - - if (tor_memneq(expected, auth, sizeof(expected))) - ERR("Some field in the AUTHENTICATE cell body was not as expected"); - - { - crypto_pk_t *pk = tor_tls_cert_get_key( - chan->conn->handshake_state->auth_cert); - char d[DIGEST256_LEN]; - char *signed_data; - size_t keysize; - int signed_len; - - if (!pk) - ERR("Internal error: couldn't get RSA key from AUTH cert."); - crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); - - keysize = crypto_pk_keysize(pk); - signed_data = tor_malloc(keysize); - signed_len = crypto_pk_public_checksig(pk, signed_data, keysize, - (char*)auth + V3_AUTH_BODY_LEN, - authlen - V3_AUTH_BODY_LEN); - crypto_pk_free(pk); - if (signed_len < 0) { - tor_free(signed_data); - ERR("Signature wasn't valid"); - } - if (signed_len < DIGEST256_LEN) { - tor_free(signed_data); - ERR("Not enough data was signed"); - } - /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here, - * in case they're later used to hold a SHA3 digest or something. */ - if (tor_memneq(signed_data, d, DIGEST256_LEN)) { - tor_free(signed_data); - ERR("Signature did not match data to be signed."); - } - tor_free(signed_data); - } - - /* Okay, we are authenticated. */ - chan->conn->handshake_state->received_authenticate = 1; - chan->conn->handshake_state->authenticated = 1; - chan->conn->handshake_state->digest_received_data = 0; - { - crypto_pk_t *identity_rcvd = - tor_tls_cert_get_key(chan->conn->handshake_state->id_cert); - const digests_t *id_digests = - tor_cert_get_id_digests(chan->conn->handshake_state->id_cert); - - /* This must exist; we checked key type when reading the cert. */ - tor_assert(id_digests); - - memcpy(chan->conn->handshake_state->authenticated_peer_id, - id_digests->d[DIGEST_SHA1], DIGEST_LEN); - - channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd, - chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); - crypto_pk_free(identity_rcvd); - - connection_or_init_conn_from_address(chan->conn, - &(chan->conn->base_.addr), - chan->conn->base_.port, - (const char*)(chan->conn->handshake_state-> - authenticated_peer_id), - 0); - - log_info(LD_OR, - "Got an AUTHENTICATE cell from %s:%d: Looks good.", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); - } - -#undef ERR -} - diff --git a/src/tor/channeltls.h b/src/tor/channeltls.h deleted file mode 100644 index b4a7e2b..0000000 --- a/src/tor/channeltls.h +++ /dev/null @@ -1,57 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file channeltls.h - * \brief Header file for channeltls.c - **/ - -#ifndef TOR_CHANNELTLS_H -#define TOR_CHANNELTLS_H - -#include "or.h" -#include "channel.h" - -#define BASE_CHAN_TO_TLS(c) (channel_tls_from_base((c))) -#define TLS_CHAN_TO_BASE(c) (channel_tls_to_base((c))) - -#define TLS_CHAN_MAGIC 0x8a192427U - -#ifdef TOR_CHANNEL_INTERNAL_ - -struct channel_tls_s { - /* Base channel_t struct */ - channel_t base_; - /* or_connection_t pointer */ - or_connection_t *conn; -}; - -#endif /* TOR_CHANNEL_INTERNAL_ */ - -channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest); -channel_listener_t * channel_tls_get_listener(void); -channel_listener_t * channel_tls_start_listener(void); -channel_t * channel_tls_handle_incoming(or_connection_t *orconn); - -/* Casts */ - -channel_t * channel_tls_to_base(channel_tls_t *tlschan); -channel_tls_t * channel_tls_from_base(channel_t *chan); - -/* Things for connection_or.c to call back into */ -ssize_t channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells); -int channel_tls_more_to_flush(channel_tls_t *chan); -void channel_tls_handle_cell(cell_t *cell, or_connection_t *conn); -void channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, - or_connection_t *conn, - uint8_t old_state, - uint8_t state); -void channel_tls_handle_var_cell(var_cell_t *var_cell, - or_connection_t *conn); - -/* Cleanup at shutdown */ -void channel_tls_free_all(void); - -#endif - diff --git a/src/tor/ciphers.inc b/src/tor/ciphers.inc deleted file mode 100644 index 137d78b..0000000 --- a/src/tor/ciphers.inc +++ /dev/null @@ -1,182 +0,0 @@ -/* This is an include file used to define the list of ciphers clients should - * advertise. Before including it, you should define the CIPHER and XCIPHER - * macros. - * - * This file was automatically generated by get_mozilla_ciphers.py. - */ -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA - CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA - CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) -#else - XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA - CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) -#else - XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) -#endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA - CIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA - CIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -#else - XCIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA - CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) -#else - XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA - CIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA - CIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA - CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA - CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) -#else - XCIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_AES_128_SHA - CIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA) -#else - XCIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA) -#endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA - CIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA - CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA - CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) -#else - XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA - CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -#else - XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_SEED_SHA - CIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) -#else - XCIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) -#endif -#ifdef SSL3_TXT_RSA_RC4_128_MD5 - CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) -#else - XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) -#endif -#ifdef SSL3_TXT_RSA_RC4_128_SHA - CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) -#else - XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_AES_128_SHA - CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) -#else - XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA) -#endif -#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) -#endif -#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA - CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) -#else - XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) -#endif -#ifdef SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA - CIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA) -#else - XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA) -#endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) -#endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) -#else - XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) -#endif -/* No openssl macro found for 0xfeff */ -#ifdef SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA - CIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) -#else - XCIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) -#endif -#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA - CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) -#else - XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA) -#endif diff --git a/src/tor/circpathbias.c b/src/tor/circpathbias.c deleted file mode 100644 index 51a75cf..0000000 --- a/src/tor/circpathbias.c +++ /dev/null @@ -1,1538 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "channel.h" -#include "circpathbias.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "circuitstats.h" -#include "connection_edge.h" -#include "config.h" -#include "entrynodes.h" -#include "networkstatus.h" -#include "relay.h" - -static void pathbias_count_successful_close(origin_circuit_t *circ); -static void pathbias_count_collapse(origin_circuit_t *circ); -static void pathbias_count_use_failed(origin_circuit_t *circ); -static void pathbias_measure_use_rate(entry_guard_t *guard); -static void pathbias_measure_close_rate(entry_guard_t *guard); -static void pathbias_scale_use_rates(entry_guard_t *guard); -static void pathbias_scale_close_rates(entry_guard_t *guard); -static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard); - -/** Increment the number of times we successfully extended a circuit to - * guard, first checking if the failure rate is high enough that - * we should eliminate the guard. Return -1 if the guard looks no good; - * return 0 if the guard looks fine. - */ -static int -entry_guard_inc_circ_attempt_count(entry_guard_t *guard) -{ - entry_guards_changed(); - - pathbias_measure_close_rate(guard); - - if (guard->path_bias_disabled) - return -1; - - pathbias_scale_close_rates(guard); - guard->circ_attempts++; - - log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); - return 0; -} - -/** The minimum number of circuit attempts before we start - * thinking about warning about path bias and dropping guards */ -static int -pathbias_get_min_circs(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_MIN_CIRC 150 - if (options->PathBiasCircThreshold >= 5) - return options->PathBiasCircThreshold; - else - return networkstatus_get_param(NULL, "pb_mincircs", - DFLT_PATH_BIAS_MIN_CIRC, - 5, INT32_MAX); -} - -/** The circuit success rate below which we issue a notice */ -static double -pathbias_get_notice_rate(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_NOTICE_PCT 70 - if (options->PathBiasNoticeRate >= 0.0) - return options->PathBiasNoticeRate; - else - return networkstatus_get_param(NULL, "pb_noticepct", - DFLT_PATH_BIAS_NOTICE_PCT, 0, 100)/100.0; -} - -/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */ -/** The circuit success rate below which we issue a warn */ -static double -pathbias_get_warn_rate(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_WARN_PCT 50 - if (options->PathBiasWarnRate >= 0.0) - return options->PathBiasWarnRate; - else - return networkstatus_get_param(NULL, "pb_warnpct", - DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0; -} - -/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */ -/** - * The extreme rate is the rate at which we would drop the guard, - * if pb_dropguard is also set. Otherwise we just warn. - */ -double -pathbias_get_extreme_rate(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_EXTREME_PCT 30 - if (options->PathBiasExtremeRate >= 0.0) - return options->PathBiasExtremeRate; - else - return networkstatus_get_param(NULL, "pb_extremepct", - DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0; -} - -/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */ -/** - * If 1, we actually disable use of guards that fall below - * the extreme_pct. - */ -int -pathbias_get_dropguards(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_DROP_GUARDS 0 - if (options->PathBiasDropGuards >= 0) - return options->PathBiasDropGuards; - else - return networkstatus_get_param(NULL, "pb_dropguards", - DFLT_PATH_BIAS_DROP_GUARDS, 0, 1); -} - -/** - * This is the number of circuits at which we scale our - * counts by mult_factor/scale_factor. Note, this count is - * not exact, as we only perform the scaling in the event - * of no integer truncation. - */ -static int -pathbias_get_scale_threshold(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_SCALE_THRESHOLD 300 - if (options->PathBiasScaleThreshold >= 10) - return options->PathBiasScaleThreshold; - else - return networkstatus_get_param(NULL, "pb_scalecircs", - DFLT_PATH_BIAS_SCALE_THRESHOLD, 10, - INT32_MAX); -} - -/** - * Compute the path bias scaling ratio from the consensus - * parameters pb_multfactor/pb_scalefactor. - * - * Returns a value in (0, 1.0] which we multiply our pathbias - * counts with to scale them down. - */ -static double -pathbias_get_scale_ratio(const or_options_t *options) -{ - /* - * The scale factor is the denominator for our scaling - * of circuit counts for our path bias window. - * - * Note that our use of doubles for the path bias state - * file means that powers of 2 work best here. - */ - int denominator = networkstatus_get_param(NULL, "pb_scalefactor", - 2, 2, INT32_MAX); - (void) options; - /** - * The mult factor is the numerator for our scaling - * of circuit counts for our path bias window. It - * allows us to scale by fractions. - */ - return networkstatus_get_param(NULL, "pb_multfactor", - 1, 1, denominator)/((double)denominator); -} - -/** The minimum number of circuit usage attempts before we start - * thinking about warning about path use bias and dropping guards */ -static int -pathbias_get_min_use(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_MIN_USE 20 - if (options->PathBiasUseThreshold >= 3) - return options->PathBiasUseThreshold; - else - return networkstatus_get_param(NULL, "pb_minuse", - DFLT_PATH_BIAS_MIN_USE, - 3, INT32_MAX); -} - -/** The circuit use success rate below which we issue a notice */ -static double -pathbias_get_notice_use_rate(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_NOTICE_USE_PCT 80 - if (options->PathBiasNoticeUseRate >= 0.0) - return options->PathBiasNoticeUseRate; - else - return networkstatus_get_param(NULL, "pb_noticeusepct", - DFLT_PATH_BIAS_NOTICE_USE_PCT, - 0, 100)/100.0; -} - -/** - * The extreme use rate is the rate at which we would drop the guard, - * if pb_dropguard is also set. Otherwise we just warn. - */ -double -pathbias_get_extreme_use_rate(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_EXTREME_USE_PCT 60 - if (options->PathBiasExtremeUseRate >= 0.0) - return options->PathBiasExtremeUseRate; - else - return networkstatus_get_param(NULL, "pb_extremeusepct", - DFLT_PATH_BIAS_EXTREME_USE_PCT, - 0, 100)/100.0; -} - -/** - * This is the number of circuits at which we scale our - * use counts by mult_factor/scale_factor. Note, this count is - * not exact, as we only perform the scaling in the event - * of no integer truncation. - */ -static int -pathbias_get_scale_use_threshold(const or_options_t *options) -{ -#define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100 - if (options->PathBiasScaleUseThreshold >= 10) - return options->PathBiasScaleUseThreshold; - else - return networkstatus_get_param(NULL, "pb_scaleuse", - DFLT_PATH_BIAS_SCALE_USE_THRESHOLD, - 10, INT32_MAX); -} - -/** - * Convert a Guard's path state to string. - */ -const char * -pathbias_state_to_string(path_state_t state) -{ - switch (state) { - case PATH_STATE_NEW_CIRC: - return "new"; - case PATH_STATE_BUILD_ATTEMPTED: - return "build attempted"; - case PATH_STATE_BUILD_SUCCEEDED: - return "build succeeded"; - case PATH_STATE_USE_ATTEMPTED: - return "use attempted"; - case PATH_STATE_USE_SUCCEEDED: - return "use succeeded"; - case PATH_STATE_USE_FAILED: - return "use failed"; - case PATH_STATE_ALREADY_COUNTED: - return "already counted"; - } - - return "unknown"; -} - -/** - * This function decides if a circuit has progressed far enough to count - * as a circuit "attempt". As long as end-to-end tagging is possible, - * we assume the adversary will use it over hop-to-hop failure. Therefore, - * we only need to account bias for the last hop. This should make us - * much more resilient to ambient circuit failure, and also make that - * failure easier to measure (we only need to measure Exit failure rates). - */ -static int -pathbias_is_new_circ_attempt(origin_circuit_t *circ) -{ -#define N2N_TAGGING_IS_POSSIBLE -#ifdef N2N_TAGGING_IS_POSSIBLE - /* cpath is a circular list. We want circs with more than one hop, - * and the second hop must be waiting for keys still (it's just - * about to get them). */ - return circ->cpath && - circ->cpath->next != circ->cpath && - circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS; -#else - /* If tagging attacks are no longer possible, we probably want to - * count bias from the first hop. However, one could argue that - * timing-based tagging is still more useful than per-hop failure. - * In which case, we'd never want to use this. - */ - return circ->cpath && - circ->cpath->state == CPATH_STATE_AWAITING_KEYS; -#endif -} - -/** - * Decide if the path bias code should count a circuit. - * - * @returns 1 if we should count it, 0 otherwise. - */ -static int -pathbias_should_count(origin_circuit_t *circ) -{ -#define PATHBIAS_COUNT_INTERVAL (600) - static ratelim_t count_limit = - RATELIM_INIT(PATHBIAS_COUNT_INTERVAL); - char *rate_msg = NULL; - - /* We can't do path bias accounting without entry guards. - * Testing and controller circuits also have no guards. - * - * We also don't count server-side rends, because their - * endpoint could be chosen maliciously. - * Similarly, we can't count client-side intro attempts, - * because clients can be manipulated into connecting to - * malicious intro points. */ - if (get_options()->UseEntryGuards == 0 || - circ->base_.purpose == CIRCUIT_PURPOSE_TESTING || - circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER || - circ->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || - circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED || - (circ->base_.purpose >= CIRCUIT_PURPOSE_C_INTRODUCING && - circ->base_.purpose <= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) { - - /* Check to see if the shouldcount result has changed due to a - * unexpected purpose change that would affect our results. - * - * The reason we check the path state too here is because for the - * cannibalized versions of these purposes, we count them as successful - * before their purpose change. - */ - if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED - && circ->path_state != PATH_STATE_ALREADY_COUNTED) { - log_info(LD_BUG, - "Circuit %d is now being ignored despite being counted " - "in the past. Purpose is %s, path state is %s", - circ->global_identifier, - circuit_purpose_to_string(circ->base_.purpose), - pathbias_state_to_string(circ->path_state)); - } - circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED; - return 0; - } - - /* Completely ignore one hop circuits */ - if (circ->build_state->onehop_tunnel || - circ->build_state->desired_path_len == 1) { - /* Check for inconsistency */ - if (circ->build_state->desired_path_len != 1 || - !circ->build_state->onehop_tunnel) { - if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) { - log_info(LD_BUG, - "One-hop circuit has length %d. Path state is %s. " - "Circuit is a %s currently %s.%s", - circ->build_state->desired_path_len, - pathbias_state_to_string(circ->path_state), - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state), - rate_msg); - tor_free(rate_msg); - } - tor_fragile_assert(); - } - - /* Check to see if the shouldcount result has changed due to a - * unexpected change that would affect our results */ - if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED) { - log_info(LD_BUG, - "One-hop circuit %d is now being ignored despite being counted " - "in the past. Purpose is %s, path state is %s", - circ->global_identifier, - circuit_purpose_to_string(circ->base_.purpose), - pathbias_state_to_string(circ->path_state)); - } - circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED; - return 0; - } - - /* Check to see if the shouldcount result has changed due to a - * unexpected purpose change that would affect our results */ - if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) { - log_info(LD_BUG, - "Circuit %d is now being counted despite being ignored " - "in the past. Purpose is %s, path state is %s", - circ->global_identifier, - circuit_purpose_to_string(circ->base_.purpose), - pathbias_state_to_string(circ->path_state)); - } - circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED; - - return 1; -} - -/** - * Check our circuit state to see if this is a successful circuit attempt. - * If so, record it in the current guard's path bias circ_attempt count. - * - * Also check for several potential error cases for bug #6475. - */ -int -pathbias_count_build_attempt(origin_circuit_t *circ) -{ -#define CIRC_ATTEMPT_NOTICE_INTERVAL (600) - static ratelim_t circ_attempt_notice_limit = - RATELIM_INIT(CIRC_ATTEMPT_NOTICE_INTERVAL); - char *rate_msg = NULL; - - if (!pathbias_should_count(circ)) { - return 0; - } - - if (pathbias_is_new_circ_attempt(circ)) { - /* Help track down the real cause of bug #6475: */ - if (circ->has_opened && circ->path_state != PATH_STATE_BUILD_ATTEMPTED) { - if ((rate_msg = rate_limit_log(&circ_attempt_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); - tor_free(rate_msg); - } - } - - /* Don't re-count cannibalized circs.. */ - if (!circ->has_opened) { - entry_guard_t *guard = NULL; - - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } else if (circ->base_.n_chan) { - guard = - entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest); - } - - if (guard) { - if (circ->path_state == PATH_STATE_NEW_CIRC) { - circ->path_state = PATH_STATE_BUILD_ATTEMPTED; - - if (entry_guard_inc_circ_attempt_count(guard) < 0) { - /* Bogus guard; we already warned. */ - return -END_CIRC_REASON_TORPROTOCOL; - } - } else { - if ((rate_msg = rate_limit_log(&circ_attempt_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); - tor_free(rate_msg); - } - } - } else { - if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit, - approx_time()))) { - log_info(LD_CIRC, - "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); - tor_free(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. - */ -void -pathbias_count_build_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; - entry_guard_t *guard = NULL; - - if (!pathbias_should_count(circ)) { - return; - } - - /* Don't count cannibalized/reused circs for path bias - * "build" success, since they get counted under "use" success. */ - if (!circ->has_opened) { - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } - - if (guard) { - if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) { - circ->path_state = PATH_STATE_BUILD_SUCCEEDED; - guard->circ_successes++; - entry_guards_changed(); - - log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - 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); - tor_free(rate_msg); - } - } - - if (guard->circ_attempts < guard->circ_successes) { - log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) " - "for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - 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_CIRC, - "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); - tor_free(rate_msg); - } - } - } else { - if (circ->path_state < PATH_STATE_BUILD_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); - tor_free(rate_msg); - } - } - } -} - -/** - * Record an attempt to use a circuit. Changes the circuit's - * path state and update its guard's usage counter. - * - * Used for path bias usage accounting. - */ -void -pathbias_count_use_attempt(origin_circuit_t *circ) -{ - entry_guard_t *guard; - - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) { - log_notice(LD_BUG, - "Used circuit is in strange path state %s. " - "Circuit is a %s currently %s.", - pathbias_state_to_string(circ->path_state), - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - if (guard) { - pathbias_measure_use_rate(guard); - pathbias_scale_use_rates(guard); - guard->use_attempts++; - entry_guards_changed(); - - log_debug(LD_CIRC, - "Marked circuit %d (%f/%f) as used for guard %s ($%s).", - circ->global_identifier, - guard->use_successes, guard->use_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); - } - - circ->path_state = PATH_STATE_USE_ATTEMPTED; - } else { - /* Harmless but educational log message */ - log_info(LD_CIRC, - "Used circuit %d is already in path state %s. " - "Circuit is a %s currently %s.", - circ->global_identifier, - pathbias_state_to_string(circ->path_state), - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } - - return; -} - -/** - * Check the circuit's path state is appropriate and mark it as - * successfully used. Used for path bias usage accounting. - * - * We don't actually increment the guard's counters until - * pathbias_check_close(), because the circuit can still transition - * back to PATH_STATE_USE_ATTEMPTED if a stream fails later (this - * is done so we can probe the circuit for liveness at close). - */ -void -pathbias_mark_use_success(origin_circuit_t *circ) -{ - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->path_state < PATH_STATE_USE_ATTEMPTED) { - log_notice(LD_BUG, - "Used circuit %d is in strange path state %s. " - "Circuit is a %s currently %s.", - circ->global_identifier, - pathbias_state_to_string(circ->path_state), - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - - pathbias_count_use_attempt(circ); - } - - /* We don't do any accounting at the guard until actual circuit close */ - circ->path_state = PATH_STATE_USE_SUCCEEDED; - - return; -} - -/** - * If a stream ever detatches from a circuit in a retriable way, - * we need to mark this circuit as still needing either another - * successful stream, or in need of a probe. - * - * An adversary could let the first stream request succeed (ie the - * resolve), but then tag and timeout the remainder (via cell - * dropping), forcing them on new circuits. - * - * Rolling back the state will cause us to probe such circuits, which - * should lead to probe failures in the event of such tagging due to - * either unrecognized cells coming in while we wait for the probe, - * or the cipher state getting out of sync in the case of dropped cells. - */ -void -pathbias_mark_use_rollback(origin_circuit_t *circ) -{ - if (circ->path_state == PATH_STATE_USE_SUCCEEDED) { - log_info(LD_CIRC, - "Rolling back pathbias use state to 'attempted' for detached " - "circuit %d", circ->global_identifier); - circ->path_state = PATH_STATE_USE_ATTEMPTED; - } -} - -/** - * Actually count a circuit success towards a guard's usage counters - * if the path state is appropriate. - */ -static void -pathbias_count_use_success(origin_circuit_t *circ) -{ - entry_guard_t *guard; - - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->path_state != PATH_STATE_USE_SUCCEEDED) { - log_notice(LD_BUG, - "Successfully used circuit %d is in strange path state %s. " - "Circuit is a %s currently %s.", - circ->global_identifier, - pathbias_state_to_string(circ->path_state), - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } else { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - if (guard) { - guard->use_successes++; - entry_guards_changed(); - - if (guard->use_attempts < guard->use_successes) { - log_notice(LD_BUG, "Unexpectedly high use successes counts (%f/%f) " - "for guard %s=%s", - guard->use_successes, guard->use_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); - } - - log_debug(LD_CIRC, - "Marked circuit %d (%f/%f) as used successfully for guard " - "%s ($%s).", - circ->global_identifier, guard->use_successes, - guard->use_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); - } - } - - return; -} - -/** - * Send a probe down a circuit that the client attempted to use, - * but for which the stream timed out/failed. The probe is a - * RELAY_BEGIN cell with a 0.a.b.c destination address, which - * the exit will reject and reply back, echoing that address. - * - * The reason for such probes is because it is possible to bias - * a user's paths simply by causing timeouts, and these timeouts - * are not possible to differentiate from unresponsive servers. - * - * The probe is sent at the end of the circuit lifetime for two - * reasons: to prevent cryptographic taggers from being able to - * drop cells to cause timeouts, and to prevent easy recognition - * of probes before any real client traffic happens. - * - * Returns -1 if we couldn't probe, 0 otherwise. - */ -static int -pathbias_send_usable_probe(circuit_t *circ) -{ - /* Based on connection_ap_handshake_send_begin() */ - char payload[CELL_PAYLOAD_SIZE]; - int payload_len; - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - crypt_path_t *cpath_layer = NULL; - char *probe_nonce = NULL; - - tor_assert(ocirc); - - cpath_layer = ocirc->cpath->prev; - - if (cpath_layer->state != CPATH_STATE_OPEN) { - /* This can happen for cannibalized circuits. Their - * last hop isn't yet open */ - log_info(LD_CIRC, - "Got pathbias probe request for unopened circuit %d. " - "Opened %d, len %d", ocirc->global_identifier, - ocirc->has_opened, ocirc->build_state->desired_path_len); - return -1; - } - - /* We already went down this road. */ - if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING && - ocirc->pathbias_probe_id) { - log_info(LD_CIRC, - "Got pathbias probe request for circuit %d with " - "outstanding probe", ocirc->global_identifier); - return -1; - } - - /* Can't probe if the channel isn't open */ - if (circ->n_chan == NULL || - (circ->n_chan->state != CHANNEL_STATE_OPEN - && circ->n_chan->state != CHANNEL_STATE_MAINT)) { - log_info(LD_CIRC, - "Skipping pathbias probe for circuit %d: Channel is not open.", - ocirc->global_identifier); - return -1; - } - - circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING); - - /* Update timestamp for when circuit_expire_building() should kill us */ - tor_gettimeofday(&circ->timestamp_began); - - /* Generate a random address for the nonce */ - crypto_rand((char*)ô->pathbias_probe_nonce, - sizeof(ocirc->pathbias_probe_nonce)); - ocirc->pathbias_probe_nonce &= 0x00ffffff; - probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce); - - tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce); - payload_len = (int)strlen(payload)+1; - - // XXX: need this? Can we assume ipv4 will always be supported? - // If not, how do we tell? - //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) { - // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags)); - // payload_len += 4; - //} - - /* Generate+Store stream id, make sure it's non-zero */ - ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc); - - if (ocirc->pathbias_probe_id==0) { - log_warn(LD_CIRC, - "Ran out of stream IDs on circuit %u during " - "pathbias probe attempt.", ocirc->global_identifier); - tor_free(probe_nonce); - return -1; - } - - log_info(LD_CIRC, - "Sending pathbias testing cell to %s:25 on stream %d for circ %d.", - probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier); - tor_free(probe_nonce); - - /* Send a test relay cell */ - if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ, - RELAY_COMMAND_BEGIN, payload, - payload_len, cpath_layer) < 0) { - log_notice(LD_CIRC, - "Failed to send pathbias probe cell on circuit %d.", - ocirc->global_identifier); - return -1; - } - - /* Mark it freshly dirty so it doesn't get expired in the meantime */ - circ->timestamp_dirty = time(NULL); - - return 0; -} - -/** - * Check the response to a pathbias probe, to ensure the - * cell is recognized and the nonce and other probe - * characteristics are as expected. - * - * If the response is valid, return 0. Otherwise return < 0. - */ -int -pathbias_check_probe_response(circuit_t *circ, const cell_t *cell) -{ - /* Based on connection_edge_process_relay_cell() */ - relay_header_t rh; - int reason; - uint32_t ipv4_host; - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - - tor_assert(cell); - tor_assert(ocirc); - tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING); - - relay_header_unpack(&rh, cell->payload); - - reason = rh.length > 0 ? - get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC; - - if (rh.command == RELAY_COMMAND_END && - reason == END_STREAM_REASON_EXITPOLICY && - ocirc->pathbias_probe_id == rh.stream_id) { - - /* Check length+extract host: It is in network order after the reason code. - * See connection_edge_end(). */ - if (rh.length < 9) { /* reason+ipv4+dns_ttl */ - log_notice(LD_PROTOCOL, - "Short path bias probe response length field (%d).", rh.length); - return - END_CIRC_REASON_TORPROTOCOL; - } - - ipv4_host = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); - - /* Check nonce */ - if (ipv4_host == ocirc->pathbias_probe_nonce) { - pathbias_mark_use_success(ocirc); - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - log_info(LD_CIRC, - "Got valid path bias probe back for circ %d, stream %d.", - ocirc->global_identifier, ocirc->pathbias_probe_id); - return 0; - } else { - log_notice(LD_CIRC, - "Got strange probe value 0x%x vs 0x%x back for circ %d, " - "stream %d.", ipv4_host, ocirc->pathbias_probe_nonce, - ocirc->global_identifier, ocirc->pathbias_probe_id); - return -1; - } - } - log_info(LD_CIRC, - "Got another cell back back on pathbias probe circuit %d: " - "Command: %d, Reason: %d, Stream-id: %d", - ocirc->global_identifier, rh.command, reason, rh.stream_id); - return -1; -} - -/** - * Check if a circuit was used and/or closed successfully. - * - * If we attempted to use the circuit to carry a stream but failed - * for whatever reason, or if the circuit mysteriously died before - * we could attach any streams, record these two cases. - * - * If we *have* successfully used the circuit, or it appears to - * have been closed by us locally, count it as a success. - * - * Returns 0 if we're done making decisions with the circ, - * or -1 if we want to probe it first. - */ -int -pathbias_check_close(origin_circuit_t *ocirc, int reason) -{ - circuit_t *circ = ô->base_; - - if (!pathbias_should_count(ocirc)) { - return 0; - } - - switch (ocirc->path_state) { - /* If the circuit was closed after building, but before use, we need - * to ensure we were the ones who tried to close it (and not a remote - * actor). */ - case PATH_STATE_BUILD_SUCCEEDED: - if (reason & END_CIRC_REASON_FLAG_REMOTE) { - /* Remote circ close reasons on an unused circuit all could be bias */ - log_info(LD_CIRC, - "Circuit %d remote-closed without successful use for reason %d. " - "Circuit purpose %d currently %d,%s. Len %d.", - ocirc->global_identifier, - reason, circ->purpose, ocirc->has_opened, - circuit_state_to_string(circ->state), - ocirc->build_state->desired_path_len); - pathbias_count_collapse(ocirc); - } else if ((reason & ~END_CIRC_REASON_FLAG_REMOTE) - == END_CIRC_REASON_CHANNEL_CLOSED && - circ->n_chan && - circ->n_chan->reason_for_closing - != CHANNEL_CLOSE_REQUESTED) { - /* If we didn't close the channel ourselves, it could be bias */ - /* XXX: Only count bias if the network is live? - * What about clock jumps/suspends? */ - log_info(LD_CIRC, - "Circuit %d's channel closed without successful use for reason " - "%d, channel reason %d. Circuit purpose %d currently %d,%s. Len " - "%d.", ocirc->global_identifier, - reason, circ->n_chan->reason_for_closing, - circ->purpose, ocirc->has_opened, - circuit_state_to_string(circ->state), - ocirc->build_state->desired_path_len); - pathbias_count_collapse(ocirc); - } else { - pathbias_count_successful_close(ocirc); - } - break; - - /* If we tried to use a circuit but failed, we should probe it to ensure - * it has not been tampered with. */ - case PATH_STATE_USE_ATTEMPTED: - /* XXX: Only probe and/or count failure if the network is live? - * What about clock jumps/suspends? */ - if (pathbias_send_usable_probe(circ) == 0) - return -1; - else - pathbias_count_use_failed(ocirc); - - /* Any circuit where there were attempted streams but no successful - * streams could be bias */ - log_info(LD_CIRC, - "Circuit %d closed without successful use for reason %d. " - "Circuit purpose %d currently %d,%s. Len %d.", - ocirc->global_identifier, - reason, circ->purpose, ocirc->has_opened, - circuit_state_to_string(circ->state), - ocirc->build_state->desired_path_len); - break; - - case PATH_STATE_USE_SUCCEEDED: - pathbias_count_successful_close(ocirc); - pathbias_count_use_success(ocirc); - break; - - case PATH_STATE_USE_FAILED: - pathbias_count_use_failed(ocirc); - break; - - case PATH_STATE_NEW_CIRC: - case PATH_STATE_BUILD_ATTEMPTED: - case PATH_STATE_ALREADY_COUNTED: - default: - // Other states are uninteresting. No stats to count. - break; - } - - ocirc->path_state = PATH_STATE_ALREADY_COUNTED; - - return 0; -} - -/** - * Count a successfully closed circuit. - */ -static void -pathbias_count_successful_close(origin_circuit_t *circ) -{ - entry_guard_t *guard = NULL; - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } - - if (guard) { - /* In the long run: circuit_success ~= successful_circuit_close + - * circ_failure + stream_failure */ - guard->successful_circuits_closed++; - entry_guards_changed(); - } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - /* 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. */ - log_info(LD_CIRC, - "Successfully closed circuit has no known guard. " - "Circuit is a %s currently %s", - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } -} - -/** - * Count a circuit that fails after it is built, but before it can - * carry any traffic. - * - * This is needed because there are ways to destroy a - * circuit after it has successfully completed. Right now, this is - * used for purely informational/debugging purposes. - */ -static void -pathbias_count_collapse(origin_circuit_t *circ) -{ - entry_guard_t *guard = NULL; - - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } - - if (guard) { - guard->collapsed_circuits++; - entry_guards_changed(); - } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - /* 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. */ - log_info(LD_CIRC, - "Destroyed circuit has no known guard. " - "Circuit is a %s currently %s", - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } -} - -/** - * Count a known failed circuit (because we could not probe it). - * - * This counter is informational. - */ -static void -pathbias_count_use_failed(origin_circuit_t *circ) -{ - entry_guard_t *guard = NULL; - if (!pathbias_should_count(circ)) { - return; - } - - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } - - if (guard) { - guard->unusable_circuits++; - entry_guards_changed(); - } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - /* 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. */ - /* XXX note cut-and-paste code in this function compared to nearby - * functions. Would be nice to refactor. -RD */ - log_info(LD_CIRC, - "Stream-failing circuit has no known guard. " - "Circuit is a %s currently %s", - circuit_purpose_to_string(circ->base_.purpose), - circuit_state_to_string(circ->base_.state)); - } -} - -/** - * Count timeouts for path bias log messages. - * - * These counts are purely informational. - */ -void -pathbias_count_timeout(origin_circuit_t *circ) -{ - entry_guard_t *guard = NULL; - - if (!pathbias_should_count(circ)) { - return; - } - - /* For hidden service circs, they can actually be used - * successfully and then time out later (because - * the other side declines to use them). */ - if (circ->path_state == PATH_STATE_USE_SUCCEEDED) { - return; - } - - if (circ->cpath && circ->cpath->extend_info) { - guard = entry_guard_get_by_id_digest( - circ->cpath->extend_info->identity_digest); - } - - if (guard) { - guard->timeouts++; - entry_guards_changed(); - } -} - -/** - * Helper function to count all of the currently opened circuits - * for a guard that are in a given path state range. The state - * range is inclusive on both ends. - */ -static int -pathbias_count_circs_in_states(entry_guard_t *guard, - path_state_t from, - path_state_t to) -{ - circuit_t *circ; - int open_circuits = 0; - - /* Count currently open circuits. Give them the benefit of the doubt. */ - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - origin_circuit_t *ocirc = NULL; - if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */ - circ->marked_for_close) /* already counted */ - continue; - - ocirc = TO_ORIGIN_CIRCUIT(circ); - - if (!ocirc->cpath || !ocirc->cpath->extend_info) - continue; - - if (ocirc->path_state >= from && - ocirc->path_state <= to && - pathbias_should_count(ocirc) && - fast_memeq(guard->identity, - ocirc->cpath->extend_info->identity_digest, - DIGEST_LEN)) { - log_debug(LD_CIRC, "Found opened circuit %d in path_state %s", - ocirc->global_identifier, - pathbias_state_to_string(ocirc->path_state)); - open_circuits++; - } - } - - return open_circuits; -} - -/** - * Return the number of circuits counted as successfully closed for - * this guard. - * - * Also add in the currently open circuits to give them the benefit - * of the doubt. - */ -double -pathbias_get_close_success_count(entry_guard_t *guard) -{ - return guard->successful_circuits_closed + - pathbias_count_circs_in_states(guard, - PATH_STATE_BUILD_SUCCEEDED, - PATH_STATE_USE_SUCCEEDED); -} - -/** - * Return the number of circuits counted as successfully used - * this guard. - * - * Also add in the currently open circuits that we are attempting - * to use to give them the benefit of the doubt. - */ -double -pathbias_get_use_success_count(entry_guard_t *guard) -{ - return guard->use_successes + - pathbias_count_circs_in_states(guard, - PATH_STATE_USE_ATTEMPTED, - PATH_STATE_USE_SUCCEEDED); -} - -/** - * Check the path bias use rate against our consensus parameter limits. - * - * Emits a log message if the use success rates are too low. - * - * If pathbias_get_dropguards() is set, we also disable the use of - * very failure prone guards. - */ -static void -pathbias_measure_use_rate(entry_guard_t *guard) -{ - const or_options_t *options = get_options(); - - if (guard->use_attempts > pathbias_get_min_use(options)) { - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_use_success_count(guard)/guard->use_attempts - < pathbias_get_extreme_use_rate(options)) { - /* Dropping is currently disabled by default. */ - if (pathbias_get_dropguards(options)) { - if (!guard->path_bias_disabled) { - log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing to carry an extremely large " - "amount of stream on its circuits. " - "To avoid potential route manipulation attacks, Tor has " - "disabled use of this guard. " - "Use counts are %ld/%ld. Success counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - guard->path_bias_disabled = 1; - guard->bad_since = approx_time(); - entry_guards_changed(); - return; - } - } else if (!guard->path_bias_use_extreme) { - guard->path_bias_use_extreme = 1; - log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing to carry an extremely large " - "amount of streams on its circuits. " - "This could indicate a route manipulation attack, network " - "overload, bad local network connectivity, or a bug. " - "Use counts are %ld/%ld. Success counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - } - } else if (pathbias_get_use_success_count(guard)/guard->use_attempts - < pathbias_get_notice_use_rate(options)) { - if (!guard->path_bias_use_noticed) { - guard->path_bias_use_noticed = 1; - log_notice(LD_CIRC, - "Your Guard %s ($%s) is failing to carry more streams on its " - "circuits than usual. " - "Most likely this means the Tor network is overloaded " - "or your network connection is poor. " - "Use counts are %ld/%ld. Success counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - } - } - } -} - -/** - * Check the path bias circuit close status rates against our consensus - * parameter limits. - * - * Emits a log message if the use success rates are too low. - * - * If pathbias_get_dropguards() is set, we also disable the use of - * very failure prone guards. - * - * XXX: This function shares similar log messages and checks to - * pathbias_measure_use_rate(). It may be possible to combine them - * eventually, especially if we can ever remove the need for 3 - * levels of closure warns (if the overall circuit failure rate - * goes down with ntor). One way to do so would be to multiply - * the build rate with the use rate to get an idea of the total - * fraction of the total network paths the user is able to use. - * See ticket #8159. - */ -static void -pathbias_measure_close_rate(entry_guard_t *guard) -{ - const or_options_t *options = get_options(); - - if (guard->circ_attempts > pathbias_get_min_circs(options)) { - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_close_success_count(guard)/guard->circ_attempts - < pathbias_get_extreme_rate(options)) { - /* Dropping is currently disabled by default. */ - if (pathbias_get_dropguards(options)) { - if (!guard->path_bias_disabled) { - log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing an extremely large " - "amount of circuits. " - "To avoid potential route manipulation attacks, Tor has " - "disabled use of this guard. " - "Success counts are %ld/%ld. Use counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - guard->path_bias_disabled = 1; - guard->bad_since = approx_time(); - entry_guards_changed(); - return; - } - } else if (!guard->path_bias_extreme) { - guard->path_bias_extreme = 1; - log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing an extremely large " - "amount of circuits. " - "This could indicate a route manipulation attack, " - "extreme network overload, or a bug. " - "Success counts are %ld/%ld. Use counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - } - } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts - < pathbias_get_warn_rate(options)) { - if (!guard->path_bias_warned) { - guard->path_bias_warned = 1; - log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing a very large " - "amount of circuits. " - "Most likely this means the Tor network is " - "overloaded, but it could also mean an attack against " - "you or potentially the guard itself. " - "Success counts are %ld/%ld. Use counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - } - } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts - < pathbias_get_notice_rate(options)) { - if (!guard->path_bias_noticed) { - guard->path_bias_noticed = 1; - log_notice(LD_CIRC, - "Your Guard %s ($%s) is failing more circuits than " - "usual. " - "Most likely this means the Tor network is overloaded. " - "Success counts are %ld/%ld. Use counts are %ld/%ld. " - "%ld circuits completed, %ld were unusable, %ld collapsed, " - "and %ld timed out. " - "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), - tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), - tor_lround(get_circuit_build_close_time_ms()/1000)); - } - } - } -} - -/** - * This function scales the path bias use rates if we have - * more data than the scaling threshold. This allows us to - * be more sensitive to recent measurements. - * - * XXX: The attempt count transfer stuff here might be done - * better by keeping separate pending counters that get - * transfered at circuit close. See ticket #8160. - */ -static void -pathbias_scale_close_rates(entry_guard_t *guard) -{ - const or_options_t *options = get_options(); - - /* If we get a ton of circuits, just scale everything down */ - if (guard->circ_attempts > pathbias_get_scale_threshold(options)) { - double scale_ratio = pathbias_get_scale_ratio(options); - int opened_attempts = pathbias_count_circs_in_states(guard, - PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED); - int opened_built = pathbias_count_circs_in_states(guard, - PATH_STATE_BUILD_SUCCEEDED, - PATH_STATE_USE_FAILED); - /* Verify that the counts are sane before and after scaling */ - int counts_are_sane = (guard->circ_attempts >= guard->circ_successes); - - guard->circ_attempts -= (opened_attempts+opened_built); - guard->circ_successes -= opened_built; - - guard->circ_attempts *= scale_ratio; - guard->circ_successes *= scale_ratio; - guard->timeouts *= scale_ratio; - guard->successful_circuits_closed *= scale_ratio; - guard->collapsed_circuits *= scale_ratio; - guard->unusable_circuits *= scale_ratio; - - guard->circ_attempts += (opened_attempts+opened_built); - guard->circ_successes += opened_built; - - entry_guards_changed(); - - log_info(LD_CIRC, - "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard " - "%s ($%s)", - guard->circ_successes, guard->successful_circuits_closed, - guard->circ_attempts, opened_built, opened_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); - - /* Have the counts just become invalid by this scaling attempt? */ - if (counts_are_sane && guard->circ_attempts < guard->circ_successes) { - log_notice(LD_BUG, - "Scaling has mangled pathbias counts to %f/%f (%d/%d open) " - "for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, opened_built, - opened_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); - } - } -} - -/** - * This function scales the path bias circuit close rates if we have - * more data than the scaling threshold. This allows us to be more - * sensitive to recent measurements. - * - * XXX: The attempt count transfer stuff here might be done - * better by keeping separate pending counters that get - * transfered at circuit close. See ticket #8160. - */ -void -pathbias_scale_use_rates(entry_guard_t *guard) -{ - const or_options_t *options = get_options(); - - /* If we get a ton of circuits, just scale everything down */ - if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) { - double scale_ratio = pathbias_get_scale_ratio(options); - int opened_attempts = pathbias_count_circs_in_states(guard, - PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED); - /* Verify that the counts are sane before and after scaling */ - int counts_are_sane = (guard->use_attempts >= guard->use_successes); - - guard->use_attempts -= opened_attempts; - - guard->use_attempts *= scale_ratio; - guard->use_successes *= scale_ratio; - - guard->use_attempts += opened_attempts; - - log_info(LD_CIRC, - "Scaled pathbias use counts to %f/%f (%d open) for guard %s ($%s)", - guard->use_successes, guard->use_attempts, opened_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); - - /* Have the counts just become invalid by this scaling attempt? */ - if (counts_are_sane && guard->use_attempts < guard->use_successes) { - log_notice(LD_BUG, - "Scaling has mangled pathbias usage counts to %f/%f " - "(%d open) for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - opened_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); - } - - entry_guards_changed(); - } -} - diff --git a/src/tor/circpathbias.h b/src/tor/circpathbias.h deleted file mode 100644 index c95d801..0000000 --- a/src/tor/circpathbias.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitbuild.h - * \brief Header file for circuitbuild.c. - **/ - -#ifndef TOR_CIRCPATHBIAS_H -#define TOR_CIRCPATHBIAS_H - -double pathbias_get_extreme_rate(const or_options_t *options); -double pathbias_get_extreme_use_rate(const or_options_t *options); -int pathbias_get_dropguards(const or_options_t *options); -void pathbias_count_timeout(origin_circuit_t *circ); -void pathbias_count_build_success(origin_circuit_t *circ); -int pathbias_count_build_attempt(origin_circuit_t *circ); -int pathbias_check_close(origin_circuit_t *circ, int reason); -int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell); -void pathbias_count_use_attempt(origin_circuit_t *circ); -void pathbias_mark_use_success(origin_circuit_t *circ); -void pathbias_mark_use_rollback(origin_circuit_t *circ); -const char *pathbias_state_to_string(path_state_t state); - -#endif - diff --git a/src/tor/circuitbuild.c b/src/tor/circuitbuild.c deleted file mode 100644 index aeeea47..0000000 --- a/src/tor/circuitbuild.c +++ /dev/null @@ -1,2200 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitbuild.c - * \brief The actual details of building circuits. - **/ - -#include "or.h" -#include "channel.h" -#include "circpathbias.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitstats.h" -#include "circuituse.h" -#include "command.h" -#include "config.h" -#include "confparse.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "directory.h" -#include "entrynodes.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "onion.h" -#include "onion_tap.h" -#include "onion_fast.h" -#include "policies.h" -#include "transports.h" -#include "relay.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" -#include "routerset.h" -#include "crypto.h" - -#ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -#endif - -static channel_t * channel_connect_for_circuit(const tor_addr_t *addr, - uint16_t port, - const char *id_digest); -static int circuit_deliver_create_cell(circuit_t *circ, - const create_cell_t *create_cell, - int relayed); -static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit); -static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); -static int onion_extend_cpath(origin_circuit_t *circ); -static int count_acceptable_nodes(smartlist_t *routers); -static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); -#ifdef CURVE25519_ENABLED -static int circuits_can_use_ntor(void); -#endif - -/** This function tries to get a channel to the specified endpoint, - * and then calls command_setup_channel() to give it the right - * callbacks. - */ -static channel_t * -channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port, - const char *id_digest) -{ - channel_t *chan; - - chan = channel_connect(addr, port, id_digest); - if (chan) command_setup_channel(chan); - - return chan; -} - -/** Iterate over values of circ_id, starting from conn-\>next_circ_id, - * and with the high bit specified by conn-\>circ_id_type, until we get - * a circ_id that is not in use by any other circuit on that conn. - * - * Return it, or 0 if can't get a unique circ_id. - */ -static circid_t -get_unique_circ_id_by_chan(channel_t *chan) -{ - circid_t test_circ_id; - circid_t attempts=0; - circid_t high_bit, max_range; - - tor_assert(chan); - - if (chan->circ_id_type == CIRC_ID_TYPE_NEITHER) { - log_warn(LD_BUG, - "Trying to pick a circuit ID for a connection from " - "a client with no identity."); - return 0; - } - max_range = (chan->wide_circ_ids) ? (1u<<31) : (1u<<15); - high_bit = (chan->circ_id_type == CIRC_ID_TYPE_HIGHER) ? max_range : 0; - do { - /* Sequentially iterate over test_circ_id=1...max_range until we find a - * circID such that (high_bit|test_circ_id) is not already used. */ - test_circ_id = chan->next_circ_id++; - if (test_circ_id == 0 || test_circ_id >= max_range) { - test_circ_id = 1; - chan->next_circ_id = 2; - } - if (++attempts > max_range) { - /* Make sure we don't loop forever if all circ_id's are used. This - * matters because it's an external DoS opportunity. - */ - log_warn(LD_CIRC,"No unused circ IDs. Failing."); - return 0; - } - test_circ_id |= high_bit; - } while (circuit_id_in_use_on_channel(test_circ_id, chan)); - return test_circ_id; -} - -/** If verbose is false, allocate and return a comma-separated list of - * the currently built elements of circ. If verbose is true, also - * list information about link status in a more verbose format using spaces. - * If verbose_names is false, give nicknames for Named routers and hex - * digests for others; if verbose_names is true, use $DIGEST=Name style - * names. - */ -static char * -circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) -{ - crypt_path_t *hop; - smartlist_t *elements; - const char *states[] = {"closed", "waiting for keys", "open"}; - char *s; - - elements = smartlist_new(); - - if (verbose) { - const char *nickname = build_state_get_exit_nickname(circ->build_state); - smartlist_add_asprintf(elements, "%s%s circ (length %d%s%s):", - circ->build_state->is_internal ? "internal" : "exit", - circ->build_state->need_uptime ? " (high-uptime)" : "", - circ->build_state->desired_path_len, - circ->base_.state == CIRCUIT_STATE_OPEN ? "" : ", last hop ", - circ->base_.state == CIRCUIT_STATE_OPEN ? "" : - (nickname?nickname:"*unnamed*")); - } - - hop = circ->cpath; - do { - char *elt; - const char *id; - const node_t *node; - if (!hop) - break; - if (!verbose && hop->state != CPATH_STATE_OPEN) - break; - if (!hop->extend_info) - break; - id = hop->extend_info->identity_digest; - if (verbose_names) { - elt = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1); - if ((node = node_get_by_id(id))) { - node_get_verbose_nickname(node, elt); - } else if (is_legal_nickname(hop->extend_info->nickname)) { - elt[0] = '$'; - base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); - elt[HEX_DIGEST_LEN+1]= '~'; - strlcpy(elt+HEX_DIGEST_LEN+2, - hop->extend_info->nickname, MAX_NICKNAME_LEN+1); - } else { - elt[0] = '$'; - base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); - } - } else { /* ! verbose_names */ - node = node_get_by_id(id); - if (node && node_is_named(node)) { - elt = tor_strdup(node_get_nickname(node)); - } else { - elt = tor_malloc(HEX_DIGEST_LEN+2); - elt[0] = '$'; - base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); - } - } - tor_assert(elt); - if (verbose) { - tor_assert(hop->state <= 2); - smartlist_add_asprintf(elements,"%s(%s)",elt,states[hop->state]); - tor_free(elt); - } else { - smartlist_add(elements, elt); - } - hop = hop->next; - } while (hop != circ->cpath); - - s = smartlist_join_strings(elements, verbose?" ":",", 0, NULL); - SMARTLIST_FOREACH(elements, char*, cp, tor_free(cp)); - smartlist_free(elements); - return s; -} - -/** If verbose is false, allocate and return a comma-separated - * list of the currently built elements of circ. If - * verbose is true, also list information about link status in - * a more verbose format using spaces. - */ -char * -circuit_list_path(origin_circuit_t *circ, int verbose) -{ - return circuit_list_path_impl(circ, verbose, 0); -} - -/** Allocate and return a comma-separated list of the currently built elements - * of circ, giving each as a verbose nickname. - */ -char * -circuit_list_path_for_controller(origin_circuit_t *circ) -{ - return circuit_list_path_impl(circ, 0, 1); -} - -/** Log, at severity severity, the nicknames of each router in - * circ's cpath. Also log the length of the cpath, and the intended - * exit point. - */ -void -circuit_log_path(int severity, unsigned int domain, origin_circuit_t *circ) -{ - char *s = circuit_list_path(circ,1); - tor_log(severity,domain,"%s",s); - tor_free(s); -} - -/** Tell the rep(utation)hist(ory) module about the status of the links - * in circ. Hops that have become OPEN are marked as successfully - * extended; the _first_ hop that isn't open (if any) is marked as - * unable to extend. - */ -/* XXXX Someday we should learn from OR circuits too. */ -void -circuit_rep_hist_note_result(origin_circuit_t *circ) -{ - crypt_path_t *hop; - const char *prev_digest = NULL; - hop = circ->cpath; - if (!hop) /* circuit hasn't started building yet. */ - return; - if (server_mode(get_options())) { - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return; - prev_digest = me->cache_info.identity_digest; - } - do { - const node_t *node = node_get_by_id(hop->extend_info->identity_digest); - if (node) { /* Why do we check this? We know the identity. -NM XXXX */ - if (prev_digest) { - if (hop->state == CPATH_STATE_OPEN) - rep_hist_note_extend_succeeded(prev_digest, node->identity); - else { - rep_hist_note_extend_failed(prev_digest, node->identity); - break; - } - } - prev_digest = node->identity; - } else { - prev_digest = NULL; - } - hop=hop->next; - } while (hop!=circ->cpath); -} - -#ifdef CURVE25519_ENABLED -/** Return 1 iff at least one node in circ's cpath supports ntor. */ -static int -circuit_cpath_supports_ntor(const origin_circuit_t *circ) -{ - crypt_path_t *head = circ->cpath, *cpath = circ->cpath; - - cpath = head; - do { - if (cpath->extend_info && - !tor_mem_is_zero( - (const char*)cpath->extend_info->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) - return 1; - - cpath = cpath->next; - } while (cpath != head); - - return 0; -} -#else -#define circuit_cpath_supports_ntor(circ) 0 -#endif - -/** Pick all the entries in our cpath. Stop and return 0 when we're - * happy, or return -1 if an error occurs. */ -static int -onion_populate_cpath(origin_circuit_t *circ) -{ - int n_tries = 0; -#ifdef CURVE25519_ENABLED - const int using_ntor = circuits_can_use_ntor(); -#else - const int using_ntor = 0; -#endif - -#define MAX_POPULATE_ATTEMPTS 32 - - while (1) { - int r = onion_extend_cpath(circ); - if (r < 0) { - log_info(LD_CIRC,"Generating cpath hop failed."); - return -1; - } - if (r == 1) { - /* This circuit doesn't need/shouldn't be forced to have an ntor hop */ - if (circ->build_state->desired_path_len <= 1 || ! using_ntor) - return 0; - - /* This circuit has an ntor hop. great! */ - if (circuit_cpath_supports_ntor(circ)) - return 0; - - /* No node in the circuit supports ntor. Have we already tried too many - * times? */ - if (++n_tries >= MAX_POPULATE_ATTEMPTS) - break; - - /* Clear the path and retry */ - circuit_clear_cpath(circ); - } - } - log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop " - "circuit with at least one node that supports ntor.", - MAX_POPULATE_ATTEMPTS, - circ->build_state->desired_path_len); - - return -1; -} - -/** Create and return a new origin circuit. Initialize its purpose and - * build-state based on our arguments. The flags argument is a - * bitfield of CIRCLAUNCH_* flags. */ -origin_circuit_t * -origin_circuit_init(uint8_t purpose, int flags) -{ - /* sets circ->p_circ_id and circ->p_chan */ - origin_circuit_t *circ = origin_circuit_new(); - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_CHAN_WAIT); - circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); - circ->build_state->onehop_tunnel = - ((flags & CIRCLAUNCH_ONEHOP_TUNNEL) ? 1 : 0); - circ->build_state->need_uptime = - ((flags & CIRCLAUNCH_NEED_UPTIME) ? 1 : 0); - circ->build_state->need_capacity = - ((flags & CIRCLAUNCH_NEED_CAPACITY) ? 1 : 0); - circ->build_state->is_internal = - ((flags & CIRCLAUNCH_IS_INTERNAL) ? 1 : 0); - circ->base_.purpose = purpose; - return circ; -} - -/** Build a new circuit for purpose. If exit - * is defined, then use that as your exit router, else choose a suitable - * exit node. - * - * Also launch a connection to the first OR in the chosen path, if - * it's not open already. - */ -origin_circuit_t * -circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, int flags) -{ - origin_circuit_t *circ; - int err_reason = 0; - - circ = origin_circuit_init(purpose, flags); - - if (onion_pick_cpath_exit(circ, exit) < 0 || - onion_populate_cpath(circ) < 0) { - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH); - return NULL; - } - - control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED, 0); - - if ((err_reason = circuit_handle_first_hop(circ)) < 0) { - circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason); - return NULL; - } - return circ; -} - -/** Start establishing the first hop of our circuit. Figure out what - * OR we should connect to, and if necessary start the connection to - * it. If we're already connected, then send the 'create' cell. - * Return 0 for ok, -reason if circ should be marked-for-close. */ -int -circuit_handle_first_hop(origin_circuit_t *circ) -{ - crypt_path_t *firsthop; - channel_t *n_chan; - int err_reason = 0; - const char *msg = NULL; - int should_launch = 0; - - firsthop = onion_next_hop_in_cpath(circ->cpath); - tor_assert(firsthop); - tor_assert(firsthop->extend_info); - - /* now see if we're already connected to the first OR in 'route' */ - log_debug(LD_CIRC,"Looking for firsthop '%s'", - fmt_addrport(&firsthop->extend_info->addr, - firsthop->extend_info->port)); - - n_chan = channel_get_for_extend(firsthop->extend_info->identity_digest, - &firsthop->extend_info->addr, - &msg, - &should_launch); - - if (!n_chan) { - /* not currently connected in a useful way. */ - log_info(LD_CIRC, "Next router is %s: %s", - safe_str_client(extend_info_describe(firsthop->extend_info)), - msg?msg:"???"); - circ->base_.n_hop = extend_info_dup(firsthop->extend_info); - - if (should_launch) { - if (circ->build_state->onehop_tunnel) - control_event_bootstrap(BOOTSTRAP_STATUS_CONN_DIR, 0); - n_chan = channel_connect_for_circuit( - &firsthop->extend_info->addr, - firsthop->extend_info->port, - firsthop->extend_info->identity_digest); - if (!n_chan) { /* connect failed, forget the whole thing */ - log_info(LD_CIRC,"connect to firsthop failed. Closing."); - return -END_CIRC_REASON_CONNECTFAILED; - } - } - - log_debug(LD_CIRC,"connecting in progress (or finished). Good."); - /* return success. The onion/circuit/etc will be taken care of - * automatically (may already have been) whenever n_chan reaches - * OR_CONN_STATE_OPEN. - */ - return 0; - } else { /* it's already open. use it. */ - tor_assert(!circ->base_.n_hop); - circ->base_.n_chan = n_chan; - log_debug(LD_CIRC,"Conn open. Delivering first onion skin."); - if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { - log_info(LD_CIRC,"circuit_send_next_onion_skin failed."); - return err_reason; - } - } - return 0; -} - -/** Find any circuits that are waiting on or_conn to become - * open and get them to send their create cells forward. - * - * Status is 1 if connect succeeded, or 0 if connect failed. - */ -void -circuit_n_chan_done(channel_t *chan, int status) -{ - smartlist_t *pending_circs; - int err_reason = 0; - - tor_assert(chan); - - log_debug(LD_CIRC,"chan to %s/%s, status=%d", - chan->nickname ? chan->nickname : "NULL", - channel_get_canonical_remote_descr(chan), status); - - pending_circs = smartlist_new(); - circuit_get_all_pending_on_channel(pending_circs, chan); - - SMARTLIST_FOREACH_BEGIN(pending_circs, circuit_t *, circ) - { - /* These checks are redundant wrt get_all_pending_on_or_conn, but I'm - * leaving them in in case it's possible for the status of a circuit to - * change as we're going down the list. */ - if (circ->marked_for_close || circ->n_chan || !circ->n_hop || - circ->state != CIRCUIT_STATE_CHAN_WAIT) - continue; - - if (tor_digest_is_zero(circ->n_hop->identity_digest)) { - /* Look at addr/port. This is an unkeyed connection. */ - if (!channel_matches_extend_info(chan, circ->n_hop)) - continue; - } else { - /* We expected a key. See if it's the right one. */ - if (tor_memneq(chan->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) - continue; - } - if (!status) { /* chan failed; close circ */ - log_info(LD_CIRC,"Channel failed; closing circ."); - circuit_mark_for_close(circ, END_CIRC_REASON_CHANNEL_CLOSED); - continue; - } - log_debug(LD_CIRC, "Found circ, sending create cell."); - /* circuit_deliver_create_cell will set n_circ_id and add us to - * chan_circuid_circuit_map, so we don't need to call - * set_circid_chan here. */ - circ->n_chan = chan; - extend_info_free(circ->n_hop); - circ->n_hop = NULL; - - if (CIRCUIT_IS_ORIGIN(circ)) { - if ((err_reason = - circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ))) < 0) { - log_info(LD_CIRC, - "send_next_onion_skin failed; circuit marked for closing."); - circuit_mark_for_close(circ, -err_reason); - continue; - /* XXX could this be bad, eg if next_onion_skin failed because conn - * died? */ - } - } else { - /* pull the create cell out of circ->n_chan_create_cell, and send it */ - tor_assert(circ->n_chan_create_cell); - if (circuit_deliver_create_cell(circ, circ->n_chan_create_cell, 1)<0) { - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - continue; - } - tor_free(circ->n_chan_create_cell); - circuit_set_state(circ, CIRCUIT_STATE_OPEN); - } - } - SMARTLIST_FOREACH_END(circ); - - smartlist_free(pending_circs); -} - -/** Find a new circid that isn't currently in use on the circ->n_chan - * for the outgoing - * circuit circ, and deliver the cell create_cell to this - * circuit. If relayed is true, this is a create cell somebody - * gave us via an EXTEND cell, so we shouldn't worry if we don't understand - * it. Return -1 if we failed to find a suitable circid, else return 0. - */ -static int -circuit_deliver_create_cell(circuit_t *circ, const create_cell_t *create_cell, - int relayed) -{ - cell_t cell; - circid_t id; - int r; - - tor_assert(circ); - tor_assert(circ->n_chan); - tor_assert(create_cell); - tor_assert(create_cell->cell_type == CELL_CREATE || - create_cell->cell_type == CELL_CREATE_FAST || - create_cell->cell_type == CELL_CREATE2); - - id = get_unique_circ_id_by_chan(circ->n_chan); - if (!id) { - log_warn(LD_CIRC,"failed to get unique circID."); - return -1; - } - log_debug(LD_CIRC,"Chosen circID %u.", (unsigned)id); - circuit_set_n_circid_chan(circ, id, circ->n_chan); - - memset(&cell, 0, sizeof(cell_t)); - r = relayed ? create_cell_format_relayed(&cell, create_cell) - : create_cell_format(&cell, create_cell); - if (r < 0) { - log_warn(LD_CIRC,"Couldn't format create cell"); - return -1; - } - cell.circ_id = circ->n_circ_id; - - append_cell_to_circuit_queue(circ, circ->n_chan, &cell, - CELL_DIRECTION_OUT, 0); - - if (CIRCUIT_IS_ORIGIN(circ)) { - /* Update began timestamp for circuits starting their first hop */ - if (TO_ORIGIN_CIRCUIT(circ)->cpath->state == CPATH_STATE_CLOSED) { - if (circ->n_chan->state != CHANNEL_STATE_OPEN) { - log_warn(LD_CIRC, - "Got first hop for a circuit without an opened channel. " - "State: %s.", channel_state_to_string(circ->n_chan->state)); - tor_fragile_assert(); - } - - tor_gettimeofday(&circ->timestamp_began); - } - - /* mark it so it gets better rate limiting treatment. */ - channel_timestamp_client(circ->n_chan); - } - - return 0; -} - -/** We've decided to start our reachability testing. If all - * is set, log this to the user. Return 1 if we did, or 0 if - * we chose not to log anything. */ -int -inform_testing_reachability(void) -{ - char dirbuf[128]; - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return 0; - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY ORADDRESS=%s:%d", - me->address, me->or_port); - if (me->dir_port) { - tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d", - me->address, me->dir_port); - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY DIRADDRESS=%s:%d", - me->address, me->dir_port); - } - log_notice(LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... " - "(this may take up to %d minutes -- look for log " - "messages indicating success)", - me->address, me->or_port, - me->dir_port ? dirbuf : "", - me->dir_port ? "are" : "is", - TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); - - return 1; -} - -/** Return true iff we should send a create_fast cell to start building a given - * circuit */ -static INLINE int -should_use_create_fast_for_circuit(origin_circuit_t *circ) -{ - const or_options_t *options = get_options(); - tor_assert(circ->cpath); - tor_assert(circ->cpath->extend_info); - - if (!circ->cpath->extend_info->onion_key) - return 1; /* our hand is forced: only a create_fast will work. */ - if (public_server_mode(options)) { - /* We're a server, and we know an onion key. We can choose. - * Prefer to blend our circuit into the other circuits we are - * creating on behalf of others. */ - return 0; - } - if (options->FastFirstHopPK == -1) { - /* option is "auto", so look at the consensus. */ - return networkstatus_get_param(NULL, "usecreatefast", 1, 0, 1); - } - - return options->FastFirstHopPK; -} - -/** Return true if circ is the type of circuit we want to count - * timeouts from. In particular, we want it to have not completed yet - * (already completing indicates we cannibalized it), and we want it to - * have exactly three hops. - */ -int -circuit_timeout_want_to_count_circ(origin_circuit_t *circ) -{ - return !circ->has_opened - && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; -} - -#ifdef CURVE25519_ENABLED -/** Return true if the ntor handshake is enabled in the configuration, or if - * it's been set to "auto" in the configuration and it's enabled in the - * consensus. */ -static int -circuits_can_use_ntor(void) -{ - const or_options_t *options = get_options(); - if (options->UseNTorHandshake != -1) - return options->UseNTorHandshake; - return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1); -} -#endif - -/** Decide whether to use a TAP or ntor handshake for connecting to ei - * directly, and set *cell_type_out and *handshake_type_out - * accordingly. */ -static void -circuit_pick_create_handshake(uint8_t *cell_type_out, - uint16_t *handshake_type_out, - const extend_info_t *ei) -{ -#ifdef CURVE25519_ENABLED - if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN) && - circuits_can_use_ntor()) { - *cell_type_out = CELL_CREATE2; - *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; - return; - } -#else - (void) ei; -#endif - - *cell_type_out = CELL_CREATE; - *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP; -} - -/** Decide whether to use a TAP or ntor handshake for connecting to ei - * directly, and set *handshake_type_out accordingly. Decide whether, - * in extending through node to do so, we should use an EXTEND2 or an - * EXTEND cell to do so, and set *cell_type_out and - * *create_cell_type_out accordingly. */ -static void -circuit_pick_extend_handshake(uint8_t *cell_type_out, - uint8_t *create_cell_type_out, - uint16_t *handshake_type_out, - const node_t *node_prev, - const extend_info_t *ei) -{ - uint8_t t; - circuit_pick_create_handshake(&t, handshake_type_out, ei); - /* XXXX024 The check for whether the node has a curve25519 key is a bad - * proxy for whether it can do extend2 cells; once a version that - * handles extend2 cells is out, remove it. */ - if (node_prev && - *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && - (node_has_curve25519_onion_key(node_prev) || - (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) { - *cell_type_out = RELAY_COMMAND_EXTEND2; - *create_cell_type_out = CELL_CREATE2; - } else { - *cell_type_out = RELAY_COMMAND_EXTEND; - *create_cell_type_out = CELL_CREATE; - } -} - -/** This is the backbone function for building circuits. - * - * If circ's first hop is closed, then we need to build a create - * cell and send it forward. - * - * Otherwise, we need to build a relay extend cell and send it - * forward. - * - * Return -reason if we want to tear down circ, else return 0. - */ -int -circuit_send_next_onion_skin(origin_circuit_t *circ) -{ - crypt_path_t *hop; - const node_t *node; - - tor_assert(circ); - - if (circ->cpath->state == CPATH_STATE_CLOSED) { - /* This is the first hop. */ - create_cell_t cc; - int fast; - int len; - log_debug(LD_CIRC,"First skin; sending create cell."); - memset(&cc, 0, sizeof(cc)); - if (circ->build_state->onehop_tunnel) - control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0); - else - control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0); - - node = node_get_by_id(circ->base_.n_chan->identity_digest); - fast = should_use_create_fast_for_circuit(circ); - if (!fast) { - /* We are an OR and we know the right onion key: we should - * send a create cell. - */ - circuit_pick_create_handshake(&cc.cell_type, &cc.handshake_type, - circ->cpath->extend_info); - note_request("cell: create", 1); - } else { - /* We are not an OR, and we're building the first hop of a circuit to a - * new OR: we can be speedy and use CREATE_FAST to save an RSA operation - * and a DH operation. */ - cc.cell_type = CELL_CREATE_FAST; - cc.handshake_type = ONION_HANDSHAKE_TYPE_FAST; - note_request("cell: create fast", 1); - } - - len = onion_skin_create(cc.handshake_type, - circ->cpath->extend_info, - &circ->cpath->handshake_state, - cc.onionskin); - if (len < 0) { - log_warn(LD_CIRC,"onion_skin_create (first hop) failed."); - return - END_CIRC_REASON_INTERNAL; - } - cc.handshake_len = len; - - if (circuit_deliver_create_cell(TO_CIRCUIT(circ), &cc, 0) < 0) - return - END_CIRC_REASON_RESOURCELIMIT; - - circ->cpath->state = CPATH_STATE_AWAITING_KEYS; - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); - log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'", - fast ? "CREATE_FAST" : "CREATE", - node ? node_describe(node) : ""); - } else { - extend_cell_t ec; - int len; - tor_assert(circ->cpath->state == CPATH_STATE_OPEN); - tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING); - log_debug(LD_CIRC,"starting to send subsequent skin."); - hop = onion_next_hop_in_cpath(circ->cpath); - memset(&ec, 0, sizeof(ec)); - if (!hop) { - /* done building the circuit. whew. */ - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); - if (circuit_timeout_want_to_count_circ(circ)) { - struct timeval end; - long timediff; - tor_gettimeofday(&end); - timediff = tv_mdiff(&circ->base_.timestamp_began, &end); - - /* - * If the circuit build time is much greater than we would have cut - * it off at, we probably had a suspend event along this codepath, - * and we should discard the value. - */ - if (timediff < 0 || - timediff > 2*get_circuit_build_close_time_ms()+1000) { - log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " - "Assuming clock jump. Purpose %d (%s)", timediff, - circ->base_.purpose, - circuit_purpose_to_string(circ->base_.purpose)); - } else if (!circuit_build_times_disabled()) { - /* Only count circuit times if the network is live */ - if (circuit_build_times_network_check_live( - get_circuit_build_times())) { - circuit_build_times_add_time(get_circuit_build_times_mutable(), - (build_time_t)timediff); - circuit_build_times_set_timeout(get_circuit_build_times_mutable()); - } - - if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - circuit_build_times_network_circ_success( - get_circuit_build_times_mutable()); - } - } - } - log_info(LD_CIRC,"circuit built!"); - circuit_reset_failure_count(0); - - if (circ->build_state->onehop_tunnel || circ->has_opened) { - control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0); - } - - pathbias_count_build_success(circ); - circuit_rep_hist_note_result(circ); - circuit_has_opened(circ); /* do other actions as necessary */ - - if (!can_complete_circuit && !circ->build_state->onehop_tunnel) { - const or_options_t *options = get_options(); - can_complete_circuit=1; - /* FFFF Log a count of known routers here */ - log_notice(LD_GENERAL, - "Tor has successfully opened a circuit. " - "Looks like client functionality is working."); - control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0); - control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); - clear_broken_connection_map(1); - if (server_mode(options) && !check_whether_orport_reachable()) { - inform_testing_reachability(); - consider_testing_reachability(1, 1); - } - } - - /* We're done with measurement circuits here. Just close them */ - if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED); - } - return 0; - } - - if (tor_addr_family(&hop->extend_info->addr) != AF_INET) { - log_warn(LD_BUG, "Trying to extend to a non-IPv4 address."); - return - END_CIRC_REASON_INTERNAL; - } - - { - const node_t *prev_node; - prev_node = node_get_by_id(hop->prev->extend_info->identity_digest); - circuit_pick_extend_handshake(&ec.cell_type, - &ec.create_cell.cell_type, - &ec.create_cell.handshake_type, - prev_node, - hop->extend_info); - } - - tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr); - ec.orport_ipv4.port = hop->extend_info->port; - tor_addr_make_unspec(&ec.orport_ipv6.addr); - memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN); - - len = onion_skin_create(ec.create_cell.handshake_type, - hop->extend_info, - &hop->handshake_state, - ec.create_cell.onionskin); - if (len < 0) { - log_warn(LD_CIRC,"onion_skin_create failed."); - return - END_CIRC_REASON_INTERNAL; - } - ec.create_cell.handshake_len = len; - - log_info(LD_CIRC,"Sending extend relay cell."); - note_request("cell: extend", 1); - { - uint8_t command = 0; - uint16_t payload_len=0; - uint8_t payload[RELAY_PAYLOAD_SIZE]; - if (extend_cell_format(&command, &payload_len, payload, &ec)<0) { - log_warn(LD_CIRC,"Couldn't format extend cell"); - return -END_CIRC_REASON_INTERNAL; - } - - /* send it to hop->prev, because it will transfer - * it to a create cell and then send to hop */ - if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), - command, - (char*)payload, payload_len, - hop->prev) < 0) - return 0; /* circuit is closed */ - } - hop->state = CPATH_STATE_AWAITING_KEYS; - } - return 0; -} - -/** Our clock just jumped by seconds_elapsed. Assume - * something has also gone wrong with our network: notify the user, - * and abandon all not-yet-used circuits. */ -void -circuit_note_clock_jumped(int seconds_elapsed) -{ - int severity = server_mode(get_options()) ? LOG_WARN : LOG_NOTICE; - tor_log(severity, LD_GENERAL, "Your system clock just jumped %d seconds %s; " - "assuming established circuits no longer work.", - seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed, - seconds_elapsed >=0 ? "forward" : "backward"); - control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME=%d", - seconds_elapsed); - can_complete_circuit=0; /* so it'll log when it works again */ - control_event_client_status(severity, "CIRCUIT_NOT_ESTABLISHED REASON=%s", - "CLOCK_JUMPED"); - circuit_mark_all_unused_circs(); - circuit_mark_all_dirty_circs_as_unusable(); -} - -/** Take the 'extend' cell, pull out addr/port plus the onion - * skin and identity digest for the next hop. If we're already connected, - * pass the onion skin to the next hop using a create cell; otherwise - * launch a new OR connection, and circ will notice when the - * connection succeeds or fails. - * - * Return -1 if we want to warn and tear down the circuit, else return 0. - */ -int -circuit_extend(cell_t *cell, circuit_t *circ) -{ - channel_t *n_chan; - relay_header_t rh; - extend_cell_t ec; - const char *msg = NULL; - int should_launch = 0; - - if (circ->n_chan) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "n_chan already set. Bug/attack. Closing."); - return -1; - } - if (circ->n_hop) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "conn to next hop already launched. Bug/attack. Closing."); - return -1; - } - - if (!server_mode(get_options())) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Got an extend cell, but running as a client. Closing."); - return -1; - } - - relay_header_unpack(&rh, cell->payload); - - if (extend_cell_parse(&ec, rh.command, - cell->payload+RELAY_HEADER_SIZE, - rh.length) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Can't parse extend cell. Closing circuit."); - return -1; - } - - if (!ec.orport_ipv4.port || tor_addr_is_null(&ec.orport_ipv4.addr)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend to zero destination port or addr."); - return -1; - } - - if (tor_addr_is_internal(&ec.orport_ipv4.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 - * fingerprints -- a) because it opens the user up to a mitm attack, - * and b) because it lets an attacker force the relay to hold open a - * new TLS connection for each extend request. */ - if (tor_digest_is_zero((const char*)ec.node_id)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend without specifying an id_digest."); - return -1; - } - - /* Next, check if we're being asked to connect to the hop that the - * extend cell came from. There isn't any reason for that, and it can - * assist circular-path attacks. */ - if (tor_memeq(ec.node_id, - TO_OR_CIRCUIT(circ)->p_chan->identity_digest, - DIGEST_LEN)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend back to the previous hop."); - return -1; - } - - n_chan = channel_get_for_extend((const char*)ec.node_id, - &ec.orport_ipv4.addr, - &msg, - &should_launch); - - if (!n_chan) { - log_debug(LD_CIRC|LD_OR,"Next router (%s): %s", - fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port), - msg?msg:"????"); - - circ->n_hop = extend_info_new(NULL /*nickname*/, - (const char*)ec.node_id, - NULL /*onion_key*/, - NULL /*curve25519_key*/, - &ec.orport_ipv4.addr, - ec.orport_ipv4.port); - - circ->n_chan_create_cell = tor_memdup(&ec.create_cell, - sizeof(ec.create_cell)); - - circuit_set_state(circ, CIRCUIT_STATE_CHAN_WAIT); - - if (should_launch) { - /* we should try to open a connection */ - n_chan = channel_connect_for_circuit(&ec.orport_ipv4.addr, - ec.orport_ipv4.port, - (const char*)ec.node_id); - if (!n_chan) { - log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); - circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); - return 0; - } - log_debug(LD_CIRC,"connecting in progress (or finished). Good."); - } - /* return success. The onion/circuit/etc will be taken care of - * automatically (may already have been) whenever n_chan reaches - * OR_CONN_STATE_OPEN. - */ - return 0; - } - - tor_assert(!circ->n_hop); /* Connection is already established. */ - circ->n_chan = n_chan; - log_debug(LD_CIRC, - "n_chan is %s", - channel_get_canonical_remote_descr(n_chan)); - - if (circuit_deliver_create_cell(circ, &ec.create_cell, 1) < 0) - return -1; - - return 0; -} - -/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in - * key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are - * used as follows: - * - 20 to initialize f_digest - * - 20 to initialize b_digest - * - 16 to key f_crypto - * - 16 to key b_crypto - * - * (If 'reverse' is true, then f_XX and b_XX are swapped.) - */ -int -circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, - int reverse) -{ - crypto_digest_t *tmp_digest; - crypto_cipher_t *tmp_crypto; - - tor_assert(cpath); - tor_assert(key_data); - tor_assert(!(cpath->f_crypto || cpath->b_crypto || - cpath->f_digest || cpath->b_digest)); - - cpath->f_digest = crypto_digest_new(); - crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); - cpath->b_digest = crypto_digest_new(); - crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); - - if (!(cpath->f_crypto = - crypto_cipher_new(key_data+(2*DIGEST_LEN)))) { - log_warn(LD_BUG,"Forward cipher initialization failed."); - return -1; - } - if (!(cpath->b_crypto = - crypto_cipher_new(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN))) { - log_warn(LD_BUG,"Backward cipher initialization failed."); - return -1; - } - - if (reverse) { - tmp_digest = cpath->f_digest; - cpath->f_digest = cpath->b_digest; - cpath->b_digest = tmp_digest; - tmp_crypto = cpath->f_crypto; - cpath->f_crypto = cpath->b_crypto; - cpath->b_crypto = tmp_crypto; - } - - return 0; -} - -/** A "created" cell reply came back to us on circuit circ. - * (The body of reply varies depending on what sort of handshake - * this is.) - * - * Calculate the appropriate keys and digests, make sure KH is - * correct, and initialize this hop of the cpath. - * - * Return - reason if we want to mark circ for close, else return 0. - */ -int -circuit_finish_handshake(origin_circuit_t *circ, - const created_cell_t *reply) -{ - char keys[CPATH_KEY_MATERIAL_LEN]; - crypt_path_t *hop; - int rv; - - if ((rv = pathbias_count_build_attempt(circ)) < 0) - return rv; - - if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) { - hop = circ->cpath; - } else { - hop = onion_next_hop_in_cpath(circ->cpath); - if (!hop) { /* got an extended when we're all done? */ - log_warn(LD_PROTOCOL,"got extended when circ already built? Closing."); - return - END_CIRC_REASON_TORPROTOCOL; - } - } - tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS); - - { - if (onion_skin_client_handshake(hop->handshake_state.tag, - &hop->handshake_state, - reply->reply, reply->handshake_len, - (uint8_t*)keys, sizeof(keys), - (uint8_t*)hop->rend_circ_nonce) < 0) { - log_warn(LD_CIRC,"onion_skin_client_handshake failed."); - return -END_CIRC_REASON_TORPROTOCOL; - } - } - - onion_handshake_state_release(&hop->handshake_state); - - if (circuit_init_cpath_crypto(hop, keys, 0)<0) { - return -END_CIRC_REASON_TORPROTOCOL; - } - - hop->state = CPATH_STATE_OPEN; - log_info(LD_CIRC,"Finished building circuit hop:"); - circuit_log_path(LOG_INFO,LD_CIRC,circ); - control_event_circuit_status(circ, CIRC_EVENT_EXTENDED, 0); - - return 0; -} - -/** We received a relay truncated cell on circ. - * - * Since we don't send truncates currently, getting a truncated - * means that a connection broke or an extend failed. For now, - * just give up: force circ to close, and return 0. - */ -int -circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason) -{ -// crypt_path_t *victim; -// connection_t *stream; - - tor_assert(circ); - tor_assert(layer); - - /* XXX Since we don't send truncates currently, getting a truncated - * means that a connection broke or an extend failed. For now, - * just give up. - */ - circuit_mark_for_close(TO_CIRCUIT(circ), - END_CIRC_REASON_FLAG_REMOTE|reason); - return 0; - -#if 0 - while (layer->next != circ->cpath) { - /* we need to clear out layer->next */ - victim = layer->next; - log_debug(LD_CIRC, "Killing a layer of the cpath."); - - for (stream = circ->p_streams; stream; stream=stream->next_stream) { - if (stream->cpath_layer == victim) { - log_info(LD_APP, "Marking stream %d for close because of truncate.", - stream->stream_id); - /* no need to send 'end' relay cells, - * because the other side's already dead - */ - connection_mark_unattached_ap(stream, END_STREAM_REASON_DESTROY); - } - } - - layer->next = victim->next; - circuit_free_cpath_node(victim); - } - - log_info(LD_CIRC, "finished"); - return 0; -#endif -} - -/** Given a response payload and keys, initialize, then send a created - * cell back. - */ -int -onionskin_answer(or_circuit_t *circ, - const created_cell_t *created_cell, - const char *keys, - const uint8_t *rend_circ_nonce) -{ - cell_t cell; - crypt_path_t *tmp_cpath; - - if (created_cell_format(&cell, created_cell) < 0) { - log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d)", - (int)created_cell->cell_type, (int)created_cell->handshake_len); - return -1; - } - cell.circ_id = circ->p_circ_id; - - tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t)); - tmp_cpath->magic = CRYPT_PATH_MAGIC; - - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); - - log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.", - (unsigned int)get_uint32(keys), - (unsigned int)get_uint32(keys+20)); - if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { - log_warn(LD_BUG,"Circuit initialization failed"); - tor_free(tmp_cpath); - return -1; - } - circ->n_digest = tmp_cpath->f_digest; - circ->n_crypto = tmp_cpath->f_crypto; - circ->p_digest = tmp_cpath->b_digest; - circ->p_crypto = tmp_cpath->b_crypto; - tmp_cpath->magic = 0; - tor_free(tmp_cpath); - - memcpy(circ->rend_circ_nonce, rend_circ_nonce, DIGEST_LEN); - - circ->is_first_hop = (created_cell->cell_type == CELL_CREATED_FAST); - - append_cell_to_circuit_queue(TO_CIRCUIT(circ), - circ->p_chan, &cell, CELL_DIRECTION_IN, 0); - log_debug(LD_CIRC,"Finished sending '%s' cell.", - circ->is_first_hop ? "created_fast" : "created"); - - if (!channel_is_local(circ->p_chan) && - !channel_is_outgoing(circ->p_chan)) { - /* record that we could process create cells from a non-local conn - * that we didn't initiate; presumably this means that create cells - * can reach us too. */ - router_orport_found_reachable(); - } - - return 0; -} - -/** Choose a length for a circuit of purpose purpose: three + the - * number of endpoints that would give something away about our destination. - * - * If the routerlist nodes doesn't have enough routers - * to handle the desired path length, return -1. - */ -static int -new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes) -{ - int num_acceptable_routers; - int routelen; - - tor_assert(nodes); - - routelen = DEFAULT_ROUTE_LEN; - if (exit && - purpose != CIRCUIT_PURPOSE_TESTING && - purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) - routelen++; - - num_acceptable_routers = count_acceptable_nodes(nodes); - - log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).", - routelen, num_acceptable_routers, smartlist_len(nodes)); - - if (num_acceptable_routers < routelen) { - log_info(LD_CIRC, - "Not enough acceptable routers (%d/%d). Discarding this circuit.", - num_acceptable_routers, routelen); - return -1; - } - - return routelen; -} - -/** Return a newly allocated list of uint16_t * for each predicted port not - * handled by a current circuit. */ -static smartlist_t * -circuit_get_unhandled_ports(time_t now) -{ - smartlist_t *dest = rep_hist_get_predicted_ports(now); - circuit_remove_handled_ports(dest); - return dest; -} - -/** Return 1 if we already have circuits present or on the way for - * all anticipated ports. Return 0 if we should make more. - * - * If we're returning 0, set need_uptime and need_capacity to - * indicate any requirements that the unhandled ports have. - */ -int -circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity) -{ - int i, enough; - uint16_t *port; - smartlist_t *sl = circuit_get_unhandled_ports(now); - smartlist_t *LongLivedServices = get_options()->LongLivedPorts; - tor_assert(need_uptime); - tor_assert(need_capacity); - // Always predict need_capacity - *need_capacity = 1; - enough = (smartlist_len(sl) == 0); - for (i = 0; i < smartlist_len(sl); ++i) { - port = smartlist_get(sl, i); - if (smartlist_contains_int_as_string(LongLivedServices, *port)) - *need_uptime = 1; - tor_free(port); - } - smartlist_free(sl); - return enough; -} - -/** Return 1 if node can handle one or more of the ports in - * needed_ports, else return 0. - */ -static int -node_handles_some_port(const node_t *node, smartlist_t *needed_ports) -{ /* XXXX MOVE */ - int i; - uint16_t port; - - for (i = 0; i < smartlist_len(needed_ports); ++i) { - addr_policy_result_t r; - /* alignment issues aren't a worry for this dereference, since - needed_ports is explicitly a smartlist of uint16_t's */ - port = *(uint16_t *)smartlist_get(needed_ports, i); - tor_assert(port); - if (node) - r = compare_tor_addr_to_node_policy(NULL, port, node); - else - continue; - if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED) - return 1; - } - return 0; -} - -/** Return true iff conn needs another general circuit to be - * built. */ -static int -ap_stream_wants_exit_attention(connection_t *conn) -{ - entry_connection_t *entry; - if (conn->type != CONN_TYPE_AP) - return 0; - entry = TO_ENTRY_CONN(conn); - - if (conn->state == AP_CONN_STATE_CIRCUIT_WAIT && - !conn->marked_for_close && - !(entry->want_onehop) && /* ignore one-hop streams */ - !(entry->use_begindir) && /* ignore targeted dir fetches */ - !(entry->chosen_exit_name) && /* ignore defined streams */ - !connection_edge_is_rendezvous_stream(TO_EDGE_CONN(conn)) && - !circuit_stream_is_being_handled(TO_ENTRY_CONN(conn), 0, - MIN_CIRCUITS_HANDLING_STREAM)) - return 1; - return 0; -} - -/** Return a pointer to a suitable router to be the exit node for the - * general-purpose circuit we're about to build. - * - * Look through the connection array, and choose a router that maximizes - * the number of pending streams that can exit from this router. - * - * Return NULL if we can't find any suitable routers. - */ -static const node_t * -choose_good_exit_server_general(int need_uptime, int need_capacity) -{ - int *n_supported; - int n_pending_connections = 0; - smartlist_t *connections; - int best_support = -1; - int n_best_support=0; - const or_options_t *options = get_options(); - const smartlist_t *the_nodes; - const node_t *node=NULL; - - connections = get_connection_array(); - - /* Count how many connections are waiting for a circuit to be built. - * We use this for log messages now, but in the future we may depend on it. - */ - SMARTLIST_FOREACH(connections, connection_t *, conn, - { - if (ap_stream_wants_exit_attention(conn)) - ++n_pending_connections; - }); -// log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending", -// n_pending_connections); - /* Now we count, for each of the routers in the directory, how many - * of the pending connections could possibly exit from that - * router (n_supported[i]). (We can't be sure about cases where we - * don't know the IP address of the pending connection.) - * - * -1 means "Don't use this router at all." - */ - the_nodes = nodelist_get_list(); - n_supported = tor_malloc(sizeof(int)*smartlist_len(the_nodes)); - SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) { - const int i = node_sl_idx; - if (router_digest_is_me(node->identity)) { - n_supported[i] = -1; -// log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname); - /* XXX there's probably a reverse predecessor attack here, but - * it's slow. should we take this out? -RD - */ - continue; - } - if (!node_has_descriptor(node)) { - n_supported[i] = -1; - continue; - } - if (!node->is_running || node->is_bad_exit) { - n_supported[i] = -1; - continue; /* skip routers that are known to be down or bad exits */ - } - if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) { - /* never pick a non-general node as a random exit. */ - n_supported[i] = -1; - continue; - } - if (routerset_contains_node(options->ExcludeExitNodesUnion_, node)) { - n_supported[i] = -1; - continue; /* user asked us not to use it, no matter what */ - } - if (options->ExitNodes && - !routerset_contains_node(options->ExitNodes, node)) { - n_supported[i] = -1; - continue; /* not one of our chosen exit nodes */ - } - - if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { - n_supported[i] = -1; - continue; /* skip routers that are not suitable. Don't worry if - * this makes us reject all the possible routers: if so, - * we'll retry later in this function with need_update and - * need_capacity set to 0. */ - } - if (!(node->is_valid || options->AllowInvalid_ & ALLOW_INVALID_EXIT)) { - /* if it's invalid and we don't want it */ - n_supported[i] = -1; -// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.", -// router->nickname, i); - continue; /* skip invalid routers */ - } - if (options->ExcludeSingleHopRelays && - node_allows_single_hop_exits(node)) { - n_supported[i] = -1; - continue; - } - if (node_exit_policy_rejects_all(node)) { - n_supported[i] = -1; -// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", -// router->nickname, i); - continue; /* skip routers that reject all */ - } - n_supported[i] = 0; - /* iterate over connections */ - SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) { - if (!ap_stream_wants_exit_attention(conn)) - continue; /* Skip everything but APs in CIRCUIT_WAIT */ - if (connection_ap_can_use_exit(TO_ENTRY_CONN(conn), node)) { - ++n_supported[i]; -// log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", -// router->nickname, i, n_supported[i]); - } else { -// log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.", -// router->nickname, i); - } - } SMARTLIST_FOREACH_END(conn); - if (n_pending_connections > 0 && n_supported[i] == 0) { - /* Leave best_support at -1 if that's where it is, so we can - * distinguish it later. */ - continue; - } - if (n_supported[i] > best_support) { - /* If this router is better than previous ones, remember its index - * and goodness, and start counting how many routers are this good. */ - best_support = n_supported[i]; n_best_support=1; -// log_fn(LOG_DEBUG,"%s is new best supported option so far.", -// router->nickname); - } else if (n_supported[i] == best_support) { - /* If this router is _as good_ as the best one, just increment the - * count of equally good routers.*/ - ++n_best_support; - } - } SMARTLIST_FOREACH_END(node); - log_info(LD_CIRC, - "Found %d servers that might support %d/%d pending connections.", - n_best_support, best_support >= 0 ? best_support : 0, - n_pending_connections); - - /* If any routers definitely support any pending connections, choose one - * at random. */ - if (best_support > 0) { - smartlist_t *supporting = smartlist_new(); - - SMARTLIST_FOREACH(the_nodes, const node_t *, node, { - if (n_supported[node_sl_idx] == best_support) - smartlist_add(supporting, (void*)node); - }); - - node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); - smartlist_free(supporting); - } else { - /* Either there are no pending connections, or no routers even seem to - * possibly support any of them. Choose a router at random that satisfies - * at least one predicted exit port. */ - - int attempt; - smartlist_t *needed_ports, *supporting; - - if (best_support == -1) { - if (need_uptime || need_capacity) { - log_info(LD_CIRC, - "We couldn't find any live%s%s routers; falling back " - "to list of all routers.", - need_capacity?", fast":"", - need_uptime?", stable":""); - tor_free(n_supported); - return choose_good_exit_server_general(0, 0); - } - log_notice(LD_CIRC, "All routers are down or won't exit%s -- " - "choosing a doomed exit at random.", - options->ExcludeExitNodesUnion_ ? " or are Excluded" : ""); - } - supporting = smartlist_new(); - needed_ports = circuit_get_unhandled_ports(time(NULL)); - for (attempt = 0; attempt < 2; attempt++) { - /* try once to pick only from routers that satisfy a needed port, - * then if there are none, pick from any that support exiting. */ - SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) { - if (n_supported[node_sl_idx] != -1 && - (attempt || node_handles_some_port(node, needed_ports))) { -// log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.", -// try, router->nickname); - smartlist_add(supporting, (void*)node); - } - } SMARTLIST_FOREACH_END(node); - - node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); - if (node) - break; - smartlist_clear(supporting); - /* If we reach this point, we can't actually support any unhandled - * predicted ports, so clear all the remaining ones. */ - if (smartlist_len(needed_ports)) - rep_hist_remove_predicted_ports(needed_ports); - } - SMARTLIST_FOREACH(needed_ports, uint16_t *, cp, tor_free(cp)); - smartlist_free(needed_ports); - smartlist_free(supporting); - } - - tor_free(n_supported); - if (node) { - log_info(LD_CIRC, "Chose exit server '%s'", node_describe(node)); - return node; - } - if (options->ExitNodes) { - log_warn(LD_CIRC, - "No specified %sexit routers seem to be running: " - "can't choose an exit.", - options->ExcludeExitNodesUnion_ ? "non-excluded " : ""); - } - return NULL; -} - -/** Return a pointer to a suitable router to be the exit node for the - * circuit of purpose purpose that we're about to build (or NULL - * if no router is suitable). - * - * For general-purpose circuits, pass it off to - * choose_good_exit_server_general() - * - * For client-side rendezvous circuits, choose a random node, weighted - * toward the preferences in 'options'. - */ -static const node_t * -choose_good_exit_server(uint8_t purpose, - int need_uptime, int need_capacity, int is_internal) -{ - const or_options_t *options = get_options(); - router_crn_flags_t flags = CRN_NEED_DESC; - if (need_uptime) - flags |= CRN_NEED_UPTIME; - if (need_capacity) - flags |= CRN_NEED_CAPACITY; - - switch (purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - if (options->AllowInvalid_ & ALLOW_INVALID_MIDDLE) - flags |= CRN_ALLOW_INVALID; - if (is_internal) /* pick it like a middle hop */ - return router_choose_random_node(NULL, options->ExcludeNodes, flags); - else - return choose_good_exit_server_general(need_uptime,need_capacity); - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - if (options->AllowInvalid_ & ALLOW_INVALID_RENDEZVOUS) - flags |= CRN_ALLOW_INVALID; - return router_choose_random_node(NULL, options->ExcludeNodes, flags); - } - log_warn(LD_BUG,"Unhandled purpose %d", purpose); - tor_fragile_assert(); - return NULL; -} - -/** Log a warning if the user specified an exit for the circuit that - * has been excluded from use by ExcludeNodes or ExcludeExitNodes. */ -static void -warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) -{ - const or_options_t *options = get_options(); - routerset_t *rs = options->ExcludeNodes; - const char *description; - uint8_t purpose = circ->base_.purpose; - - if (circ->build_state->onehop_tunnel) - return; - - switch (purpose) - { - default: - case CIRCUIT_PURPOSE_OR: - case CIRCUIT_PURPOSE_INTRO_POINT: - case CIRCUIT_PURPOSE_REND_POINT_WAITING: - case CIRCUIT_PURPOSE_REND_ESTABLISHED: - log_warn(LD_BUG, "Called on non-origin circuit (purpose %d, %s)", - (int)purpose, - circuit_purpose_to_string(purpose)); - return; - case CIRCUIT_PURPOSE_C_GENERAL: - if (circ->build_state->is_internal) - return; - description = "requested exit node"; - rs = options->ExcludeExitNodesUnion_; - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - case CIRCUIT_PURPOSE_S_CONNECT_REND: - case CIRCUIT_PURPOSE_S_REND_JOINED: - case CIRCUIT_PURPOSE_TESTING: - return; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - case CIRCUIT_PURPOSE_C_REND_READY: - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - case CIRCUIT_PURPOSE_C_REND_JOINED: - description = "chosen rendezvous point"; - break; - case CIRCUIT_PURPOSE_CONTROLLER: - rs = options->ExcludeExitNodesUnion_; - description = "controller-selected circuit target"; - break; - } - - if (routerset_contains_extendinfo(rs, exit)) { - /* We should never get here if StrictNodes is set to 1. */ - if (options->StrictNodes) { - log_warn(LD_BUG, "Using %s '%s' which is listed in ExcludeNodes%s, " - "even though StrictNodes is set. Please report. " - "(Circuit purpose: %s)", - description, extend_info_describe(exit), - rs==options->ExcludeNodes?"":" or ExcludeExitNodes", - circuit_purpose_to_string(purpose)); - } else { - log_warn(LD_CIRC, "Using %s '%s' which is listed in " - "ExcludeNodes%s, because no better options were available. To " - "prevent this (and possibly break your Tor functionality), " - "set the StrictNodes configuration option. " - "(Circuit purpose: %s)", - description, extend_info_describe(exit), - rs==options->ExcludeNodes?"":" or ExcludeExitNodes", - circuit_purpose_to_string(purpose)); - } - circuit_log_path(LOG_WARN, LD_CIRC, circ); - } - - return; -} - -/** Decide a suitable length for circ's cpath, and pick an exit - * router (or use exit if provided). Store these in the - * cpath. Return 0 if ok, -1 if circuit should be closed. */ -static int -onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) -{ - cpath_build_state_t *state = circ->build_state; - - if (state->onehop_tunnel) { - log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel."); - state->desired_path_len = 1; - } else { - int r = new_route_len(circ->base_.purpose, exit, nodelist_get_list()); - if (r < 1) /* must be at least 1 */ - return -1; - state->desired_path_len = r; - } - - if (exit) { /* the circuit-builder pre-requested one */ - warn_if_last_router_excluded(circ, exit); - log_info(LD_CIRC,"Using requested exit node '%s'", - extend_info_describe(exit)); - exit = extend_info_dup(exit); - } else { /* we have to decide one */ - const node_t *node = - choose_good_exit_server(circ->base_.purpose, state->need_uptime, - state->need_capacity, state->is_internal); - if (!node) { - log_warn(LD_CIRC,"failed to choose an exit server"); - return -1; - } - exit = extend_info_from_node(node, 0); - tor_assert(exit); - } - state->chosen_exit = exit; - return 0; -} - -/** Give circ a new exit destination to exit, and add a - * hop to the cpath reflecting this. Don't send the next extend cell -- - * the caller will do this if it wants to. - */ -int -circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *exit) -{ - cpath_build_state_t *state; - tor_assert(exit); - tor_assert(circ); - - state = circ->build_state; - tor_assert(state); - extend_info_free(state->chosen_exit); - state->chosen_exit = extend_info_dup(exit); - - ++circ->build_state->desired_path_len; - onion_append_hop(&circ->cpath, exit); - return 0; -} - -/** Take an open circ, and add a new hop at the end, based on - * info. Set its state back to CIRCUIT_STATE_BUILDING, and then - * send the next extend cell to begin connecting to that hop. - */ -int -circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit) -{ - int err_reason = 0; - warn_if_last_router_excluded(circ, exit); - - tor_gettimeofday(&circ->base_.timestamp_began); - - circuit_append_new_exit(circ, exit); - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); - if ((err_reason = circuit_send_next_onion_skin(circ))<0) { - log_warn(LD_CIRC, "Couldn't extend circuit to new point %s.", - extend_info_describe(exit)); - circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason); - return -1; - } - - // XXX: Should cannibalized circuits be dirty or not? Not easy to say.. - - return 0; -} - -/** Return the number of routers in routers that are currently up - * and available for building circuits through. - */ -static int -count_acceptable_nodes(smartlist_t *nodes) -{ - int num=0; - - SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { - // log_debug(LD_CIRC, -// "Contemplating whether router %d (%s) is a new option.", -// i, r->nickname); - if (! node->is_running) -// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); - continue; - if (! node->is_valid) -// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); - continue; - if (! node_has_descriptor(node)) - continue; - /* XXX This clause makes us count incorrectly: if AllowInvalidRouters - * allows this node in some places, then we're getting an inaccurate - * count. For now, be conservative and don't count it. But later we - * should try to be smarter. */ - ++num; - } SMARTLIST_FOREACH_END(node); - -// log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num); - - return num; -} - -/** Add new_hop to the end of the doubly-linked-list head_ptr. - * This function is used to extend cpath by another hop. - */ -void -onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop) -{ - if (*head_ptr) { - new_hop->next = (*head_ptr); - new_hop->prev = (*head_ptr)->prev; - (*head_ptr)->prev->next = new_hop; - (*head_ptr)->prev = new_hop; - } else { - *head_ptr = new_hop; - new_hop->prev = new_hop->next = new_hop; - } -} - -/** A helper function used by onion_extend_cpath(). Use purpose - * and state and the cpath head (currently populated only - * to length cur_len to decide a suitable middle hop for a - * circuit. In particular, make sure we don't pick the exit node or its - * family, and make sure we don't duplicate any previous nodes or their - * families. */ -static const node_t * -choose_good_middle_server(uint8_t purpose, - cpath_build_state_t *state, - crypt_path_t *head, - int cur_len) -{ - int i; - const node_t *r, *choice; - crypt_path_t *cpath; - smartlist_t *excluded; - const or_options_t *options = get_options(); - router_crn_flags_t flags = CRN_NEED_DESC; - tor_assert(CIRCUIT_PURPOSE_MIN_ <= purpose && - purpose <= CIRCUIT_PURPOSE_MAX_); - - log_debug(LD_CIRC, "Contemplating intermediate hop: random choice."); - excluded = smartlist_new(); - if ((r = build_state_get_exit_node(state))) { - nodelist_add_node_and_family(excluded, r); - } - for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) { - if ((r = node_get_by_id(cpath->extend_info->identity_digest))) { - nodelist_add_node_and_family(excluded, r); - } - } - - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; - if (options->AllowInvalid_ & ALLOW_INVALID_MIDDLE) - flags |= CRN_ALLOW_INVALID; - choice = router_choose_random_node(excluded, options->ExcludeNodes, flags); - smartlist_free(excluded); - return choice; -} - -/** Pick a good entry server for the circuit to be built according to - * state. Don't reuse a chosen exit (if any), don't use this - * router (if we're an OR), and respect firewall settings; if we're - * configured to use entry guards, return one. - * - * If state is NULL, we're choosing a router to serve as an entry - * guard, not for any particular circuit. - */ -/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */ -const node_t * -choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) -{ - const node_t *choice; - smartlist_t *excluded; - const or_options_t *options = get_options(); - router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC; - const node_t *node; - - if (state && options->UseEntryGuards && - (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) { - /* This request is for an entry server to use for a regular circuit, - * and we use entry guard nodes. Just return one of the guard nodes. */ - return choose_random_entry(state); - } - - excluded = smartlist_new(); - - if (state && (node = build_state_get_exit_node(state))) { - /* Exclude the exit node from the state, if we have one. Also exclude its - * family. */ - nodelist_add_node_and_family(excluded, node); - } - if (firewall_is_fascist_or()) { - /* Exclude all ORs that we can't reach through our firewall */ - smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, const node_t *, node, { - if (!fascist_firewall_allows_node(node)) - smartlist_add(excluded, (void*)node); - }); - } - /* and exclude current entry guards and their families, if applicable */ - /*XXXX025 use the using_as_guard flag to accomplish this.*/ - if (options->UseEntryGuards) { - SMARTLIST_FOREACH(get_entry_guards(), const entry_guard_t *, entry, - { - if ((node = node_get_by_id(entry->identity))) { - nodelist_add_node_and_family(excluded, node); - } - }); - } - - if (state) { - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; - } - if (options->AllowInvalid_ & ALLOW_INVALID_ENTRY) - flags |= CRN_ALLOW_INVALID; - - choice = router_choose_random_node(excluded, options->ExcludeNodes, flags); - smartlist_free(excluded); - return choice; -} - -/** Return the first non-open hop in cpath, or return NULL if all - * hops are open. */ -static crypt_path_t * -onion_next_hop_in_cpath(crypt_path_t *cpath) -{ - crypt_path_t *hop = cpath; - do { - if (hop->state != CPATH_STATE_OPEN) - return hop; - hop = hop->next; - } while (hop != cpath); - return NULL; -} - -/** Choose a suitable next hop in the cpath head_ptr, - * based on state. Append the hop info to head_ptr. - * - * Return 1 if the path is complete, 0 if we successfully added a hop, - * and -1 on error. - */ -static int -onion_extend_cpath(origin_circuit_t *circ) -{ - uint8_t purpose = circ->base_.purpose; - cpath_build_state_t *state = circ->build_state; - int cur_len = circuit_get_cpath_len(circ); - extend_info_t *info = NULL; - - if (cur_len >= state->desired_path_len) { - log_debug(LD_CIRC, "Path is complete: %d steps long", - state->desired_path_len); - return 1; - } - - log_debug(LD_CIRC, "Path is %d long; we want %d", cur_len, - state->desired_path_len); - - if (cur_len == state->desired_path_len - 1) { /* Picking last node */ - info = extend_info_dup(state->chosen_exit); - } else if (cur_len == 0) { /* picking first node */ - const node_t *r = choose_good_entry_server(purpose, state); - if (r) { - /* 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 { - const node_t *r = - choose_good_middle_server(purpose, state, circ->cpath, cur_len); - if (r) { - info = extend_info_from_node(r, 0); - tor_assert(info); - } - } - - if (!info) { - log_warn(LD_CIRC,"Failed to find node for hop %d of our path. Discarding " - "this circuit.", cur_len); - return -1; - } - - log_debug(LD_CIRC,"Chose router %s for hop %d (exit is %s)", - extend_info_describe(info), - cur_len+1, build_state_get_exit_nickname(state)); - - onion_append_hop(&circ->cpath, info); - extend_info_free(info); - return 0; -} - -/** Create a new hop, annotate it with information about its - * corresponding router choice, and append it to the - * end of the cpath head_ptr. */ -static int -onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) -{ - crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); - - /* link hop into the cpath, at the end. */ - onion_append_to_cpath(head_ptr, hop); - - hop->magic = CRYPT_PATH_MAGIC; - hop->state = CPATH_STATE_CLOSED; - - hop->extend_info = extend_info_dup(choice); - - hop->package_window = circuit_initial_package_window(); - hop->deliver_window = CIRCWINDOW_START; - - return 0; -} - -/** Allocate a new extend_info object based on the various arguments. */ -extend_info_t * -extend_info_new(const char *nickname, const char *digest, - crypto_pk_t *onion_key, - const curve25519_public_key_t *curve25519_key, - const tor_addr_t *addr, uint16_t port) -{ - extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); - memcpy(info->identity_digest, digest, DIGEST_LEN); - if (nickname) - strlcpy(info->nickname, nickname, sizeof(info->nickname)); - if (onion_key) - info->onion_key = crypto_pk_dup_key(onion_key); -#ifdef CURVE25519_ENABLED - if (curve25519_key) - memcpy(&info->curve25519_onion_key, curve25519_key, - sizeof(curve25519_public_key_t)); -#else - (void)curve25519_key; -#endif - tor_addr_copy(&info->addr, addr); - info->port = port; - return info; -} - -/** Allocate and return a new extend_info that can be used to build a - * circuit to or through the node node. Use the primary address - * of the node (i.e. its IPv4 address) unless - * for_direct_connect is true, in which case the preferred - * address is used instead. May return NULL if there is not enough - * info about node 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) -{ - tor_addr_port_t ap; - - if (node->ri == NULL && (node->rs == NULL || node->md == NULL)) - return NULL; - - if (for_direct_connect) - node_get_pref_orport(node, &ap); - else - node_get_prim_orport(node, &ap); - - log_debug(LD_CIRC, "using %s for %s", - fmt_addrport(&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, - node->ri->onion_curve25519_pkey, - &ap.addr, - ap.port); - else if (node->rs && node->md) - return extend_info_new(node->rs->nickname, - node->identity, - node->md->onion_pkey, - node->md->onion_curve25519_pkey, - &ap.addr, - ap.port); - else - return NULL; -} - -/** Release storage held by an extend_info_t struct. */ -void -extend_info_free(extend_info_t *info) -{ - if (!info) - return; - crypto_pk_free(info->onion_key); - tor_free(info); -} - -/** Allocate and return a new extend_info_t with the same contents as - * info. */ -extend_info_t * -extend_info_dup(extend_info_t *info) -{ - extend_info_t *newinfo; - tor_assert(info); - newinfo = tor_malloc(sizeof(extend_info_t)); - memcpy(newinfo, info, sizeof(extend_info_t)); - if (info->onion_key) - newinfo->onion_key = crypto_pk_dup_key(info->onion_key); - else - newinfo->onion_key = NULL; - return newinfo; -} - -/** Return the routerinfo_t for the chosen exit router in state. - * If there is no chosen exit, or if we don't know the routerinfo_t for - * the chosen exit, return NULL. - */ -const node_t * -build_state_get_exit_node(cpath_build_state_t *state) -{ - if (!state || !state->chosen_exit) - return NULL; - return node_get_by_id(state->chosen_exit->identity_digest); -} - -/** Return the nickname for the chosen exit router in state. If - * there is no chosen exit, or if we don't know the routerinfo_t for the - * chosen exit, return NULL. - */ -const char * -build_state_get_exit_nickname(cpath_build_state_t *state) -{ - if (!state || !state->chosen_exit) - return NULL; - return state->chosen_exit->nickname; -} - diff --git a/src/tor/circuitbuild.h b/src/tor/circuitbuild.h deleted file mode 100644 index ebcb22c..0000000 --- a/src/tor/circuitbuild.h +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitbuild.h - * \brief Header file for circuitbuild.c. - **/ - -#ifndef TOR_CIRCUITBUILD_H -#define TOR_CIRCUITBUILD_H - -char *circuit_list_path(origin_circuit_t *circ, int verbose); -char *circuit_list_path_for_controller(origin_circuit_t *circ); -void circuit_log_path(int severity, unsigned int domain, - origin_circuit_t *circ); -void circuit_rep_hist_note_result(origin_circuit_t *circ); -origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags); -origin_circuit_t *circuit_establish_circuit(uint8_t purpose, - extend_info_t *exit, - int flags); -int circuit_handle_first_hop(origin_circuit_t *circ); -void circuit_n_chan_done(channel_t *chan, int status); -int inform_testing_reachability(void); -int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); -int circuit_send_next_onion_skin(origin_circuit_t *circ); -void circuit_note_clock_jumped(int seconds_elapsed); -int circuit_extend(cell_t *cell, circuit_t *circ); -int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, - int reverse); -struct created_cell_t; -int circuit_finish_handshake(origin_circuit_t *circ, - const struct created_cell_t *created_cell); -int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, - int reason); -int onionskin_answer(or_circuit_t *circ, - const struct created_cell_t *created_cell, - const char *keys, - const uint8_t *rend_circ_nonce); -int circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity); - -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_new(const char *nickname, const char *digest, - crypto_pk_t *onion_key, - const curve25519_public_key_t *curve25519_key, - const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); -extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free(extend_info_t *info); -const node_t *build_state_get_exit_node(cpath_build_state_t *state); -const char *build_state_get_exit_nickname(cpath_build_state_t *state); - -const node_t *choose_good_entry_server(uint8_t purpose, - cpath_build_state_t *state); - -#endif - diff --git a/src/tor/circuitlist.c b/src/tor/circuitlist.c deleted file mode 100644 index 9474896..0000000 --- a/src/tor/circuitlist.c +++ /dev/null @@ -1,1878 +0,0 @@ -/* Copyright 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitlist.c - * \brief Manage the global circuit list. - **/ -#define CIRCUITLIST_PRIVATE -#include "or.h" -#include "channel.h" -#include "circpathbias.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "circuitstats.h" -#include "connection.h" -#include "config.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "onion.h" -#include "onion_fast.h" -#include "policies.h" -#include "relay.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rephist.h" -#include "routerlist.h" -#include "routerset.h" -#include "ht.h" - -/********* START VARIABLES **********/ - -/** A global list of all circuits at this hop. */ -struct global_circuitlist_s global_circuitlist = - TOR_LIST_HEAD_INITIALIZER(global_circuitlist); - -/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ -static smartlist_t *circuits_pending_chans = NULL; - -static void circuit_free_cpath_node(crypt_path_t *victim); -static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); - -/********* END VARIABLES ************/ - -/** A map from channel and circuit ID to circuit. (Lookup performance is - * very important here, since we need to do it every time a cell arrives.) */ -typedef struct chan_circid_circuit_map_t { - HT_ENTRY(chan_circid_circuit_map_t) node; - channel_t *chan; - circid_t circ_id; - circuit_t *circuit; -} chan_circid_circuit_map_t; - -/** Helper for hash tables: compare the channel and circuit ID for a and - * b, and return less than, equal to, or greater than zero appropriately. - */ -static INLINE int -chan_circid_entries_eq_(chan_circid_circuit_map_t *a, - chan_circid_circuit_map_t *b) -{ - return a->chan == b->chan && a->circ_id == b->circ_id; -} - -/** Helper: return a hash based on circuit ID and the pointer value of - * chan in a. */ -static INLINE unsigned int -chan_circid_entry_hash_(chan_circid_circuit_map_t *a) -{ - return ((unsigned)a->circ_id) ^ (unsigned)(uintptr_t)(a->chan); -} - -/** Map from [chan,circid] to circuit. */ -static HT_HEAD(chan_circid_map, chan_circid_circuit_map_t) - chan_circid_map = HT_INITIALIZER(); -HT_PROTOTYPE(chan_circid_map, chan_circid_circuit_map_t, node, - chan_circid_entry_hash_, chan_circid_entries_eq_) -HT_GENERATE(chan_circid_map, chan_circid_circuit_map_t, node, - chan_circid_entry_hash_, chan_circid_entries_eq_, 0.6, - malloc, realloc, free) - -/** The most recently returned entry from circuit_get_by_circid_chan; - * used to improve performance when many cells arrive in a row from the - * same circuit. - */ -chan_circid_circuit_map_t *_last_circid_chan_ent = NULL; - -/** Implementation helper for circuit_set_{p,n}_circid_channel: A circuit ID - * and/or channel for circ has just changed from old_chan, old_id - * to chan, id. Adjust the chan,circid map as appropriate, removing - * the old entry (if any) and adding a new one. */ -static void -circuit_set_circid_chan_helper(circuit_t *circ, int direction, - circid_t id, - channel_t *chan) -{ - chan_circid_circuit_map_t search; - chan_circid_circuit_map_t *found; - channel_t *old_chan, **chan_ptr; - circid_t old_id, *circid_ptr; - int make_active, attached = 0; - - if (direction == CELL_DIRECTION_OUT) { - chan_ptr = &circ->n_chan; - circid_ptr = &circ->n_circ_id; - make_active = circ->n_chan_cells.n > 0; - } else { - or_circuit_t *c = TO_OR_CIRCUIT(circ); - chan_ptr = &c->p_chan; - circid_ptr = &c->p_circ_id; - make_active = c->p_chan_cells.n > 0; - } - old_chan = *chan_ptr; - old_id = *circid_ptr; - - if (id == old_id && chan == old_chan) - return; - - if (_last_circid_chan_ent && - ((old_id == _last_circid_chan_ent->circ_id && - old_chan == _last_circid_chan_ent->chan) || - (id == _last_circid_chan_ent->circ_id && - chan == _last_circid_chan_ent->chan))) { - _last_circid_chan_ent = NULL; - } - - if (old_chan) { - /* - * If we're changing channels or ID and had an old channel and a non - * zero old ID and weren't marked for close (i.e., we should have been - * attached), detach the circuit. ID changes require this because - * circuitmux hashes on (channel_id, circuit_id). - */ - if (old_id != 0 && (old_chan != chan || old_id != id) && - !(circ->marked_for_close)) { - tor_assert(old_chan->cmux); - circuitmux_detach_circuit(old_chan->cmux, circ); - } - - /* we may need to remove it from the conn-circid map */ - search.circ_id = old_id; - search.chan = old_chan; - found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); - if (found) { - tor_free(found); - if (direction == CELL_DIRECTION_OUT) { - /* One fewer circuits use old_chan as n_chan */ - --(old_chan->num_n_circuits); - } else { - /* One fewer circuits use old_chan as p_chan */ - --(old_chan->num_p_circuits); - } - } - } - - /* Change the values only after we have possibly made the circuit inactive - * on the previous chan. */ - *chan_ptr = chan; - *circid_ptr = id; - - if (chan == NULL) - return; - - /* now add the new one to the conn-circid map */ - search.circ_id = id; - search.chan = chan; - found = HT_FIND(chan_circid_map, &chan_circid_map, &search); - if (found) { - found->circuit = circ; - } else { - found = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); - found->circ_id = id; - found->chan = chan; - found->circuit = circ; - HT_INSERT(chan_circid_map, &chan_circid_map, found); - } - - /* - * Attach to the circuitmux if we're changing channels or IDs and - * have a new channel and ID to use and the circuit is not marked for - * close. - */ - if (chan && id != 0 && (old_chan != chan || old_id != id) && - !(circ->marked_for_close)) { - tor_assert(chan->cmux); - circuitmux_attach_circuit(chan->cmux, circ, direction); - attached = 1; - } - - /* - * This is a no-op if we have no cells, but if we do it marks us active to - * the circuitmux - */ - if (make_active && attached) - update_circuit_on_cmux(circ, direction); - - /* Adjust circuit counts on new channel */ - if (direction == CELL_DIRECTION_OUT) { - ++chan->num_n_circuits; - } else { - ++chan->num_p_circuits; - } -} - -/** Mark that circuit id id shouldn't be used on channel chan, - * even if there is no circuit on the channel. We use this to keep the - * circuit id from getting re-used while we have queued but not yet sent - * a destroy cell. */ -void -channel_mark_circid_unusable(channel_t *chan, circid_t id) -{ - chan_circid_circuit_map_t search; - chan_circid_circuit_map_t *ent; - - /* See if there's an entry there. That wouldn't be good. */ - memset(&search, 0, sizeof(search)); - search.chan = chan; - search.circ_id = id; - ent = HT_FIND(chan_circid_map, &chan_circid_map, &search); - - if (ent && ent->circuit) { - /* we have a problem. */ - log_warn(LD_BUG, "Tried to mark %u unusable on %p, but there was already " - "a circuit there.", (unsigned)id, chan); - } else if (ent) { - /* It's already marked. */ - } else { - ent = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); - ent->chan = chan; - ent->circ_id = id; - /* leave circuit at NULL */ - HT_INSERT(chan_circid_map, &chan_circid_map, ent); - } -} - -/** Mark that a circuit id id can be used again on chan. - * We use this to re-enable the circuit ID after we've sent a destroy cell. - */ -void -channel_mark_circid_usable(channel_t *chan, circid_t id) -{ - chan_circid_circuit_map_t search; - chan_circid_circuit_map_t *ent; - - /* See if there's an entry there. That wouldn't be good. */ - memset(&search, 0, sizeof(search)); - search.chan = chan; - search.circ_id = id; - ent = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); - if (ent && ent->circuit) { - log_warn(LD_BUG, "Tried to mark %u usable on %p, but there was already " - "a circuit there.", (unsigned)id, chan); - return; - } - if (_last_circid_chan_ent == ent) - _last_circid_chan_ent = NULL; - tor_free(ent); -} - -/** Called to indicate that a DESTROY is pending on chan with - * circuit ID id, but hasn't been sent yet. */ -void -channel_note_destroy_pending(channel_t *chan, circid_t id) -{ - circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); - if (circ) { - if (circ->n_chan == chan && circ->n_circ_id == id) { - circ->n_delete_pending = 1; - } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { - circ->p_delete_pending = 1; - } - } - return; - } - channel_mark_circid_unusable(chan, id); -} - -/** Called to indicate that a DESTROY is no longer pending on chan with - * circuit ID id -- typically, because it has been sent. */ -void -channel_note_destroy_not_pending(channel_t *chan, circid_t id) -{ - circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); - if (circ) { - if (circ->n_chan == chan && circ->n_circ_id == id) { - circ->n_delete_pending = 0; - } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { - circ->p_delete_pending = 0; - } - } - /* XXXX this shouldn't happen; log a bug here. */ - return; - } - channel_mark_circid_usable(chan, id); -} - -/** Set the p_conn field of a circuit circ, along - * with the corresponding circuit ID, and add the circuit as appropriate - * to the (chan,id)-\>circuit map. */ -void -circuit_set_p_circid_chan(or_circuit_t *or_circ, circid_t id, - channel_t *chan) -{ - circuit_t *circ = TO_CIRCUIT(or_circ); - channel_t *old_chan = or_circ->p_chan; - circid_t old_id = or_circ->p_circ_id; - - circuit_set_circid_chan_helper(circ, CELL_DIRECTION_IN, id, chan); - - if (chan) - tor_assert(bool_eq(or_circ->p_chan_cells.n, - or_circ->next_active_on_p_chan)); - - if (circ->p_delete_pending && old_chan) { - channel_mark_circid_unusable(old_chan, old_id); - circ->p_delete_pending = 0; - } -} - -/** Set the n_conn field of a circuit circ, along - * with the corresponding circuit ID, and add the circuit as appropriate - * to the (chan,id)-\>circuit map. */ -void -circuit_set_n_circid_chan(circuit_t *circ, circid_t id, - channel_t *chan) -{ - channel_t *old_chan = circ->n_chan; - circid_t old_id = circ->n_circ_id; - - circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan); - - if (chan) - tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan)); - - if (circ->n_delete_pending && old_chan) { - channel_mark_circid_unusable(old_chan, old_id); - circ->n_delete_pending = 0; - } -} - -/** Change the state of circ to state, adding it to or removing - * it from lists as appropriate. */ -void -circuit_set_state(circuit_t *circ, uint8_t state) -{ - tor_assert(circ); - if (state == circ->state) - return; - if (!circuits_pending_chans) - circuits_pending_chans = smartlist_new(); - if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { - /* remove from waiting-circuit list. */ - smartlist_remove(circuits_pending_chans, circ); - } - if (state == CIRCUIT_STATE_CHAN_WAIT) { - /* add to waiting-circuit list. */ - smartlist_add(circuits_pending_chans, circ); - } - if (state == CIRCUIT_STATE_OPEN) - tor_assert(!circ->n_chan_create_cell); - circ->state = state; -} - -/** Append to out all circuits in state CHAN_WAIT waiting for - * the given connection. */ -void -circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan) -{ - tor_assert(out); - tor_assert(chan); - - if (!circuits_pending_chans) - return; - - SMARTLIST_FOREACH_BEGIN(circuits_pending_chans, circuit_t *, circ) { - if (circ->marked_for_close) - continue; - if (!circ->n_hop) - continue; - tor_assert(circ->state == CIRCUIT_STATE_CHAN_WAIT); - if (tor_digest_is_zero(circ->n_hop->identity_digest)) { - /* Look at addr/port. This is an unkeyed connection. */ - if (!channel_matches_extend_info(chan, circ->n_hop)) - continue; - } else { - /* We expected a key. See if it's the right one. */ - if (tor_memneq(chan->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) - continue; - } - smartlist_add(out, circ); - } SMARTLIST_FOREACH_END(circ); -} - -/** Return the number of circuits in state CHAN_WAIT, waiting for the given - * channel. */ -int -circuit_count_pending_on_channel(channel_t *chan) -{ - int cnt; - smartlist_t *sl = smartlist_new(); - - tor_assert(chan); - - circuit_get_all_pending_on_channel(sl, chan); - cnt = smartlist_len(sl); - smartlist_free(sl); - log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - chan->nickname ? chan->nickname : "NULL", - channel_get_canonical_remote_descr(chan), - cnt); - return cnt; -} - -/** Detach from the global circuit list, and deallocate, all - * circuits that have been marked for close. - */ -void -circuit_close_all_marked(void) -{ - circuit_t *circ, *tmp; - TOR_LIST_FOREACH_SAFE(circ, &global_circuitlist, head, tmp) - if (circ->marked_for_close) - circuit_free(circ); -} - -/** Return the head of the global linked list of circuits. */ -struct global_circuitlist_s * -circuit_get_global_list(void) -{ - return &global_circuitlist; -} - -/** Function to make circ-\>state human-readable */ -const char * -circuit_state_to_string(int state) -{ - static char buf[64]; - switch (state) { - case CIRCUIT_STATE_BUILDING: return "doing handshakes"; - case CIRCUIT_STATE_ONIONSKIN_PENDING: return "processing the onion"; - case CIRCUIT_STATE_CHAN_WAIT: return "connecting to server"; - case CIRCUIT_STATE_OPEN: return "open"; - default: - log_warn(LD_BUG, "Unknown circuit state %d", state); - tor_snprintf(buf, sizeof(buf), "unknown state [%d]", state); - return buf; - } -} - -/** Map a circuit purpose to a string suitable to be displayed to a - * controller. */ -const char * -circuit_purpose_to_controller_string(uint8_t purpose) -{ - static char buf[32]; - switch (purpose) { - case CIRCUIT_PURPOSE_OR: - case CIRCUIT_PURPOSE_INTRO_POINT: - case CIRCUIT_PURPOSE_REND_POINT_WAITING: - case CIRCUIT_PURPOSE_REND_ESTABLISHED: - return "SERVER"; /* A controller should never see these, actually. */ - - case CIRCUIT_PURPOSE_C_GENERAL: - return "GENERAL"; - case CIRCUIT_PURPOSE_C_INTRODUCING: - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: - return "HS_CLIENT_INTRO"; - - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - case CIRCUIT_PURPOSE_C_REND_READY: - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - case CIRCUIT_PURPOSE_C_REND_JOINED: - return "HS_CLIENT_REND"; - - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - case CIRCUIT_PURPOSE_S_INTRO: - return "HS_SERVICE_INTRO"; - - case CIRCUIT_PURPOSE_S_CONNECT_REND: - case CIRCUIT_PURPOSE_S_REND_JOINED: - return "HS_SERVICE_REND"; - - case CIRCUIT_PURPOSE_TESTING: - return "TESTING"; - case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: - return "MEASURE_TIMEOUT"; - case CIRCUIT_PURPOSE_CONTROLLER: - return "CONTROLLER"; - case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: - return "PATH_BIAS_TESTING"; - - default: - tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); - return buf; - } -} - -/** Return a string specifying the state of the hidden-service circuit - * purpose purpose, or NULL if purpose is not a - * hidden-service-related circuit purpose. */ -const char * -circuit_purpose_to_controller_hs_state_string(uint8_t purpose) -{ - switch (purpose) - { - default: - log_fn(LOG_WARN, LD_BUG, - "Unrecognized circuit purpose: %d", - (int)purpose); - tor_fragile_assert(); - /* fall through */ - - case CIRCUIT_PURPOSE_OR: - case CIRCUIT_PURPOSE_C_GENERAL: - case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: - case CIRCUIT_PURPOSE_TESTING: - case CIRCUIT_PURPOSE_CONTROLLER: - case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: - return NULL; - - case CIRCUIT_PURPOSE_INTRO_POINT: - return "OR_HSSI_ESTABLISHED"; - case CIRCUIT_PURPOSE_REND_POINT_WAITING: - return "OR_HSCR_ESTABLISHED"; - case CIRCUIT_PURPOSE_REND_ESTABLISHED: - return "OR_HS_R_JOINED"; - - case CIRCUIT_PURPOSE_C_INTRODUCING: - return "HSCI_CONNECTING"; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - return "HSCI_INTRO_SENT"; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: - return "HSCI_DONE"; - - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - return "HSCR_CONNECTING"; - case CIRCUIT_PURPOSE_C_REND_READY: - return "HSCR_ESTABLISHED_IDLE"; - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - return "HSCR_ESTABLISHED_WAITING"; - case CIRCUIT_PURPOSE_C_REND_JOINED: - return "HSCR_JOINED"; - - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - return "HSSI_CONNECTING"; - case CIRCUIT_PURPOSE_S_INTRO: - return "HSSI_ESTABLISHED"; - - case CIRCUIT_PURPOSE_S_CONNECT_REND: - return "HSSR_CONNECTING"; - case CIRCUIT_PURPOSE_S_REND_JOINED: - return "HSSR_JOINED"; - } -} - -/** Return a human-readable string for the circuit purpose purpose. */ -const char * -circuit_purpose_to_string(uint8_t purpose) -{ - static char buf[32]; - - switch (purpose) - { - case CIRCUIT_PURPOSE_OR: - return "Circuit at relay"; - case CIRCUIT_PURPOSE_INTRO_POINT: - return "Acting as intro point"; - case CIRCUIT_PURPOSE_REND_POINT_WAITING: - return "Acting as rendevous (pending)"; - case CIRCUIT_PURPOSE_REND_ESTABLISHED: - return "Acting as rendevous (established)"; - case CIRCUIT_PURPOSE_C_GENERAL: - return "General-purpose client"; - case CIRCUIT_PURPOSE_C_INTRODUCING: - return "Hidden service client: Connecting to intro point"; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - return "Hidden service client: Waiting for ack from intro point"; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: - return "Hidden service client: Received ack from intro point"; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - return "Hidden service client: Establishing rendezvous point"; - case CIRCUIT_PURPOSE_C_REND_READY: - return "Hidden service client: Pending rendezvous point"; - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - return "Hidden service client: Pending rendezvous point (ack received)"; - case CIRCUIT_PURPOSE_C_REND_JOINED: - return "Hidden service client: Active rendezvous point"; - case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: - return "Measuring circuit timeout"; - - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - return "Hidden service: Establishing introduction point"; - case CIRCUIT_PURPOSE_S_INTRO: - return "Hidden service: Introduction point"; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - return "Hidden service: Connecting to rendezvous point"; - case CIRCUIT_PURPOSE_S_REND_JOINED: - return "Hidden service: Active rendezvous point"; - - case CIRCUIT_PURPOSE_TESTING: - return "Testing circuit"; - - case CIRCUIT_PURPOSE_CONTROLLER: - return "Circuit made by controller"; - - case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: - return "Path-bias testing circuit"; - - default: - tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); - return buf; - } -} - -/** Pick a reasonable package_window to start out for our circuits. - * Originally this was hard-coded at 1000, but now the consensus votes - * on the answer. See proposal 168. */ -int32_t -circuit_initial_package_window(void) -{ - int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START, - CIRCWINDOW_START_MIN, - CIRCWINDOW_START_MAX); - /* If the consensus tells us a negative number, we'd assert. */ - if (num < 0) - num = CIRCWINDOW_START; - return num; -} - -/** Initialize the common elements in a circuit_t, and add it to the global - * list. */ -static void -init_circuit_base(circuit_t *circ) -{ - tor_gettimeofday(&circ->timestamp_created); - - // Gets reset when we send CREATE_FAST. - // circuit_expire_building() expects these to be equal - // until the orconn is built. - circ->timestamp_began = circ->timestamp_created; - - circ->package_window = circuit_initial_package_window(); - circ->deliver_window = CIRCWINDOW_START; - cell_queue_init(&circ->n_chan_cells); - - TOR_LIST_INSERT_HEAD(&global_circuitlist, circ, head); -} - -/** Allocate space for a new circuit, initializing with p_circ_id - * and p_conn. Add it to the global circuit list. - */ -origin_circuit_t * -origin_circuit_new(void) -{ - origin_circuit_t *circ; - /* never zero, since a global ID of 0 is treated specially by the - * controller */ - static uint32_t n_circuits_allocated = 1; - - circ = tor_malloc_zero(sizeof(origin_circuit_t)); - circ->base_.magic = ORIGIN_CIRCUIT_MAGIC; - - circ->next_stream_id = crypto_rand_int(1<<16); - circ->global_identifier = n_circuits_allocated++; - circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT; - circ->remaining_relay_early_cells -= crypto_rand_int(2); - - init_circuit_base(TO_CIRCUIT(circ)); - - circuit_build_times_update_last_circ(get_circuit_build_times_mutable()); - - return circ; -} - -/** Allocate a new or_circuit_t, connected to p_conn as - * p_circ_id. If p_conn is NULL, the circuit is unattached. */ -or_circuit_t * -or_circuit_new(circid_t p_circ_id, channel_t *p_chan) -{ - /* CircIDs */ - or_circuit_t *circ; - - circ = tor_malloc_zero(sizeof(or_circuit_t)); - circ->base_.magic = OR_CIRCUIT_MAGIC; - - if (p_chan) - circuit_set_p_circid_chan(circ, p_circ_id, p_chan); - - circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT; - cell_queue_init(&circ->p_chan_cells); - - init_circuit_base(TO_CIRCUIT(circ)); - - return circ; -} - -/** Deallocate space associated with circ. - */ -STATIC void -circuit_free(circuit_t *circ) -{ - void *mem; - size_t memlen; - if (!circ) - return; - - if (CIRCUIT_IS_ORIGIN(circ)) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - mem = ocirc; - memlen = sizeof(origin_circuit_t); - tor_assert(circ->magic == ORIGIN_CIRCUIT_MAGIC); - if (ocirc->build_state) { - extend_info_free(ocirc->build_state->chosen_exit); - circuit_free_cpath_node(ocirc->build_state->pending_final_cpath); - cpath_ref_decref(ocirc->build_state->service_pending_final_cpath_ref); - } - tor_free(ocirc->build_state); - - circuit_clear_cpath(ocirc); - - crypto_pk_free(ocirc->intro_key); - rend_data_free(ocirc->rend_data); - - tor_free(ocirc->dest_address); - if (ocirc->socks_username) { - memwipe(ocirc->socks_username, 0x12, ocirc->socks_username_len); - tor_free(ocirc->socks_username); - } - if (ocirc->socks_password) { - memwipe(ocirc->socks_password, 0x06, ocirc->socks_password_len); - tor_free(ocirc->socks_password); - } - addr_policy_list_free(ocirc->prepend_policy); - } else { - or_circuit_t *ocirc = TO_OR_CIRCUIT(circ); - /* Remember cell statistics for this circuit before deallocating. */ - if (get_options()->CellStatistics) - rep_hist_buffer_stats_add_circ(circ, time(NULL)); - mem = ocirc; - memlen = sizeof(or_circuit_t); - tor_assert(circ->magic == OR_CIRCUIT_MAGIC); - - crypto_cipher_free(ocirc->p_crypto); - crypto_digest_free(ocirc->p_digest); - crypto_cipher_free(ocirc->n_crypto); - crypto_digest_free(ocirc->n_digest); - - if (ocirc->rend_splice) { - or_circuit_t *other = ocirc->rend_splice; - tor_assert(other->base_.magic == OR_CIRCUIT_MAGIC); - other->rend_splice = NULL; - } - - /* remove from map. */ - circuit_set_p_circid_chan(ocirc, 0, NULL); - - /* Clear cell queue _after_ removing it from the map. Otherwise our - * "active" checks will be violated. */ - cell_queue_clear(ô->p_chan_cells); - } - - extend_info_free(circ->n_hop); - tor_free(circ->n_chan_create_cell); - - TOR_LIST_REMOVE(circ, head); - - /* Remove from map. */ - circuit_set_n_circid_chan(circ, 0, NULL); - - /* Clear cell queue _after_ removing it from the map. Otherwise our - * "active" checks will be violated. */ - cell_queue_clear(&circ->n_chan_cells); - - memwipe(mem, 0xAA, memlen); /* poison memory */ - tor_free(mem); -} - -/** Deallocate the linked list circ->cpath, and remove the cpath from - * circ. */ -void -circuit_clear_cpath(origin_circuit_t *circ) -{ - crypt_path_t *victim, *head, *cpath; - - head = cpath = circ->cpath; - - if (!cpath) - return; - - /* it's a circular list, so we have to notice when we've - * gone through it once. */ - while (cpath->next && cpath->next != head) { - victim = cpath; - cpath = victim->next; - circuit_free_cpath_node(victim); - } - - circuit_free_cpath_node(cpath); - - circ->cpath = NULL; -} - -/** Release all storage held by circuits. */ -void -circuit_free_all(void) -{ - circuit_t *tmp, *tmp2; - - TOR_LIST_FOREACH_SAFE(tmp, &global_circuitlist, head, tmp2) { - if (! CIRCUIT_IS_ORIGIN(tmp)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(tmp); - while (or_circ->resolving_streams) { - edge_connection_t *next_conn; - next_conn = or_circ->resolving_streams->next_stream; - connection_free(TO_CONN(or_circ->resolving_streams)); - or_circ->resolving_streams = next_conn; - } - } - circuit_free(tmp); - } - - smartlist_free(circuits_pending_chans); - circuits_pending_chans = NULL; - - HT_CLEAR(chan_circid_map, &chan_circid_map); -} - -/** Deallocate space associated with the cpath node victim. */ -static void -circuit_free_cpath_node(crypt_path_t *victim) -{ - if (!victim) - return; - - crypto_cipher_free(victim->f_crypto); - crypto_cipher_free(victim->b_crypto); - crypto_digest_free(victim->f_digest); - crypto_digest_free(victim->b_digest); - onion_handshake_state_release(&victim->handshake_state); - crypto_dh_free(victim->rend_dh_handshake_state); - extend_info_free(victim->extend_info); - - memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */ - tor_free(victim); -} - -/** Release a crypt_path_reference_t*, which may be NULL. */ -static void -cpath_ref_decref(crypt_path_reference_t *cpath_ref) -{ - if (cpath_ref != NULL) { - if (--(cpath_ref->refcount) == 0) { - circuit_free_cpath_node(cpath_ref->cpath); - tor_free(cpath_ref); - } - } -} - -/** A helper function for circuit_dump_by_conn() below. Log a bunch - * of information about circuit circ. - */ -static void -circuit_dump_conn_details(int severity, - circuit_t *circ, - int conn_array_index, - const char *type, - circid_t this_circid, - circid_t other_circid) -{ - tor_log(severity, LD_CIRC, "Conn %d has %s circuit: circID %u " - "(other side %u), state %d (%s), born %ld:", - conn_array_index, type, (unsigned)this_circid, (unsigned)other_circid, - circ->state, circuit_state_to_string(circ->state), - (long)circ->timestamp_began.tv_sec); - if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ - circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ)); - } -} - -/** Log, at severity severity, information about each circuit - * that is connected to conn. - */ -void -circuit_dump_by_conn(connection_t *conn, int severity) -{ - circuit_t *circ; - edge_connection_t *tmpconn; - - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; - - if (circ->marked_for_close) { - continue; - } - - if (!CIRCUIT_IS_ORIGIN(circ)) { - p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - } - - if (CIRCUIT_IS_ORIGIN(circ)) { - for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn; - tmpconn=tmpconn->next_stream) { - if (TO_CONN(tmpconn) == conn) { - circuit_dump_conn_details(severity, circ, conn->conn_array_index, - "App-ward", p_circ_id, n_circ_id); - } - } - } - - if (! CIRCUIT_IS_ORIGIN(circ)) { - for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn; - tmpconn=tmpconn->next_stream) { - if (TO_CONN(tmpconn) == conn) { - circuit_dump_conn_details(severity, circ, conn->conn_array_index, - "Exit-ward", n_circ_id, p_circ_id); - } - } - } - } -} - -/** A helper function for circuit_dump_by_chan() below. Log a bunch - * of information about circuit circ. - */ -static void -circuit_dump_chan_details(int severity, - circuit_t *circ, - channel_t *chan, - const char *type, - circid_t this_circid, - circid_t other_circid) -{ - tor_log(severity, LD_CIRC, "Conn %p has %s circuit: circID %u " - "(other side %u), state %d (%s), born %ld:", - chan, type, (unsigned)this_circid, (unsigned)other_circid, circ->state, - circuit_state_to_string(circ->state), - (long)circ->timestamp_began.tv_sec); - if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ - circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ)); - } -} - -/** Log, at severity severity, information about each circuit - * that is connected to chan. - */ -void -circuit_dump_by_chan(channel_t *chan, int severity) -{ - circuit_t *circ; - - tor_assert(chan); - - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; - - if (circ->marked_for_close) { - continue; - } - - if (!CIRCUIT_IS_ORIGIN(circ)) { - p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - } - - if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_chan && - TO_OR_CIRCUIT(circ)->p_chan == chan) { - circuit_dump_chan_details(severity, circ, chan, "App-ward", - p_circ_id, n_circ_id); - } - - if (circ->n_chan && circ->n_chan == chan) { - circuit_dump_chan_details(severity, circ, chan, "Exit-ward", - n_circ_id, p_circ_id); - } - - if (!circ->n_chan && circ->n_hop && - channel_matches_extend_info(chan, circ->n_hop) && - tor_memeq(chan->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) { - circuit_dump_chan_details(severity, circ, chan, - (circ->state == CIRCUIT_STATE_OPEN && - !CIRCUIT_IS_ORIGIN(circ)) ? - "Endpoint" : "Pending", - n_circ_id, p_circ_id); - } - } -} - -/** Return the circuit whose global ID is id, or NULL if no - * such circuit exists. */ -origin_circuit_t * -circuit_get_by_global_id(uint32_t id) -{ - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (CIRCUIT_IS_ORIGIN(circ) && - TO_ORIGIN_CIRCUIT(circ)->global_identifier == id) { - if (circ->marked_for_close) - return NULL; - else - return TO_ORIGIN_CIRCUIT(circ); - } - } - return NULL; -} - -/** Return a circ such that: - * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and - * - circ is attached to chan, either as p_chan or n_chan. - * Return NULL if no such circuit exists. - * - * If found_entry_out is provided, set it to true if we have a - * placeholder entry for circid/chan, and leave it unset otherwise. - */ -static INLINE circuit_t * -circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan, - int *found_entry_out) -{ - chan_circid_circuit_map_t search; - chan_circid_circuit_map_t *found; - - if (_last_circid_chan_ent && - circ_id == _last_circid_chan_ent->circ_id && - chan == _last_circid_chan_ent->chan) { - found = _last_circid_chan_ent; - } else { - search.circ_id = circ_id; - search.chan = chan; - found = HT_FIND(chan_circid_map, &chan_circid_map, &search); - _last_circid_chan_ent = found; - } - if (found && found->circuit) { - log_debug(LD_CIRC, - "circuit_get_by_circid_channel_impl() returning circuit %p for" - " circ_id %u, channel ID " U64_FORMAT " (%p)", - found->circuit, (unsigned)circ_id, - U64_PRINTF_ARG(chan->global_identifier), chan); - if (found_entry_out) - *found_entry_out = 1; - return found->circuit; - } - - log_debug(LD_CIRC, - "circuit_get_by_circid_channel_impl() found %s for" - " circ_id %u, channel ID " U64_FORMAT " (%p)", - found ? "placeholder" : "nothing", - (unsigned)circ_id, - U64_PRINTF_ARG(chan->global_identifier), chan); - - if (found_entry_out) - *found_entry_out = found ? 1 : 0; - - return NULL; - /* The rest of this checks for bugs. Disabled by default. */ - /* We comment it out because coverity complains otherwise. - { - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (! CIRCUIT_IS_ORIGIN(circ)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_chan == chan && or_circ->p_circ_id == circ_id) { - log_warn(LD_BUG, - "circuit matches p_chan, but not in hash table (Bug!)"); - return circ; - } - } - if (circ->n_chan == chan && circ->n_circ_id == circ_id) { - log_warn(LD_BUG, - "circuit matches n_chan, but not in hash table (Bug!)"); - return circ; - } - } - return NULL; - } */ -} - -/** Return a circ such that: - * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and - * - circ is attached to chan, either as p_chan or n_chan. - * - circ is not marked for close. - * Return NULL if no such circuit exists. - */ -circuit_t * -circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan) -{ - circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan, NULL); - if (!circ || circ->marked_for_close) - return NULL; - else - return circ; -} - -/** Return a circ such that: - * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and - * - circ is attached to chan, either as p_chan or n_chan. - * Return NULL if no such circuit exists. - */ -circuit_t * -circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, - channel_t *chan) -{ - return circuit_get_by_circid_channel_impl(circ_id, chan, NULL); -} - -/** Return true iff the circuit ID circ_id is currently used by a - * circuit, marked or not, on chan. */ -int -circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan) -{ - int found = 0; - return circuit_get_by_circid_channel_impl(circ_id, chan, &found) != NULL - || found; -} - -/** Return the circuit that a given edge connection is using. */ -circuit_t * -circuit_get_by_edge_conn(edge_connection_t *conn) -{ - circuit_t *circ; - - circ = conn->on_circuit; - tor_assert(!circ || - (CIRCUIT_IS_ORIGIN(circ) ? circ->magic == ORIGIN_CIRCUIT_MAGIC - : circ->magic == OR_CIRCUIT_MAGIC)); - - return circ; -} - -/** For each circuit that has chan as n_chan or p_chan, unlink the - * circuit from the chan,circid map, and mark it for close if it hasn't - * been marked already. - */ -void -circuit_unlink_all_from_channel(channel_t *chan, int reason) -{ - circuit_t *circ; - - channel_unlink_all_circuits(chan); - - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - int mark = 0; - if (circ->n_chan == chan) { - circuit_set_n_circid_chan(circ, 0, NULL); - mark = 1; - - /* If we didn't request this closure, pass the remote - * bit to mark_for_close. */ - if (chan->reason_for_closing != CHANNEL_CLOSE_REQUESTED) - reason |= END_CIRC_REASON_FLAG_REMOTE; - } - if (! CIRCUIT_IS_ORIGIN(circ)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_chan == chan) { - circuit_set_p_circid_chan(or_circ, 0, NULL); - mark = 1; - } - } - if (mark && !circ->marked_for_close) - circuit_mark_for_close(circ, reason); - } -} - -/** Return a circ such that - * - circ-\>rend_data-\>onion_address is equal to - * rend_data-\>onion_address, - * - circ-\>rend_data-\>rend_cookie is equal to - * rend_data-\>rend_cookie, and - * - circ-\>purpose is equal to CIRCUIT_PURPOSE_C_REND_READY. - * - * Return NULL if no such circuit exists. - */ -origin_circuit_t * -circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data) -{ - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (!circ->marked_for_close && - circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - if (ocirc->rend_data && - !rend_cmp_service_ids(rend_data->onion_address, - ocirc->rend_data->onion_address) && - tor_memeq(ocirc->rend_data->rend_cookie, - rend_data->rend_cookie, - REND_COOKIE_LEN)) - return ocirc; - } - } - return NULL; -} - -/** Return the first circuit originating here in global_circuitlist after - * start whose purpose is purpose, and where - * digest (if set) matches the rend_pk_digest field. Return NULL if no - * circuit is found. If start is NULL, begin at the start of the list. - */ -origin_circuit_t * -circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, - const char *digest, uint8_t purpose) -{ - circuit_t *circ; - tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose)); - if (start == NULL) - circ = TOR_LIST_FIRST(&global_circuitlist); - else - circ = TOR_LIST_NEXT(TO_CIRCUIT(start), head); - - for ( ; circ; circ = TOR_LIST_NEXT(circ, head)) { - if (circ->marked_for_close) - continue; - if (circ->purpose != purpose) - continue; - if (!digest) - return TO_ORIGIN_CIRCUIT(circ); - else if (TO_ORIGIN_CIRCUIT(circ)->rend_data && - tor_memeq(TO_ORIGIN_CIRCUIT(circ)->rend_data->rend_pk_digest, - digest, DIGEST_LEN)) - return TO_ORIGIN_CIRCUIT(circ); - } - return NULL; -} - -/** Return the first OR circuit in the global list whose purpose is - * purpose, and whose rend_token is the len-byte - * token. */ -static or_circuit_t * -circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token, - size_t len) -{ - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (! circ->marked_for_close && - circ->purpose == purpose && - tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len)) - return TO_OR_CIRCUIT(circ); - } - return NULL; -} - -/** Return the circuit waiting for a rendezvous with the provided cookie. - * Return NULL if no such circuit is found. - */ -or_circuit_t * -circuit_get_rendezvous(const char *cookie) -{ - return circuit_get_by_rend_token_and_purpose( - CIRCUIT_PURPOSE_REND_POINT_WAITING, - cookie, REND_COOKIE_LEN); -} - -/** Return the circuit waiting for intro cells of the given digest. - * Return NULL if no such circuit is found. - */ -or_circuit_t * -circuit_get_intro_point(const char *digest) -{ - return circuit_get_by_rend_token_and_purpose( - CIRCUIT_PURPOSE_INTRO_POINT, digest, - DIGEST_LEN); -} - -/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL, - * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_* - * flags in flags, and if info is defined, does not already use info - * as any of its hops; or NULL if no circuit fits this description. - * - * The purpose argument (currently ignored) refers to the purpose of - * the circuit we want to create, not the purpose of the circuit we want to - * cannibalize. - * - * If !CIRCLAUNCH_NEED_UPTIME, prefer returning non-uptime circuits. - */ -origin_circuit_t * -circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, - int flags) -{ - circuit_t *circ_; - origin_circuit_t *best=NULL; - int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0; - int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0; - int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0; - const or_options_t *options = get_options(); - - /* Make sure we're not trying to create a onehop circ by - * cannibalization. */ - tor_assert(!(flags & CIRCLAUNCH_ONEHOP_TUNNEL)); - - log_debug(LD_CIRC, - "Hunting for a circ to cannibalize: purpose %d, uptime %d, " - "capacity %d, internal %d", - purpose, need_uptime, need_capacity, internal); - - TOR_LIST_FOREACH(circ_, &global_circuitlist, head) { - if (CIRCUIT_IS_ORIGIN(circ_) && - circ_->state == CIRCUIT_STATE_OPEN && - !circ_->marked_for_close && - circ_->purpose == CIRCUIT_PURPOSE_C_GENERAL && - !circ_->timestamp_dirty) { - origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(circ_); - if ((!need_uptime || circ->build_state->need_uptime) && - (!need_capacity || circ->build_state->need_capacity) && - (internal == circ->build_state->is_internal) && - !circ->unusable_for_new_conns && - circ->remaining_relay_early_cells && - circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN && - !circ->build_state->onehop_tunnel && - !circ->isolation_values_set) { - if (info) { - /* need to make sure we don't duplicate hops */ - crypt_path_t *hop = circ->cpath; - const node_t *ri1 = node_get_by_id(info->identity_digest); - do { - const node_t *ri2; - if (tor_memeq(hop->extend_info->identity_digest, - info->identity_digest, DIGEST_LEN)) - goto next; - if (ri1 && - (ri2 = node_get_by_id(hop->extend_info->identity_digest)) - && nodes_in_same_family(ri1, ri2)) - goto next; - hop=hop->next; - } while (hop!=circ->cpath); - } - if (options->ExcludeNodes) { - /* Make sure no existing nodes in the circuit are excluded for - * general use. (This may be possible if StrictNodes is 0, and we - * thought we needed to use an otherwise excluded node for, say, a - * directory operation.) */ - crypt_path_t *hop = circ->cpath; - do { - if (routerset_contains_extendinfo(options->ExcludeNodes, - hop->extend_info)) - goto next; - hop = hop->next; - } while (hop != circ->cpath); - } - if (!best || (best->build_state->need_uptime && !need_uptime)) - best = circ; - next: ; - } - } - } - return best; -} - -/** Return the number of hops in circuit's path. */ -int -circuit_get_cpath_len(origin_circuit_t *circ) -{ - int n = 0; - if (circ && circ->cpath) { - crypt_path_t *cpath, *cpath_next = NULL; - for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) { - cpath_next = cpath->next; - ++n; - } - } - return n; -} - -/** Return the hopnumth hop in circ->cpath, or NULL if there - * aren't that many hops in the list. */ -crypt_path_t * -circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum) -{ - if (circ && circ->cpath && hopnum > 0) { - crypt_path_t *cpath, *cpath_next = NULL; - for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) { - cpath_next = cpath->next; - if (--hopnum <= 0) - return cpath; - } - } - return NULL; -} - -/** Go through the circuitlist; mark-for-close each circuit that starts - * at us but has not yet been used. */ -void -circuit_mark_all_unused_circs(void) -{ - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (CIRCUIT_IS_ORIGIN(circ) && - !circ->marked_for_close && - !circ->timestamp_dirty) - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - } -} - -/** Go through the circuitlist; for each circuit that starts at us - * and is dirty, frob its timestamp_dirty so we won't use it for any - * new streams. - * - * This is useful for letting the user change pseudonyms, so new - * streams will not be linkable to old streams. - */ -void -circuit_mark_all_dirty_circs_as_unusable(void) -{ - circuit_t *circ; - TOR_LIST_FOREACH(circ, &global_circuitlist, head) { - if (CIRCUIT_IS_ORIGIN(circ) && - !circ->marked_for_close && - circ->timestamp_dirty) { - mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); - } - } -} - -/** Mark circ to be closed next time we call - * circuit_close_all_marked(). Do any cleanup needed: - * - If state is onionskin_pending, remove circ from the onion_pending - * list. - * - If circ isn't open yet: call circuit_build_failed() if we're - * the origin, and in either case call circuit_rep_hist_note_result() - * to note stats. - * - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point - * failure we just had to the hidden service client module. - * - If purpose is C_INTRODUCING and reason isn't TIMEOUT, - * report to the hidden service client module that the intro point - * we just tried may be unreachable. - * - Send appropriate destroys and edge_destroys for conns and - * streams attached to circ. - * - If circ->rend_splice is set (we are the midpoint of a joined - * rendezvous stream), then mark the other circuit to close as well. - */ -void -circuit_mark_for_close_(circuit_t *circ, int reason, int line, - const char *file) -{ - int orig_reason = reason; /* Passed to the controller */ - assert_circuit_ok(circ); - tor_assert(line); - tor_assert(file); - - if (circ->marked_for_close) { - log_warn(LD_BUG, - "Duplicate call to circuit_mark_for_close at %s:%d" - " (first at %s:%d)", file, line, - circ->marked_for_close_file, circ->marked_for_close); - return; - } - if (reason == END_CIRC_AT_ORIGIN) { - if (!CIRCUIT_IS_ORIGIN(circ)) { - log_warn(LD_BUG, "Specified 'at-origin' non-reason for ending circuit, " - "but circuit was not at origin. (called %s:%d, purpose=%d)", - file, line, circ->purpose); - } - reason = END_CIRC_REASON_NONE; - } - - if (CIRCUIT_IS_ORIGIN(circ)) { - if (pathbias_check_close(TO_ORIGIN_CIRCUIT(circ), reason) == -1) { - /* Don't close it yet, we need to test it first */ - return; - } - - /* We don't send reasons when closing circuits at the origin. */ - reason = END_CIRC_REASON_NONE; - } - - if (reason & END_CIRC_REASON_FLAG_REMOTE) - reason &= ~END_CIRC_REASON_FLAG_REMOTE; - - if (reason < END_CIRC_REASON_MIN_ || reason > END_CIRC_REASON_MAX_) { - if (!(orig_reason & END_CIRC_REASON_FLAG_REMOTE)) - log_warn(LD_BUG, "Reason %d out of range at %s:%d", reason, file, line); - reason = END_CIRC_REASON_NONE; - } - - if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { - onion_pending_remove(TO_OR_CIRCUIT(circ)); - } - /* If the circuit ever became OPEN, we sent it to the reputation history - * module then. If it isn't OPEN, we send it there now to remember which - * links worked and which didn't. - */ - if (circ->state != CIRCUIT_STATE_OPEN) { - if (CIRCUIT_IS_ORIGIN(circ)) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - circuit_build_failed(ocirc); /* take actions if necessary */ - circuit_rep_hist_note_result(ocirc); - } - } - if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { - if (circuits_pending_chans) - smartlist_remove(circuits_pending_chans, circ); - } - if (CIRCUIT_IS_ORIGIN(circ)) { - control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), - (circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, - orig_reason); - } - if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - int timed_out = (reason == END_CIRC_REASON_TIMEOUT); - tor_assert(circ->state == CIRCUIT_STATE_OPEN); - tor_assert(ocirc->build_state->chosen_exit); - tor_assert(ocirc->rend_data); - /* treat this like getting a nack from it */ - log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s", - safe_str_client(ocirc->rend_data->onion_address), - safe_str_client(build_state_get_exit_nickname(ocirc->build_state)), - timed_out ? "Recording timeout." : "Removing from descriptor."); - rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, - ocirc->rend_data, - timed_out ? - INTRO_POINT_FAILURE_TIMEOUT : - INTRO_POINT_FAILURE_GENERIC); - } else if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING && - reason != END_CIRC_REASON_TIMEOUT) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - if (ocirc->build_state->chosen_exit && ocirc->rend_data) { - log_info(LD_REND, "Failed intro circ %s to %s " - "(building circuit to intro point). " - "Marking intro point as possibly unreachable.", - safe_str_client(ocirc->rend_data->onion_address), - safe_str_client(build_state_get_exit_nickname(ocirc->build_state))); - rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit, - ocirc->rend_data, - INTRO_POINT_FAILURE_UNREACHABLE); - } - } - if (circ->n_chan) { - circuit_clear_cell_queue(circ, circ->n_chan); - /* Only send destroy if the channel isn't closing anyway */ - if (!(circ->n_chan->state == CHANNEL_STATE_CLOSING || - circ->n_chan->state == CHANNEL_STATE_CLOSED || - circ->n_chan->state == CHANNEL_STATE_ERROR)) { - channel_send_destroy(circ->n_circ_id, circ->n_chan, reason); - } - circuitmux_detach_circuit(circ->n_chan->cmux, circ); - } - - if (! CIRCUIT_IS_ORIGIN(circ)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - edge_connection_t *conn; - for (conn=or_circ->n_streams; conn; conn=conn->next_stream) - connection_edge_destroy(or_circ->p_circ_id, conn); - or_circ->n_streams = NULL; - - while (or_circ->resolving_streams) { - conn = or_circ->resolving_streams; - or_circ->resolving_streams = conn->next_stream; - if (!conn->base_.marked_for_close) { - /* The client will see a DESTROY, and infer that the connections - * are closing because the circuit is getting torn down. No need - * to send an end cell. */ - conn->edge_has_sent_end = 1; - conn->end_reason = END_STREAM_REASON_DESTROY; - conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED; - connection_mark_for_close(TO_CONN(conn)); - } - conn->on_circuit = NULL; - } - - if (or_circ->p_chan) { - circuit_clear_cell_queue(circ, or_circ->p_chan); - /* Only send destroy if the channel isn't closing anyway */ - if (!(or_circ->p_chan->state == CHANNEL_STATE_CLOSING || - or_circ->p_chan->state == CHANNEL_STATE_CLOSED || - or_circ->p_chan->state == CHANNEL_STATE_ERROR)) { - channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason); - } - circuitmux_detach_circuit(or_circ->p_chan->cmux, circ); - } - } else { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - edge_connection_t *conn; - for (conn=ocirc->p_streams; conn; conn=conn->next_stream) - connection_edge_destroy(circ->n_circ_id, conn); - ocirc->p_streams = NULL; - } - - circ->marked_for_close = line; - circ->marked_for_close_file = file; - - if (!CIRCUIT_IS_ORIGIN(circ)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->rend_splice) { - if (!or_circ->rend_splice->base_.marked_for_close) { - /* do this after marking this circuit, to avoid infinite recursion. */ - circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); - } - or_circ->rend_splice = NULL; - } - } -} - -/** Given a marked circuit circ, aggressively free its cell queues to - * recover memory. */ -static void -marked_circuit_free_cells(circuit_t *circ) -{ - if (!circ->marked_for_close) { - log_warn(LD_BUG, "Called on non-marked circuit"); - return; - } - cell_queue_clear(&circ->n_chan_cells); - if (! CIRCUIT_IS_ORIGIN(circ)) - cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_chan_cells); -} - -/** Return the number of cells used by the circuit c's cell queues. */ -STATIC size_t -n_cells_in_circ_queues(const circuit_t *c) -{ - size_t n = c->n_chan_cells.n; - if (! CIRCUIT_IS_ORIGIN(c)) { - circuit_t *cc = (circuit_t *) c; - n += TO_OR_CIRCUIT(cc)->p_chan_cells.n; - } - return n; -} - -/** - * Return the age of the oldest cell queued on c, in milliseconds. - * Return 0 if there are no cells queued on c. Requires that now be - * the current time in milliseconds since the epoch, truncated. - * - * This function will return incorrect results if the oldest cell queued on - * the circuit is older than 2**32 msec (about 49 days) old. - */ -static uint32_t -circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) -{ - uint32_t age = 0; - packed_cell_t *cell; - - if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head))) - age = now - cell->inserted_time; - - if (! CIRCUIT_IS_ORIGIN(c)) { - const or_circuit_t *orcirc = TO_OR_CIRCUIT((circuit_t*)c); - if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) { - uint32_t age2 = now - cell->inserted_time; - if (age2 > age) - return age2; - } - } - return age; -} - -/** Temporary variable for circuits_compare_by_oldest_queued_cell_ This is a - * kludge to work around the fact that qsort doesn't provide a way for - * comparison functions to take an extra argument. */ -static uint32_t circcomp_now_tmp; - -/** Helper to sort a list of circuit_t by age of oldest cell, in descending - * order. Requires that circcomp_now_tmp is set correctly. */ -static int -circuits_compare_by_oldest_queued_cell_(const void **a_, const void **b_) -{ - const circuit_t *a = *a_; - const circuit_t *b = *b_; - uint32_t age_a = circuit_max_queued_cell_age(a, circcomp_now_tmp); - uint32_t age_b = circuit_max_queued_cell_age(b, circcomp_now_tmp); - - if (age_a < age_b) - return 1; - else if (age_a == age_b) - return 0; - else - return -1; -} - -#define FRACTION_OF_CELLS_TO_RETAIN_ON_OOM 0.90 - -/** We're out of memory for cells, having allocated current_allocation - * bytes' worth. Kill the 'worst' circuits until we're under - * FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM of our maximum usage. */ -void -circuits_handle_oom(size_t current_allocation) -{ - /* Let's hope there's enough slack space for this allocation here... */ - smartlist_t *circlist = smartlist_new(); - circuit_t *circ; - size_t n_cells_removed=0, n_cells_to_remove; - int n_circuits_killed=0; - struct timeval now; - log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " - "over-long queues. (This behavior is controlled by " - "MaxMemInCellQueues.)"); - - { - size_t mem_target = (size_t)(get_options()->MaxMemInCellQueues * - FRACTION_OF_CELLS_TO_RETAIN_ON_OOM); - size_t mem_to_recover; - if (current_allocation <= mem_target) - return; - mem_to_recover = current_allocation - mem_target; - n_cells_to_remove = CEIL_DIV(mem_to_recover, packed_cell_mem_cost()); - } - - /* This algorithm itself assumes that you've got enough memory slack - * to actually run it. */ - TOR_LIST_FOREACH(circ, &global_circuitlist, head) - smartlist_add(circlist, circ); - - /* Set circcomp_now_tmp so that the sort can work. */ - tor_gettimeofday_cached(&now); - circcomp_now_tmp = (uint32_t)tv_to_msec(&now); - - /* This is O(n log n); there are faster algorithms we could use instead. - * Let's hope this doesn't happen enough to be in the critical path. */ - smartlist_sort(circlist, circuits_compare_by_oldest_queued_cell_); - - /* Okay, now the worst circuits are at the front of the list. Let's mark - * them, and reclaim their storage aggressively. */ - SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { - size_t n = n_cells_in_circ_queues(circ); - if (! circ->marked_for_close) { - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - } - marked_circuit_free_cells(circ); - - ++n_circuits_killed; - n_cells_removed += n; - if (n_cells_removed >= n_cells_to_remove) - break; - } SMARTLIST_FOREACH_END(circ); - - clean_cell_pool(); /* In case this helps. */ - - log_notice(LD_GENERAL, "Removed "U64_FORMAT" bytes by killing %d circuits.", - U64_PRINTF_ARG(n_cells_removed * packed_cell_mem_cost()), - n_circuits_killed); - - smartlist_free(circlist); -} - -/** Verify that cpath layer cp has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void -assert_cpath_layer_ok(const crypt_path_t *cp) -{ -// tor_assert(cp->addr); /* these are zero for rendezvous extra-hops */ -// tor_assert(cp->port); - tor_assert(cp); - tor_assert(cp->magic == CRYPT_PATH_MAGIC); - switch (cp->state) - { - case CPATH_STATE_OPEN: - tor_assert(cp->f_crypto); - tor_assert(cp->b_crypto); - /* fall through */ - case CPATH_STATE_CLOSED: - /*XXXX Assert that there's no handshake_state either. */ - tor_assert(!cp->rend_dh_handshake_state); - break; - case CPATH_STATE_AWAITING_KEYS: - /* tor_assert(cp->dh_handshake_state); */ - break; - default: - log_fn(LOG_ERR, LD_BUG, "Unexpected state %d", cp->state); - tor_assert(0); - } - tor_assert(cp->package_window >= 0); - tor_assert(cp->deliver_window >= 0); -} - -/** Verify that cpath cp has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -static void -assert_cpath_ok(const crypt_path_t *cp) -{ - const crypt_path_t *start = cp; - - do { - assert_cpath_layer_ok(cp); - /* layers must be in sequence of: "open* awaiting? closed*" */ - if (cp != start) { - if (cp->state == CPATH_STATE_AWAITING_KEYS) { - tor_assert(cp->prev->state == CPATH_STATE_OPEN); - } else if (cp->state == CPATH_STATE_OPEN) { - tor_assert(cp->prev->state == CPATH_STATE_OPEN); - } - } - cp = cp->next; - tor_assert(cp); - } while (cp != start); -} - -/** Verify that circuit c has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void -assert_circuit_ok(const circuit_t *c) -{ - edge_connection_t *conn; - const or_circuit_t *or_circ = NULL; - const origin_circuit_t *origin_circ = NULL; - - tor_assert(c); - tor_assert(c->magic == ORIGIN_CIRCUIT_MAGIC || c->magic == OR_CIRCUIT_MAGIC); - tor_assert(c->purpose >= CIRCUIT_PURPOSE_MIN_ && - c->purpose <= CIRCUIT_PURPOSE_MAX_); - - { - /* Having a separate variable for this pleases GCC 4.2 in ways I hope I - * never understand. -NM. */ - circuit_t *nonconst_circ = (circuit_t*) c; - if (CIRCUIT_IS_ORIGIN(c)) - origin_circ = TO_ORIGIN_CIRCUIT(nonconst_circ); - else - or_circ = TO_OR_CIRCUIT(nonconst_circ); - } - - if (c->n_chan) { - tor_assert(!c->n_hop); - - if (c->n_circ_id) { - /* We use the _impl variant here to make sure we don't fail on marked - * circuits, which would not be returned by the regular function. */ - circuit_t *c2 = circuit_get_by_circid_channel_impl(c->n_circ_id, - c->n_chan, NULL); - tor_assert(c == c2); - } - } - if (or_circ && or_circ->p_chan) { - if (or_circ->p_circ_id) { - /* ibid */ - circuit_t *c2 = - circuit_get_by_circid_channel_impl(or_circ->p_circ_id, - or_circ->p_chan, NULL); - tor_assert(c == c2); - } - } - if (or_circ) - for (conn = or_circ->n_streams; conn; conn = conn->next_stream) - tor_assert(conn->base_.type == CONN_TYPE_EXIT); - - tor_assert(c->deliver_window >= 0); - tor_assert(c->package_window >= 0); - if (c->state == CIRCUIT_STATE_OPEN) { - tor_assert(!c->n_chan_create_cell); - if (or_circ) { - tor_assert(or_circ->n_crypto); - tor_assert(or_circ->p_crypto); - tor_assert(or_circ->n_digest); - tor_assert(or_circ->p_digest); - } - } - if (c->state == CIRCUIT_STATE_CHAN_WAIT && !c->marked_for_close) { - tor_assert(circuits_pending_chans && - smartlist_contains(circuits_pending_chans, c)); - } else { - tor_assert(!circuits_pending_chans || - !smartlist_contains(circuits_pending_chans, c)); - } - if (origin_circ && origin_circ->cpath) { - assert_cpath_ok(origin_circ->cpath); - } - if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) { - tor_assert(or_circ); - if (!c->marked_for_close) { - tor_assert(or_circ->rend_splice); - tor_assert(or_circ->rend_splice->rend_splice == or_circ); - } - tor_assert(or_circ->rend_splice != or_circ); - } else { - tor_assert(!or_circ || !or_circ->rend_splice); - } -} - diff --git a/src/tor/circuitlist.h b/src/tor/circuitlist.h deleted file mode 100644 index 1c8cf7d..0000000 --- a/src/tor/circuitlist.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitlist.h - * \brief Header file for circuitlist.c. - **/ - -#ifndef TOR_CIRCUITLIST_H -#define TOR_CIRCUITLIST_H - -#include "testsupport.h" - -TOR_LIST_HEAD(global_circuitlist_s, circuit_t); - -struct global_circuitlist_s* circuit_get_global_list(void); -const char *circuit_state_to_string(int state); -const char *circuit_purpose_to_controller_string(uint8_t purpose); -const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); -const char *circuit_purpose_to_string(uint8_t purpose); -void circuit_dump_by_conn(connection_t *conn, int severity); -void circuit_dump_by_chan(channel_t *chan, int severity); -void circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id, - channel_t *chan); -void circuit_set_n_circid_chan(circuit_t *circ, circid_t id, - channel_t *chan); -void channel_mark_circid_unusable(channel_t *chan, circid_t id); -void channel_mark_circid_usable(channel_t *chan, circid_t id); -void circuit_set_state(circuit_t *circ, uint8_t state); -void circuit_close_all_marked(void); -int32_t circuit_initial_package_window(void); -origin_circuit_t *origin_circuit_new(void); -or_circuit_t *or_circuit_new(circid_t p_circ_id, channel_t *p_chan); -circuit_t *circuit_get_by_circid_channel(circid_t circ_id, - channel_t *chan); -circuit_t * -circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, - channel_t *chan); -int circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan); -circuit_t *circuit_get_by_edge_conn(edge_connection_t *conn); -void circuit_unlink_all_from_channel(channel_t *chan, int reason); -origin_circuit_t *circuit_get_by_global_id(uint32_t id); -origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data( - const rend_data_t *rend_data); -origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, - const char *digest, uint8_t purpose); -or_circuit_t *circuit_get_rendezvous(const char *cookie); -or_circuit_t *circuit_get_intro_point(const char *digest); -origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose, - extend_info_t *info, int flags); -void circuit_mark_all_unused_circs(void); -void circuit_mark_all_dirty_circs_as_unusable(void); -void circuit_mark_for_close_(circuit_t *circ, int reason, - int line, const char *file); -int circuit_get_cpath_len(origin_circuit_t *circ); -void circuit_clear_cpath(origin_circuit_t *circ); -crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum); -void circuit_get_all_pending_on_channel(smartlist_t *out, - channel_t *chan); -int circuit_count_pending_on_channel(channel_t *chan); - -#define circuit_mark_for_close(c, reason) \ - circuit_mark_for_close_((c), (reason), __LINE__, SHORT_FILE__) - -void assert_cpath_layer_ok(const crypt_path_t *cp); -void assert_circuit_ok(const circuit_t *c); -void circuit_free_all(void); -void circuits_handle_oom(size_t current_allocation); - -void channel_note_destroy_pending(channel_t *chan, circid_t id); -void channel_note_destroy_not_pending(channel_t *chan, circid_t id); - -#ifdef CIRCUITLIST_PRIVATE -STATIC void circuit_free(circuit_t *circ); -STATIC size_t n_cells_in_circ_queues(const circuit_t *c); -#endif - -#endif - diff --git a/src/tor/circuitmux.c b/src/tor/circuitmux.c deleted file mode 100644 index f2af943..0000000 --- a/src/tor/circuitmux.c +++ /dev/null @@ -1,1898 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitmux.c - * \brief Circuit mux/cell selection abstraction - **/ - -#include "or.h" -#include "channel.h" -#include "circuitlist.h" -#include "circuitmux.h" -#include "relay.h" - -/* - * Private typedefs for circuitmux.c - */ - -/* - * Map of muxinfos for circuitmux_t to use; struct is defined below (name - * of struct must match HT_HEAD line). - */ -typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t; - -/* - * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to - * break the hash table code). - */ -typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t; - -/* - * Anything the mux wants to store per-circuit in the map; right now just - * a count of queued cells. - */ - -typedef struct circuit_muxinfo_s circuit_muxinfo_t; - -/* - * Structures for circuitmux.c - */ - -/* - * A circuitmux is a collection of circuits; it tracks which subset - * of the attached circuits are 'active' (i.e., have cells available - * to transmit) and how many cells on each. It expoes three distinct - * interfaces to other components: - * - * To channels, which each have a circuitmux_t, the supported operations - * are: - * - * circuitmux_get_first_active_circuit(): - * - * Pick one of the circuitmux's active circuits to send cells from. - * - * circuitmux_notify_xmit_cells(): - * - * Notify the circuitmux that cells have been sent on a circuit. - * - * To circuits, the exposed operations are: - * - * circuitmux_attach_circuit(): - * - * Attach a circuit to the circuitmux; this will allocate any policy- - * specific data wanted for this circuit and add it to the active - * circuits list if it has queued cells. - * - * circuitmux_detach_circuit(): - * - * Detach a circuit from the circuitmux, freeing associated structures. - * - * circuitmux_clear_num_cells(): - * - * Clear the circuitmux's cell counter for this circuit. - * - * circuitmux_set_num_cells(): - * - * Set the circuitmux's cell counter for this circuit. - * - * See circuitmux.h for the circuitmux_policy_t data structure, which contains - * a table of function pointers implementing a circuit selection policy, and - * circuitmux_ewma.c for an example of a circuitmux policy. Circuitmux - * policies can be manipulated with: - * - * circuitmux_get_policy(): - * - * Return the current policy for a circuitmux_t, if any. - * - * circuitmux_clear_policy(): - * - * Remove a policy installed on a circuitmux_t, freeing all associated - * data. The circuitmux will revert to the built-in round-robin behavior. - * - * circuitmux_set_policy(): - * - * Install a policy on a circuitmux_t; the appropriate callbacks will be - * made to attach all existing circuits to the new policy. - * - */ - -struct circuitmux_s { - /* Keep count of attached, active circuits */ - unsigned int n_circuits, n_active_circuits; - - /* Total number of queued cells on all circuits */ - unsigned int n_cells; - - /* - * Map from (channel ID, circuit ID) pairs to circuit_muxinfo_t - */ - chanid_circid_muxinfo_map_t *chanid_circid_map; - - /* - * Double-linked ring of circuits with queued cells waiting for room to - * free up on this connection's outbuf. Every time we pull cells from - * a circuit, we advance this pointer to the next circuit in the ring. - */ - struct circuit_t *active_circuits_head, *active_circuits_tail; - - /** List of queued destroy cells */ - cell_queue_t destroy_cell_queue; - /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit - * returned the destroy queue. Used to force alternation between - * destroy/non-destroy cells. - * - * XXXX There is no reason to think that alternating is a particularly good - * approach -- it's just designed to prevent destroys from starving other - * cells completely. - */ - unsigned int last_cell_was_destroy : 1; - /** Destroy counter: increment this when a destroy gets queued, decrement - * when we unqueue it, so we can test to make sure they don't starve. - */ - int64_t destroy_ctr; - - /* - * Circuitmux policy; if this is non-NULL, it can override the built- - * in round-robin active circuits behavior. This is how EWMA works in - * the new circuitmux_t world. - */ - const circuitmux_policy_t *policy; - - /* Policy-specific data */ - circuitmux_policy_data_t *policy_data; -}; - -/* - * This struct holds whatever we want to store per attached circuit on a - * circuitmux_t; right now, just the count of queued cells and the direction. - */ - -struct circuit_muxinfo_s { - /* Count of cells on this circuit at last update */ - unsigned int cell_count; - /* Direction of flow */ - cell_direction_t direction; - /* Policy-specific data */ - circuitmux_policy_circ_data_t *policy_data; - /* Mark bit for consistency checker */ - unsigned int mark:1; -}; - -/* - * A map from channel ID and circuit ID to a circuit_muxinfo_t for that - * circuit. - */ - -struct chanid_circid_muxinfo_t { - HT_ENTRY(chanid_circid_muxinfo_t) node; - uint64_t chan_id; - circid_t circ_id; - circuit_muxinfo_t muxinfo; -}; - -/* - * Internal-use #defines - */ - -#ifdef CMUX_PARANOIA -#define circuitmux_assert_okay_paranoid(cmux) \ - circuitmux_assert_okay(cmux) -#else -#define circuitmux_assert_okay_paranoid(cmux) -#endif - -/* - * Static function declarations - */ - -static INLINE int -chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, - chanid_circid_muxinfo_t *b); -static INLINE unsigned int -chanid_circid_entry_hash(chanid_circid_muxinfo_t *a); -static chanid_circid_muxinfo_t * -circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ); -static void -circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction); -static void -circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction); -static INLINE void -circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction); -static INLINE circuit_t ** -circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ); -static INLINE circuit_t ** -circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); -static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux); -static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux); -static void circuitmux_assert_okay_pass_three(circuitmux_t *cmux); - -/* Static global variables */ - -/** Count the destroy balance to debug destroy queue logic */ -static int64_t global_destroy_ctr = 0; - -/* Function definitions */ - -/** - * Linked list helpers - */ - -/** - * Move an active circuit to the tail of the cmux's active circuits list; - * used by circuitmux_notify_xmit_cells(). - */ - -static INLINE void -circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction) -{ - circuit_t **next_p = NULL, **prev_p = NULL; - circuit_t **next_prev = NULL, **prev_next = NULL; - circuit_t **tail_next = NULL; - or_circuit_t *or_circ = NULL; - - tor_assert(cmux); - tor_assert(circ); - - circuitmux_assert_okay_paranoid(cmux); - - /* Figure out our next_p and prev_p for this cmux/direction */ - if (direction) { - if (direction == CELL_DIRECTION_OUT) { - tor_assert(circ->n_mux == cmux); - next_p = &(circ->next_active_on_n_chan); - prev_p = &(circ->prev_active_on_n_chan); - } else { - or_circ = TO_OR_CIRCUIT(circ); - tor_assert(or_circ->p_mux == cmux); - next_p = &(or_circ->next_active_on_p_chan); - prev_p = &(or_circ->prev_active_on_p_chan); - } - } else { - if (circ->n_mux == cmux) { - next_p = &(circ->next_active_on_n_chan); - prev_p = &(circ->prev_active_on_n_chan); - direction = CELL_DIRECTION_OUT; - } else { - or_circ = TO_OR_CIRCUIT(circ); - tor_assert(or_circ->p_mux == cmux); - next_p = &(or_circ->next_active_on_p_chan); - prev_p = &(or_circ->prev_active_on_p_chan); - direction = CELL_DIRECTION_IN; - } - } - tor_assert(next_p); - tor_assert(prev_p); - - /* Check if this really is an active circuit */ - if ((*next_p == NULL && *prev_p == NULL) && - !(circ == cmux->active_circuits_head || - circ == cmux->active_circuits_tail)) { - /* Not active, no-op */ - return; - } - - /* Check if this is already the tail */ - if (circ == cmux->active_circuits_tail) return; - - /* Okay, we have to move it; figure out next_prev and prev_next */ - if (*next_p) next_prev = circuitmux_prev_active_circ_p(cmux, *next_p); - if (*prev_p) prev_next = circuitmux_next_active_circ_p(cmux, *prev_p); - /* Adjust the previous node's next pointer, if any */ - if (prev_next) *prev_next = *next_p; - /* Otherwise, we were the head */ - else cmux->active_circuits_head = *next_p; - /* Adjust the next node's previous pointer, if any */ - if (next_prev) *next_prev = *prev_p; - /* We're out of the list; now re-attach at the tail */ - /* Adjust our next and prev pointers */ - *next_p = NULL; - *prev_p = cmux->active_circuits_tail; - /* Set the next pointer of the tail, or the head if none */ - if (cmux->active_circuits_tail) { - tail_next = circuitmux_next_active_circ_p(cmux, - cmux->active_circuits_tail); - *tail_next = circ; - } else { - cmux->active_circuits_head = circ; - } - /* Set the tail to this circuit */ - cmux->active_circuits_tail = circ; - - circuitmux_assert_okay_paranoid(cmux); -} - -static INLINE circuit_t ** -circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ) -{ - tor_assert(cmux); - tor_assert(circ); - - if (circ->n_mux == cmux) return &(circ->next_active_on_n_chan); - else { - tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); - return &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); - } -} - -static INLINE circuit_t ** -circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ) -{ - tor_assert(cmux); - tor_assert(circ); - - if (circ->n_mux == cmux) return &(circ->prev_active_on_n_chan); - else { - tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); - return &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); - } -} - -/** - * Helper for chanid_circid_cell_count_map_t hash table: compare the channel - * ID and circuit ID for a and b, and return less than, equal to, or greater - * than zero appropriately. - */ - -static INLINE int -chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, - chanid_circid_muxinfo_t *b) -{ - return a->chan_id == b->chan_id && a->circ_id == b->circ_id; -} - -/** - * Helper: return a hash based on circuit ID and channel ID in a. - */ - -static INLINE unsigned int -chanid_circid_entry_hash(chanid_circid_muxinfo_t *a) -{ - return (((unsigned int)(a->circ_id) << 8) ^ - ((unsigned int)((a->chan_id >> 32) & 0xffffffff)) ^ - ((unsigned int)(a->chan_id & 0xffffffff))); -} - -/* Declare the struct chanid_circid_muxinfo_map type */ -HT_HEAD(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t); - -/* Emit a bunch of hash table stuff */ -HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, - chanid_circid_entry_hash, chanid_circid_entries_eq); -HT_GENERATE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, - chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6, - malloc, realloc, free); - -/* - * Circuitmux alloc/free functions - */ - -/** - * Allocate a new circuitmux_t - */ - -circuitmux_t * -circuitmux_alloc(void) -{ - circuitmux_t *rv = NULL; - - rv = tor_malloc_zero(sizeof(*rv)); - rv->chanid_circid_map = tor_malloc_zero(sizeof(*( rv->chanid_circid_map))); - HT_INIT(chanid_circid_muxinfo_map, rv->chanid_circid_map); - cell_queue_init(&rv->destroy_cell_queue); - - return rv; -} - -/** - * Detach all circuits from a circuitmux (use before circuitmux_free()) - */ - -void -circuitmux_detach_all_circuits(circuitmux_t *cmux) -{ - chanid_circid_muxinfo_t **i = NULL, *to_remove; - channel_t *chan = NULL; - circuit_t *circ = NULL; - - tor_assert(cmux); - /* - * Don't circuitmux_assert_okay_paranoid() here; this gets called when - * channels are being freed and have already been unregistered, so - * the channel ID lookups it does will fail. - */ - - i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); - while (i) { - to_remove = *i; - if (to_remove) { - /* Find a channel and circuit */ - chan = channel_find_by_global_id(to_remove->chan_id); - if (chan) { - circ = - circuit_get_by_circid_channel_even_if_marked(to_remove->circ_id, - chan); - if (circ) { - /* Clear the circuit's mux for this direction */ - if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { - /* - * Update active_circuits et al.; this does policy notifies, so - * comes before freeing policy data - */ - - if (to_remove->muxinfo.cell_count > 0) { - circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); - } - - /* Clear n_mux */ - circ->n_mux = NULL; - } else if (circ->magic == OR_CIRCUIT_MAGIC) { - /* - * Update active_circuits et al.; this does policy notifies, so - * comes before freeing policy data - */ - - if (to_remove->muxinfo.cell_count > 0) { - circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); - } - - /* - * It has a sensible p_chan and direction == CELL_DIRECTION_IN, - * so clear p_mux. - */ - TO_OR_CIRCUIT(circ)->p_mux = NULL; - } else { - /* Complain and move on */ - log_warn(LD_CIRC, - "Circuit %u/channel " U64_FORMAT " had direction == " - "CELL_DIRECTION_IN, but isn't an or_circuit_t", - (unsigned)to_remove->circ_id, - U64_PRINTF_ARG(to_remove->chan_id)); - } - - /* Free policy-specific data if we have it */ - if (to_remove->muxinfo.policy_data) { - /* - * If we have policy data, assert that we have the means to - * free it - */ - tor_assert(cmux->policy); - tor_assert(cmux->policy->free_circ_data); - /* Call free_circ_data() */ - cmux->policy->free_circ_data(cmux, - cmux->policy_data, - circ, - to_remove->muxinfo.policy_data); - to_remove->muxinfo.policy_data = NULL; - } - } else { - /* Complain and move on */ - log_warn(LD_CIRC, - "Couldn't find circuit %u (for channel " U64_FORMAT ")", - (unsigned)to_remove->circ_id, - U64_PRINTF_ARG(to_remove->chan_id)); - } - } else { - /* Complain and move on */ - log_warn(LD_CIRC, - "Couldn't find channel " U64_FORMAT " (for circuit id %u)", - U64_PRINTF_ARG(to_remove->chan_id), - (unsigned)to_remove->circ_id); - } - - /* Assert that we don't have un-freed policy data for this circuit */ - tor_assert(to_remove->muxinfo.policy_data == NULL); - } - - i = HT_NEXT_RMV(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); - - /* Free it */ - tor_free(to_remove); - } - - cmux->n_circuits = 0; - cmux->n_active_circuits = 0; - cmux->n_cells = 0; -} - -/** Reclaim all circuit IDs currently marked as unusable on chan because - * of pending destroy cells in cmux. - * - * This function must be called AFTER circuits are unlinked from the (channel, - * circuid-id) map with circuit_unlink_all_from_channel(), but before calling - * circuitmux_free(). - */ -void -circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan) -{ - packed_cell_t *cell; - int n_bad = 0; - TOR_SIMPLEQ_FOREACH(cell, &cmux->destroy_cell_queue.head, next) { - circid_t circid = 0; - if (packed_cell_is_destroy(chan, cell, &circid)) { - channel_mark_circid_usable(chan, circid); - } else { - ++n_bad; - } - } - if (n_bad) - log_warn(LD_BUG, "%d cell(s) on destroy queue did not look like a " - "DESTROY cell.", n_bad); -} - -/** - * Free a circuitmux_t; the circuits must be detached first with - * circuitmux_detach_all_circuits(). - */ - -void -circuitmux_free(circuitmux_t *cmux) -{ - if (!cmux) return; - - tor_assert(cmux->n_circuits == 0); - tor_assert(cmux->n_active_circuits == 0); - - /* - * Free policy-specific data if we have any; we don't - * need to do circuitmux_set_policy(cmux, NULL) to cover - * the circuits because they would have been handled in - * circuitmux_detach_all_circuits() before this was - * called. - */ - if (cmux->policy && cmux->policy->free_cmux_data) { - if (cmux->policy_data) { - cmux->policy->free_cmux_data(cmux, cmux->policy_data); - cmux->policy_data = NULL; - } - } else tor_assert(cmux->policy_data == NULL); - - if (cmux->chanid_circid_map) { - HT_CLEAR(chanid_circid_muxinfo_map, cmux->chanid_circid_map); - tor_free(cmux->chanid_circid_map); - } - - /* - * We're throwing away some destroys; log the counter and - * adjust the global counter by the queue size. - */ - if (cmux->destroy_cell_queue.n > 0) { - cmux->destroy_ctr -= cmux->destroy_cell_queue.n; - global_destroy_ctr -= cmux->destroy_cell_queue.n; - log_debug(LD_CIRC, - "Freeing cmux at %p with %u queued destroys; the last cmux " - "destroy balance was "I64_FORMAT", global is "I64_FORMAT, - cmux, cmux->destroy_cell_queue.n, - I64_PRINTF_ARG(cmux->destroy_ctr), - I64_PRINTF_ARG(global_destroy_ctr)); - } else { - log_debug(LD_CIRC, - "Freeing cmux at %p with no queued destroys, the cmux destroy " - "balance was "I64_FORMAT", global is "I64_FORMAT, - cmux, - I64_PRINTF_ARG(cmux->destroy_ctr), - I64_PRINTF_ARG(global_destroy_ctr)); - } - - cell_queue_clear(&cmux->destroy_cell_queue); - - tor_free(cmux); -} - -/* - * Circuitmux policy control functions - */ - -/** - * Remove any policy installed on cmux; all policy data will be freed and - * cmux behavior will revert to the built-in round-robin active_circuits - * mechanism. - */ - -void -circuitmux_clear_policy(circuitmux_t *cmux) -{ - tor_assert(cmux); - - /* Internally, this is just setting policy to NULL */ - if (cmux->policy) { - circuitmux_set_policy(cmux, NULL); - } -} - -/** - * Return the policy currently installed on a circuitmux_t - */ - -const circuitmux_policy_t * -circuitmux_get_policy(circuitmux_t *cmux) -{ - tor_assert(cmux); - - return cmux->policy; -} - -/** - * Set policy; allocate for new policy, detach all circuits from old policy - * if any, attach them to new policy, and free old policy data. - */ - -void -circuitmux_set_policy(circuitmux_t *cmux, - const circuitmux_policy_t *pol) -{ - const circuitmux_policy_t *old_pol = NULL, *new_pol = NULL; - circuitmux_policy_data_t *old_pol_data = NULL, *new_pol_data = NULL; - chanid_circid_muxinfo_t **i = NULL; - channel_t *chan = NULL; - uint64_t last_chan_id_searched = 0; - circuit_t *circ = NULL; - - tor_assert(cmux); - - /* Set up variables */ - old_pol = cmux->policy; - old_pol_data = cmux->policy_data; - new_pol = pol; - - /* Check if this is the trivial case */ - if (old_pol == new_pol) return; - - /* Allocate data for new policy, if any */ - if (new_pol && new_pol->alloc_cmux_data) { - /* - * If alloc_cmux_data is not null, then we expect to get some policy - * data. Assert that we also have free_cmux_data so we can free it - * when the time comes, and allocate it. - */ - tor_assert(new_pol->free_cmux_data); - new_pol_data = new_pol->alloc_cmux_data(cmux); - tor_assert(new_pol_data); - } - - /* Install new policy and new policy data on cmux */ - cmux->policy = new_pol; - cmux->policy_data = new_pol_data; - - /* Iterate over all circuits, attaching/detaching each one */ - i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); - while (i) { - /* Assert that this entry isn't NULL */ - tor_assert(*i); - - /* - * Get the channel; since normal case is all circuits on the mux share a - * channel, we cache last_chan_id_searched - */ - if (!chan || last_chan_id_searched != (*i)->chan_id) { - chan = channel_find_by_global_id((*i)->chan_id); - last_chan_id_searched = (*i)->chan_id; - } - tor_assert(chan); - - /* Get the circuit */ - circ = circuit_get_by_circid_channel_even_if_marked((*i)->circ_id, chan); - tor_assert(circ); - - /* Need to tell old policy it becomes inactive (i.e., it is active) ? */ - if (old_pol && old_pol->notify_circ_inactive && - (*i)->muxinfo.cell_count > 0) { - old_pol->notify_circ_inactive(cmux, old_pol_data, circ, - (*i)->muxinfo.policy_data); - } - - /* Need to free old policy data? */ - if ((*i)->muxinfo.policy_data) { - /* Assert that we have the means to free it if we have policy data */ - tor_assert(old_pol); - tor_assert(old_pol->free_circ_data); - /* Free it */ - old_pol->free_circ_data(cmux, old_pol_data, circ, - (*i)->muxinfo.policy_data); - (*i)->muxinfo.policy_data = NULL; - } - - /* Need to allocate new policy data? */ - if (new_pol && new_pol->alloc_circ_data) { - /* - * If alloc_circ_data is not null, we expect to get some per-circuit - * policy data. Assert that we also have free_circ_data so we can - * free it when the time comes, and allocate it. - */ - tor_assert(new_pol->free_circ_data); - (*i)->muxinfo.policy_data = - new_pol->alloc_circ_data(cmux, new_pol_data, circ, - (*i)->muxinfo.direction, - (*i)->muxinfo.cell_count); - } - - /* Need to make active on new policy? */ - if (new_pol && new_pol->notify_circ_active && - (*i)->muxinfo.cell_count > 0) { - new_pol->notify_circ_active(cmux, new_pol_data, circ, - (*i)->muxinfo.policy_data); - } - - /* Advance to next circuit map entry */ - i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); - } - - /* Free data for old policy, if any */ - if (old_pol_data) { - /* - * If we had old policy data, we should have an old policy and a free - * function for it. - */ - tor_assert(old_pol); - tor_assert(old_pol->free_cmux_data); - old_pol->free_cmux_data(cmux, old_pol_data); - old_pol_data = NULL; - } -} - -/* - * Circuitmux/circuit attachment status inquiry functions - */ - -/** - * Query the direction of an attached circuit - */ - -cell_direction_t -circuitmux_attached_circuit_direction(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t *hashent = NULL; - - /* Try to find a map entry */ - hashent = circuitmux_find_map_entry(cmux, circ); - - /* - * This function should only be called on attached circuits; assert that - * we had a map entry. - */ - tor_assert(hashent); - - /* Return the direction from the map entry */ - return hashent->muxinfo.direction; -} - -/** - * Find an entry in the cmux's map for this circuit or return NULL if there - * is none. - */ - -static chanid_circid_muxinfo_t * -circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t search, *hashent = NULL; - - /* Sanity-check parameters */ - tor_assert(cmux); - tor_assert(cmux->chanid_circid_map); - tor_assert(circ); - - /* Check if we have n_chan */ - if (circ->n_chan) { - /* Okay, let's see if it's attached for n_chan/n_circ_id */ - search.chan_id = circ->n_chan->global_identifier; - search.circ_id = circ->n_circ_id; - - /* Query */ - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - } - - /* Found something? */ - if (hashent) { - /* - * Assert that the direction makes sense for a hashent we found by - * n_chan/n_circ_id before we return it. - */ - tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_OUT); - } else { - /* Not there, have we got a p_chan/p_circ_id to try? */ - if (circ->magic == OR_CIRCUIT_MAGIC) { - search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - /* Check for p_chan */ - if (TO_OR_CIRCUIT(circ)->p_chan) { - search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; - /* Okay, search for that */ - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - /* Find anything? */ - if (hashent) { - /* Assert that the direction makes sense before we return it */ - tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_IN); - } - } - } - } - - /* Okay, hashent is it if it was there */ - return hashent; -} - -/** - * Query whether a circuit is attached to a circuitmux - */ - -int -circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t *hashent = NULL; - - /* Look if it's in the circuit map */ - hashent = circuitmux_find_map_entry(cmux, circ); - - return (hashent != NULL); -} - -/** - * Query whether a circuit is active on a circuitmux - */ - -int -circuitmux_is_circuit_active(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t *hashent = NULL; - int is_active = 0; - - tor_assert(cmux); - tor_assert(circ); - - /* Look if it's in the circuit map */ - hashent = circuitmux_find_map_entry(cmux, circ); - if (hashent) { - /* Check the number of cells on this circuit */ - is_active = (hashent->muxinfo.cell_count > 0); - } - /* else not attached, so not active */ - - return is_active; -} - -/** - * Query number of available cells for a circuit on a circuitmux - */ - -unsigned int -circuitmux_num_cells_for_circuit(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t *hashent = NULL; - unsigned int n_cells = 0; - - tor_assert(cmux); - tor_assert(circ); - - /* Look if it's in the circuit map */ - hashent = circuitmux_find_map_entry(cmux, circ); - if (hashent) { - /* Just get the cell count for this circuit */ - n_cells = hashent->muxinfo.cell_count; - } - /* else not attached, so 0 cells */ - - return n_cells; -} - -/** - * Query total number of available cells on a circuitmux - */ - -unsigned int -circuitmux_num_cells(circuitmux_t *cmux) -{ - tor_assert(cmux); - - return cmux->n_cells + cmux->destroy_cell_queue.n; -} - -/** - * Query total number of circuits active on a circuitmux - */ - -unsigned int -circuitmux_num_active_circuits(circuitmux_t *cmux) -{ - tor_assert(cmux); - - return cmux->n_active_circuits; -} - -/** - * Query total number of circuits attached to a circuitmux - */ - -unsigned int -circuitmux_num_circuits(circuitmux_t *cmux) -{ - tor_assert(cmux); - - return cmux->n_circuits; -} - -/* - * Functions for circuit code to call to update circuit status - */ - -/** - * Attach a circuit to a circuitmux, for the specified direction. - */ - -MOCK_IMPL(void, -circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction)) -{ - channel_t *chan = NULL; - uint64_t channel_id; - circid_t circ_id; - chanid_circid_muxinfo_t search, *hashent = NULL; - unsigned int cell_count; - - tor_assert(cmux); - tor_assert(circ); - tor_assert(direction == CELL_DIRECTION_IN || - direction == CELL_DIRECTION_OUT); - circuitmux_assert_okay_paranoid(cmux); - - /* - * Figure out which channel we're using, and get the circuit's current - * cell count and circuit ID; assert that the circuit is not already - * attached to another mux. - */ - if (direction == CELL_DIRECTION_OUT) { - /* It's n_chan */ - chan = circ->n_chan; - cell_count = circ->n_chan_cells.n; - circ_id = circ->n_circ_id; - } else { - /* We want p_chan */ - chan = TO_OR_CIRCUIT(circ)->p_chan; - cell_count = TO_OR_CIRCUIT(circ)->p_chan_cells.n; - circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - } - /* Assert that we did get a channel */ - tor_assert(chan); - /* Assert that the circuit ID is sensible */ - tor_assert(circ_id != 0); - - /* Get the channel ID */ - channel_id = chan->global_identifier; - - /* See if we already have this one */ - search.chan_id = channel_id; - search.circ_id = circ_id; - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - - if (hashent) { - /* - * This circuit was already attached to this cmux; make sure the - * directions match and update the cell count and active circuit count. - */ - log_info(LD_CIRC, - "Circuit %u on channel " U64_FORMAT " was already attached to " - "cmux %p (trying to attach to %p)", - (unsigned)circ_id, U64_PRINTF_ARG(channel_id), - ((direction == CELL_DIRECTION_OUT) ? - circ->n_mux : TO_OR_CIRCUIT(circ)->p_mux), - cmux); - - /* - * The mux pointer on this circuit and the direction in result should - * match; otherwise assert. - */ - if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == cmux); - else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); - tor_assert(hashent->muxinfo.direction == direction); - - /* - * Looks okay; just update the cell count and active circuits if we must - */ - if (hashent->muxinfo.cell_count > 0 && cell_count == 0) { - --(cmux->n_active_circuits); - circuitmux_make_circuit_inactive(cmux, circ, direction); - } else if (hashent->muxinfo.cell_count == 0 && cell_count > 0) { - ++(cmux->n_active_circuits); - circuitmux_make_circuit_active(cmux, circ, direction); - } - cmux->n_cells -= hashent->muxinfo.cell_count; - cmux->n_cells += cell_count; - hashent->muxinfo.cell_count = cell_count; - } else { - /* - * New circuit; add an entry and update the circuit/active circuit - * counts. - */ - log_debug(LD_CIRC, - "Attaching circuit %u on channel " U64_FORMAT " to cmux %p", - (unsigned)circ_id, U64_PRINTF_ARG(channel_id), cmux); - - /* - * Assert that the circuit doesn't already have a mux for this - * direction. - */ - if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == NULL); - else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == NULL); - - /* Insert it in the map */ - hashent = tor_malloc_zero(sizeof(*hashent)); - hashent->chan_id = channel_id; - hashent->circ_id = circ_id; - hashent->muxinfo.cell_count = cell_count; - hashent->muxinfo.direction = direction; - /* Allocate policy specific circuit data if we need it */ - if (cmux->policy && cmux->policy->alloc_circ_data) { - /* Assert that we have the means to free policy-specific data */ - tor_assert(cmux->policy->free_circ_data); - /* Allocate it */ - hashent->muxinfo.policy_data = - cmux->policy->alloc_circ_data(cmux, - cmux->policy_data, - circ, - direction, - cell_count); - /* If we wanted policy data, it's an error not to get any */ - tor_assert(hashent->muxinfo.policy_data); - } - HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - hashent); - - /* Set the circuit's mux for this direction */ - if (direction == CELL_DIRECTION_OUT) circ->n_mux = cmux; - else TO_OR_CIRCUIT(circ)->p_mux = cmux; - - /* Make sure the next/prev pointers are NULL */ - if (direction == CELL_DIRECTION_OUT) { - circ->next_active_on_n_chan = NULL; - circ->prev_active_on_n_chan = NULL; - } else { - TO_OR_CIRCUIT(circ)->next_active_on_p_chan = NULL; - TO_OR_CIRCUIT(circ)->prev_active_on_p_chan = NULL; - } - - /* Update counters */ - ++(cmux->n_circuits); - if (cell_count > 0) { - ++(cmux->n_active_circuits); - circuitmux_make_circuit_active(cmux, circ, direction); - } - cmux->n_cells += cell_count; - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/** - * Detach a circuit from a circuitmux and update all counters as needed; - * no-op if not attached. - */ - -MOCK_IMPL(void, -circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ)) -{ - chanid_circid_muxinfo_t search, *hashent = NULL; - /* - * Use this to keep track of whether we found it for n_chan or - * p_chan for consistency checking. - */ - cell_direction_t last_searched_direction; - - tor_assert(cmux); - tor_assert(cmux->chanid_circid_map); - tor_assert(circ); - circuitmux_assert_okay_paranoid(cmux); - - /* See if we have it for n_chan/n_circ_id */ - if (circ->n_chan) { - search.chan_id = circ->n_chan->global_identifier; - search.circ_id = circ->n_circ_id; - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - last_searched_direction = CELL_DIRECTION_OUT; - } - - /* Got one? If not, see if it's an or_circuit_t and try p_chan/p_circ_id */ - if (!hashent) { - if (circ->magic == OR_CIRCUIT_MAGIC) { - search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - if (TO_OR_CIRCUIT(circ)->p_chan) { - search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; - hashent = HT_FIND(chanid_circid_muxinfo_map, - cmux->chanid_circid_map, - &search); - last_searched_direction = CELL_DIRECTION_IN; - } - } - } - - /* - * If hashent isn't NULL, we have a circuit to detach; don't remove it from - * the map until later of circuitmux_make_circuit_inactive() breaks. - */ - if (hashent) { - /* Update counters */ - --(cmux->n_circuits); - if (hashent->muxinfo.cell_count > 0) { - --(cmux->n_active_circuits); - /* This does policy notifies, so comes before freeing policy data */ - circuitmux_make_circuit_inactive(cmux, circ, last_searched_direction); - } - cmux->n_cells -= hashent->muxinfo.cell_count; - - /* Free policy-specific data if we have it */ - if (hashent->muxinfo.policy_data) { - /* If we have policy data, assert that we have the means to free it */ - tor_assert(cmux->policy); - tor_assert(cmux->policy->free_circ_data); - /* Call free_circ_data() */ - cmux->policy->free_circ_data(cmux, - cmux->policy_data, - circ, - hashent->muxinfo.policy_data); - hashent->muxinfo.policy_data = NULL; - } - - /* Consistency check: the direction must match the direction searched */ - tor_assert(last_searched_direction == hashent->muxinfo.direction); - /* Clear the circuit's mux for this direction */ - if (last_searched_direction == CELL_DIRECTION_OUT) circ->n_mux = NULL; - else TO_OR_CIRCUIT(circ)->p_mux = NULL; - - /* Now remove it from the map */ - HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent); - - /* Free the hash entry */ - tor_free(hashent); - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/** - * Make a circuit active; update active list and policy-specific info, but - * we don't mess with the counters or hash table here. - */ - -static void -circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction) -{ - circuit_t **next_active = NULL, **prev_active = NULL, **next_prev = NULL; - circuitmux_t *circuit_cmux = NULL; - chanid_circid_muxinfo_t *hashent = NULL; - channel_t *chan = NULL; - circid_t circ_id; - int already_active; - - tor_assert(cmux); - tor_assert(circ); - tor_assert(direction == CELL_DIRECTION_OUT || - direction == CELL_DIRECTION_IN); - /* - * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count - * already got changed and we have to update the list for it to be consistent - * again. - */ - - /* Get the right set of active list links for this direction */ - if (direction == CELL_DIRECTION_OUT) { - next_active = &(circ->next_active_on_n_chan); - prev_active = &(circ->prev_active_on_n_chan); - circuit_cmux = circ->n_mux; - chan = circ->n_chan; - circ_id = circ->n_circ_id; - } else { - next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); - prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); - circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux; - chan = TO_OR_CIRCUIT(circ)->p_chan; - circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - } - - /* Assert that it is attached to this mux and a channel */ - tor_assert(cmux == circuit_cmux); - tor_assert(chan != NULL); - - /* - * Check if the circuit really was inactive; if it's active, at least one - * of the next_active and prev_active pointers will not be NULL, or this - * circuit will be either the head or tail of the list for this cmux. - */ - already_active = (*prev_active != NULL || *next_active != NULL || - cmux->active_circuits_head == circ || - cmux->active_circuits_tail == circ); - - /* If we're already active, log a warning and finish */ - if (already_active) { - log_warn(LD_CIRC, - "Circuit %u on channel " U64_FORMAT " was already active", - (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier)); - return; - } - - /* - * This is going at the head of the list; if the old head is not NULL, - * then its prev pointer should point to this. - */ - *next_active = cmux->active_circuits_head; /* Next is old head */ - *prev_active = NULL; /* Prev is NULL (this will be the head) */ - if (cmux->active_circuits_head) { - /* The list had an old head; update its prev pointer */ - next_prev = - circuitmux_prev_active_circ_p(cmux, cmux->active_circuits_head); - tor_assert(next_prev); - *next_prev = circ; - } else { - /* The list was empty; this becomes the tail as well */ - cmux->active_circuits_tail = circ; - } - /* This becomes the new head of the list */ - cmux->active_circuits_head = circ; - - /* Policy-specific notification */ - if (cmux->policy && - cmux->policy->notify_circ_active) { - /* Okay, we need to check the circuit for policy data now */ - hashent = circuitmux_find_map_entry(cmux, circ); - /* We should have found something */ - tor_assert(hashent); - /* Notify */ - cmux->policy->notify_circ_active(cmux, cmux->policy_data, - circ, hashent->muxinfo.policy_data); - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/** - * Make a circuit inactive; update active list and policy-specific info, but - * we don't mess with the counters or hash table here. - */ - -static void -circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction) -{ - circuit_t **next_active = NULL, **prev_active = NULL; - circuit_t **next_prev = NULL, **prev_next = NULL; - circuitmux_t *circuit_cmux = NULL; - chanid_circid_muxinfo_t *hashent = NULL; - channel_t *chan = NULL; - circid_t circ_id; - int already_inactive; - - tor_assert(cmux); - tor_assert(circ); - tor_assert(direction == CELL_DIRECTION_OUT || - direction == CELL_DIRECTION_IN); - /* - * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count - * already got changed and we have to update the list for it to be consistent - * again. - */ - - /* Get the right set of active list links for this direction */ - if (direction == CELL_DIRECTION_OUT) { - next_active = &(circ->next_active_on_n_chan); - prev_active = &(circ->prev_active_on_n_chan); - circuit_cmux = circ->n_mux; - chan = circ->n_chan; - circ_id = circ->n_circ_id; - } else { - next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); - prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); - circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux; - chan = TO_OR_CIRCUIT(circ)->p_chan; - circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - } - - /* Assert that it is attached to this mux and a channel */ - tor_assert(cmux == circuit_cmux); - tor_assert(chan != NULL); - - /* - * Check if the circuit really was active; if it's inactive, the - * next_active and prev_active pointers will be NULL and this circuit - * will not be the head or tail of the list for this cmux. - */ - already_inactive = (*prev_active == NULL && *next_active == NULL && - cmux->active_circuits_head != circ && - cmux->active_circuits_tail != circ); - - /* If we're already inactive, log a warning and finish */ - if (already_inactive) { - log_warn(LD_CIRC, - "Circuit %d on channel " U64_FORMAT " was already inactive", - (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier)); - return; - } - - /* Remove from the list; first get next_prev and prev_next */ - if (*next_active) { - /* - * If there's a next circuit, its previous circuit becomes this - * circuit's previous circuit. - */ - next_prev = circuitmux_prev_active_circ_p(cmux, *next_active); - } else { - /* Else, the tail becomes this circuit's previous circuit */ - next_prev = &(cmux->active_circuits_tail); - } - - /* Got next_prev, now prev_next */ - if (*prev_active) { - /* - * If there's a previous circuit, its next circuit becomes this circuit's - * next circuit. - */ - prev_next = circuitmux_next_active_circ_p(cmux, *prev_active); - } else { - /* Else, the head becomes this circuit's next circuit */ - prev_next = &(cmux->active_circuits_head); - } - - /* Assert that we got sensible values for the next/prev pointers */ - tor_assert(next_prev != NULL); - tor_assert(prev_next != NULL); - - /* Update the next/prev pointers - this removes circ from the list */ - *next_prev = *prev_active; - *prev_next = *next_active; - - /* Now null out prev_active/next_active */ - *prev_active = NULL; - *next_active = NULL; - - /* Policy-specific notification */ - if (cmux->policy && - cmux->policy->notify_circ_inactive) { - /* Okay, we need to check the circuit for policy data now */ - hashent = circuitmux_find_map_entry(cmux, circ); - /* We should have found something */ - tor_assert(hashent); - /* Notify */ - cmux->policy->notify_circ_inactive(cmux, cmux->policy_data, - circ, hashent->muxinfo.policy_data); - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/** - * Clear the cell counter for a circuit on a circuitmux - */ - -void -circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ) -{ - /* This is the same as setting the cell count to zero */ - circuitmux_set_num_cells(cmux, circ, 0); -} - -/** - * Set the cell counter for a circuit on a circuitmux - */ - -void -circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, - unsigned int n_cells) -{ - chanid_circid_muxinfo_t *hashent = NULL; - - tor_assert(cmux); - tor_assert(circ); - - circuitmux_assert_okay_paranoid(cmux); - - /* Search for this circuit's entry */ - hashent = circuitmux_find_map_entry(cmux, circ); - /* Assert that we found one */ - tor_assert(hashent); - - /* Update cmux cell counter */ - cmux->n_cells -= hashent->muxinfo.cell_count; - cmux->n_cells += n_cells; - - /* Do we need to notify a cmux policy? */ - if (cmux->policy && cmux->policy->notify_set_n_cells) { - /* Call notify_set_n_cells */ - cmux->policy->notify_set_n_cells(cmux, - cmux->policy_data, - circ, - hashent->muxinfo.policy_data, - n_cells); - } - - /* - * Update cmux active circuit counter: is the old cell count > 0 and the - * new cell count == 0 ? - */ - if (hashent->muxinfo.cell_count > 0 && n_cells == 0) { - --(cmux->n_active_circuits); - hashent->muxinfo.cell_count = n_cells; - circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); - /* Is the old cell count == 0 and the new cell count > 0 ? */ - } else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) { - ++(cmux->n_active_circuits); - hashent->muxinfo.cell_count = n_cells; - circuitmux_make_circuit_active(cmux, circ, hashent->muxinfo.direction); - } else { - /* - * Update the entry cell count like this so we can put a - * circuitmux_assert_okay_paranoid inside make_circuit_(in)active() too. - */ - hashent->muxinfo.cell_count = n_cells; - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/* - * Functions for channel code to call to get a circuit to transmit from or - * notify that cells have been transmitted. - */ - -/** - * Pick a circuit to send from, using the active circuits list or a - * circuitmux policy if one is available. This is called from channel.c. - * - * If we would rather send a destroy cell, return NULL and set - * *destroy_queue_out to the destroy queue. - * - * If we have nothing to send, set *destroy_queue_out to NULL and - * return NULL. - */ - -circuit_t * -circuitmux_get_first_active_circuit(circuitmux_t *cmux, - cell_queue_t **destroy_queue_out) -{ - circuit_t *circ = NULL; - - tor_assert(cmux); - tor_assert(destroy_queue_out); - - *destroy_queue_out = NULL; - - if (cmux->destroy_cell_queue.n && - (!cmux->last_cell_was_destroy || cmux->n_active_circuits == 0)) { - /* We have destroy cells to send, and either we just sent a relay cell, - * or we have no relay cells to send. */ - - /* XXXX We should let the cmux policy have some say in this eventually. */ - /* XXXX Alternating is not a terribly brilliant approach here. */ - *destroy_queue_out = &cmux->destroy_cell_queue; - - cmux->last_cell_was_destroy = 1; - } else if (cmux->n_active_circuits > 0) { - /* We also must have a cell available for this to be the case */ - tor_assert(cmux->n_cells > 0); - /* Do we have a policy-provided circuit selector? */ - if (cmux->policy && cmux->policy->pick_active_circuit) { - circ = cmux->policy->pick_active_circuit(cmux, cmux->policy_data); - } - /* Fall back on the head of the active circuits list */ - if (!circ) { - tor_assert(cmux->active_circuits_head); - circ = cmux->active_circuits_head; - } - cmux->last_cell_was_destroy = 0; - } else { - tor_assert(cmux->n_cells == 0); - tor_assert(cmux->destroy_cell_queue.n == 0); - } - - return circ; -} - -/** - * Notify the circuitmux that cells have been sent on a circuit; this - * is called from channel.c. - */ - -void -circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, - unsigned int n_cells) -{ - chanid_circid_muxinfo_t *hashent = NULL; - int becomes_inactive = 0; - - tor_assert(cmux); - tor_assert(circ); - circuitmux_assert_okay_paranoid(cmux); - - if (n_cells == 0) return; - - /* - * To handle this, we have to: - * - * 1.) Adjust the circuit's cell counter in the cmux hash table - * 2.) Move the circuit to the tail of the active_circuits linked list - * for this cmux, or make the circuit inactive if the cell count - * went to zero. - * 3.) Call cmux->policy->notify_xmit_cells(), if any - */ - - /* Find the hash entry */ - hashent = circuitmux_find_map_entry(cmux, circ); - /* Assert that we found one */ - tor_assert(hashent); - - /* Adjust the cell counter and assert that we had that many cells to send */ - tor_assert(n_cells <= hashent->muxinfo.cell_count); - hashent->muxinfo.cell_count -= n_cells; - /* Do we need to make the circuit inactive? */ - if (hashent->muxinfo.cell_count == 0) becomes_inactive = 1; - /* Adjust the mux cell counter */ - cmux->n_cells -= n_cells; - - /* If we aren't making it inactive later, move it to the tail of the list */ - if (!becomes_inactive) { - circuitmux_move_active_circ_to_tail(cmux, circ, - hashent->muxinfo.direction); - } - - /* - * We call notify_xmit_cells() before making the circuit inactive if needed, - * so the policy can always count on this coming in on an active circuit. - */ - if (cmux->policy && cmux->policy->notify_xmit_cells) { - cmux->policy->notify_xmit_cells(cmux, cmux->policy_data, circ, - hashent->muxinfo.policy_data, - n_cells); - } - - /* - * Now make the circuit inactive if needed; this will call the policy's - * notify_circ_inactive() if present. - */ - if (becomes_inactive) { - --(cmux->n_active_circuits); - circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); - } - - circuitmux_assert_okay_paranoid(cmux); -} - -/** - * Notify the circuitmux that a destroy was sent, so we can update - * the counter. - */ - -void -circuitmux_notify_xmit_destroy(circuitmux_t *cmux) -{ - tor_assert(cmux); - - --(cmux->destroy_ctr); - --(global_destroy_ctr); - log_debug(LD_CIRC, - "Cmux at %p sent a destroy, cmux counter is now "I64_FORMAT", " - "global counter is now "I64_FORMAT, - cmux, - I64_PRINTF_ARG(cmux->destroy_ctr), - I64_PRINTF_ARG(global_destroy_ctr)); -} - -/* - * Circuitmux consistency checking assertions - */ - -/** - * Check that circuitmux data structures are consistent and fail with an - * assert if not. - */ - -void -circuitmux_assert_okay(circuitmux_t *cmux) -{ - tor_assert(cmux); - - /* - * Pass 1: iterate the hash table; for each entry: - * a) Check that the circuit has this cmux for n_mux or p_mux - * b) If the cell_count is > 0, set the mark bit; otherwise clear it - * c) Also check activeness (cell_count > 0 should be active) - * d) Count the number of circuits, active circuits and queued cells - * and at the end check that they match the counters in the cmux. - * - * Pass 2: iterate the active circuits list; for each entry, - * make sure the circuit is attached to this mux and appears - * in the hash table. Make sure the mark bit is 1, and clear - * it in the hash table entry. Consistency-check the linked - * list pointers. - * - * Pass 3: iterate the hash table again; assert if any active circuits - * (mark bit set to 1) are discovered that weren't cleared in pass 2 - * (don't appear in the linked list). - */ - - circuitmux_assert_okay_pass_one(cmux); - circuitmux_assert_okay_pass_two(cmux); - circuitmux_assert_okay_pass_three(cmux); -} - -/** - * Do the first pass of circuitmux_assert_okay(); see the comment in that - * function. - */ - -static void -circuitmux_assert_okay_pass_one(circuitmux_t *cmux) -{ - chanid_circid_muxinfo_t **i = NULL; - uint64_t chan_id; - channel_t *chan; - circid_t circ_id; - circuit_t *circ; - or_circuit_t *or_circ; - unsigned int circ_is_active; - circuit_t **next_p, **prev_p; - unsigned int n_circuits, n_active_circuits, n_cells; - - tor_assert(cmux); - tor_assert(cmux->chanid_circid_map); - - /* Reset the counters */ - n_circuits = n_active_circuits = n_cells = 0; - /* Start iterating the hash table */ - i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); - while (i) { - /* Assert that the hash table entry isn't null */ - tor_assert(*i); - - /* Get the channel and circuit id */ - chan_id = (*i)->chan_id; - circ_id = (*i)->circ_id; - - /* Find the channel and circuit, assert that they exist */ - chan = channel_find_by_global_id(chan_id); - tor_assert(chan); - circ = circuit_get_by_circid_channel_even_if_marked(circ_id, chan); - tor_assert(circ); - /* Clear the circ_is_active bit to start */ - circ_is_active = 0; - - /* Assert that we know which direction this is going */ - tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || - (*i)->muxinfo.direction == CELL_DIRECTION_IN); - - if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { - /* We should be n_mux on this circuit */ - tor_assert(cmux == circ->n_mux); - tor_assert(chan == circ->n_chan); - /* Get next and prev for next test */ - next_p = &(circ->next_active_on_n_chan); - prev_p = &(circ->prev_active_on_n_chan); - } else { - /* This should be an or_circuit_t and we should be p_mux */ - or_circ = TO_OR_CIRCUIT(circ); - tor_assert(cmux == or_circ->p_mux); - tor_assert(chan == or_circ->p_chan); - /* Get next and prev for next test */ - next_p = &(or_circ->next_active_on_p_chan); - prev_p = &(or_circ->prev_active_on_p_chan); - } - - /* - * Should this circuit be active? I.e., does the mux know about > 0 - * cells on it? - */ - circ_is_active = ((*i)->muxinfo.cell_count > 0); - - /* It should be in the linked list iff it's active */ - if (circ_is_active) { - /* Either we have a next link or we are the tail */ - tor_assert(*next_p || (circ == cmux->active_circuits_tail)); - /* Either we have a prev link or we are the head */ - tor_assert(*prev_p || (circ == cmux->active_circuits_head)); - /* Increment the active circuits counter */ - ++n_active_circuits; - } else { - /* Shouldn't be in list, so no next or prev link */ - tor_assert(!(*next_p)); - tor_assert(!(*prev_p)); - /* And can't be head or tail */ - tor_assert(circ != cmux->active_circuits_head); - tor_assert(circ != cmux->active_circuits_tail); - } - - /* Increment the circuits counter */ - ++n_circuits; - /* Adjust the cell counter */ - n_cells += (*i)->muxinfo.cell_count; - - /* Set the mark bit to circ_is_active */ - (*i)->muxinfo.mark = circ_is_active; - - /* Advance to the next entry */ - i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); - } - - /* Now check the counters */ - tor_assert(n_cells == cmux->n_cells); - tor_assert(n_circuits == cmux->n_circuits); - tor_assert(n_active_circuits == cmux->n_active_circuits); -} - -/** - * Do the second pass of circuitmux_assert_okay(); see the comment in that - * function. - */ - -static void -circuitmux_assert_okay_pass_two(circuitmux_t *cmux) -{ - circuit_t *curr_circ, *prev_circ = NULL, *next_circ; - or_circuit_t *curr_or_circ; - uint64_t curr_chan_id; - circid_t curr_circ_id; - circuit_t **next_p, **prev_p; - channel_t *chan; - unsigned int n_active_circuits = 0; - cell_direction_t direction; - chanid_circid_muxinfo_t search, *hashent = NULL; - - tor_assert(cmux); - tor_assert(cmux->chanid_circid_map); - - /* - * Walk the linked list of active circuits in cmux; keep track of the - * previous circuit seen for consistency checking purposes. Count them - * to make sure the number in the linked list matches - * cmux->n_active_circuits. - */ - curr_circ = cmux->active_circuits_head; - while (curr_circ) { - /* Reset some things */ - chan = NULL; - curr_or_circ = NULL; - next_circ = NULL; - next_p = prev_p = NULL; - direction = 0; - - /* Figure out if this is n_mux or p_mux */ - if (cmux == curr_circ->n_mux) { - /* Get next_p and prev_p */ - next_p = &(curr_circ->next_active_on_n_chan); - prev_p = &(curr_circ->prev_active_on_n_chan); - /* Get the channel */ - chan = curr_circ->n_chan; - /* Get the circuit id */ - curr_circ_id = curr_circ->n_circ_id; - /* Remember the direction */ - direction = CELL_DIRECTION_OUT; - } else { - /* We must be p_mux and this must be an or_circuit_t */ - curr_or_circ = TO_OR_CIRCUIT(curr_circ); - tor_assert(cmux == curr_or_circ->p_mux); - /* Get next_p and prev_p */ - next_p = &(curr_or_circ->next_active_on_p_chan); - prev_p = &(curr_or_circ->prev_active_on_p_chan); - /* Get the channel */ - chan = curr_or_circ->p_chan; - /* Get the circuit id */ - curr_circ_id = curr_or_circ->p_circ_id; - /* Remember the direction */ - direction = CELL_DIRECTION_IN; - } - - /* Assert that we got a channel and get the channel ID */ - tor_assert(chan); - curr_chan_id = chan->global_identifier; - - /* Assert that prev_p points to last circuit we saw */ - tor_assert(*prev_p == prev_circ); - /* If that's NULL, assert that we are the head */ - if (!(*prev_p)) tor_assert(curr_circ == cmux->active_circuits_head); - - /* Get the next circuit */ - next_circ = *next_p; - /* If it's NULL, assert that we are the tail */ - if (!(*next_p)) tor_assert(curr_circ == cmux->active_circuits_tail); - - /* Now find the hash table entry for this circuit */ - search.chan_id = curr_chan_id; - search.circ_id = curr_circ_id; - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - - /* Assert that we have one */ - tor_assert(hashent); - - /* Assert that the direction matches */ - tor_assert(direction == hashent->muxinfo.direction); - - /* Assert that the hash entry got marked in pass one */ - tor_assert(hashent->muxinfo.mark); - - /* Clear the mark */ - hashent->muxinfo.mark = 0; - - /* Increment the counter */ - ++n_active_circuits; - - /* Advance to the next active circuit and update prev_circ */ - prev_circ = curr_circ; - curr_circ = next_circ; - } - - /* Assert that the counter matches the cmux */ - tor_assert(n_active_circuits == cmux->n_active_circuits); -} - -/** - * Do the third pass of circuitmux_assert_okay(); see the comment in that - * function. - */ - -static void -circuitmux_assert_okay_pass_three(circuitmux_t *cmux) -{ - chanid_circid_muxinfo_t **i = NULL; - - tor_assert(cmux); - tor_assert(cmux->chanid_circid_map); - - /* Start iterating the hash table */ - i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); - - /* Advance through each entry */ - while (i) { - /* Assert that it isn't null */ - tor_assert(*i); - - /* - * Assert that this entry is not marked - i.e., that either we didn't - * think it should be active in pass one or we saw it in the active - * circuits linked list. - */ - tor_assert(!((*i)->muxinfo.mark)); - - /* Advance to the next entry */ - i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); - } -} - -/*DOCDOC */ -void -circuitmux_append_destroy_cell(channel_t *chan, - circuitmux_t *cmux, - circid_t circ_id, - uint8_t reason) -{ - cell_t cell; - memset(&cell, 0, sizeof(cell_t)); - cell.circ_id = circ_id; - cell.command = CELL_DESTROY; - cell.payload[0] = (uint8_t) reason; - - cell_queue_append_packed_copy(NULL, &cmux->destroy_cell_queue, 0, &cell, - chan->wide_circ_ids, 0); - - /* Destroy entering the queue, update counters */ - ++(cmux->destroy_ctr); - ++global_destroy_ctr; - log_debug(LD_CIRC, - "Cmux at %p queued a destroy for circ %u, cmux counter is now " - I64_FORMAT", global counter is now "I64_FORMAT, - cmux, circ_id, - I64_PRINTF_ARG(cmux->destroy_ctr), - I64_PRINTF_ARG(global_destroy_ctr)); - - /* XXXX Duplicate code from append_cell_to_circuit_queue */ - if (!channel_has_queued_writes(chan)) { - /* There is no data at all waiting to be sent on the outbuf. Add a - * cell, so that we can notice when it gets flushed, flushed_some can - * get called, and we can start putting more data onto the buffer then. - */ - log_debug(LD_GENERAL, "Primed a buffer."); - channel_flush_from_first_active_circuit(chan, 1); - } -} - diff --git a/src/tor/circuitmux.h b/src/tor/circuitmux.h deleted file mode 100644 index ee2f5d1..0000000 --- a/src/tor/circuitmux.h +++ /dev/null @@ -1,147 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitmux.h - * \brief Header file for circuitmux.c - **/ - -#ifndef TOR_CIRCUITMUX_H -#define TOR_CIRCUITMUX_H - -#include "or.h" -#include "testsupport.h" - -typedef struct circuitmux_policy_s circuitmux_policy_t; -typedef struct circuitmux_policy_data_s circuitmux_policy_data_t; -typedef struct circuitmux_policy_circ_data_s circuitmux_policy_circ_data_t; - -struct circuitmux_policy_s { - /* Allocate cmux-wide policy-specific data */ - circuitmux_policy_data_t * (*alloc_cmux_data)(circuitmux_t *cmux); - /* Free cmux-wide policy-specific data */ - void (*free_cmux_data)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data); - /* Allocate circuit policy-specific data for a newly attached circuit */ - circuitmux_policy_circ_data_t * - (*alloc_circ_data)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - cell_direction_t direction, - unsigned int cell_count); - /* Free circuit policy-specific data */ - void (*free_circ_data)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); - /* Notify that a circuit has become active/inactive */ - void (*notify_circ_active)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); - void (*notify_circ_inactive)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); - /* Notify of arriving/transmitted cells on a circuit */ - void (*notify_set_n_cells)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data, - unsigned int n_cells); - void (*notify_xmit_cells)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data, - unsigned int n_cells); - /* Choose a circuit */ - circuit_t * (*pick_active_circuit)(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data); -}; - -/* - * Circuitmux policy implementations can subclass this to store circuitmux- - * wide data; it just has the magic number in the base struct. - */ - -struct circuitmux_policy_data_s { - uint32_t magic; -}; - -/* - * Circuitmux policy implementations can subclass this to store circuit- - * specific data; it just has the magic number in the base struct. - */ - -struct circuitmux_policy_circ_data_s { - uint32_t magic; -}; - -/* - * Upcast #defines for the above types - */ - -/** - * Convert a circuitmux_policy_data_t subtype to a circuitmux_policy_data_t. - */ - -#define TO_CMUX_POL_DATA(x) (&((x)->base_)) - -/** - * Convert a circuitmux_policy_circ_data_t subtype to a - * circuitmux_policy_circ_data_t. - */ - -#define TO_CMUX_POL_CIRC_DATA(x) (&((x)->base_)) - -/* Consistency check */ -void circuitmux_assert_okay(circuitmux_t *cmux); - -/* Create/destroy */ -circuitmux_t * circuitmux_alloc(void); -void circuitmux_detach_all_circuits(circuitmux_t *cmux); -void circuitmux_free(circuitmux_t *cmux); - -/* Policy control */ -void circuitmux_clear_policy(circuitmux_t *cmux); -const circuitmux_policy_t * circuitmux_get_policy(circuitmux_t *cmux); -void circuitmux_set_policy(circuitmux_t *cmux, - const circuitmux_policy_t *pol); - -/* Status inquiries */ -cell_direction_t circuitmux_attached_circuit_direction( - circuitmux_t *cmux, - circuit_t *circ); -int circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ); -int circuitmux_is_circuit_active(circuitmux_t *cmux, circuit_t *circ); -unsigned int circuitmux_num_cells_for_circuit(circuitmux_t *cmux, - circuit_t *circ); -unsigned int circuitmux_num_cells(circuitmux_t *cmux); -unsigned int circuitmux_num_circuits(circuitmux_t *cmux); -unsigned int circuitmux_num_active_circuits(circuitmux_t *cmux); - -/* Channel interface */ -circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux, - cell_queue_t **destroy_queue_out); -void circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, - unsigned int n_cells); -void circuitmux_notify_xmit_destroy(circuitmux_t *cmux); - -/* Circuit interface */ -MOCK_DECL(void, circuitmux_attach_circuit, (circuitmux_t *cmux, - circuit_t *circ, - cell_direction_t direction)); -MOCK_DECL(void, circuitmux_detach_circuit, - (circuitmux_t *cmux, circuit_t *circ)); -void circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ); -void circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, - unsigned int n_cells); - -void circuitmux_append_destroy_cell(channel_t *chan, - circuitmux_t *cmux, circid_t circ_id, - uint8_t reason); -void circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, - channel_t *chan); - -#endif /* TOR_CIRCUITMUX_H */ - diff --git a/src/tor/circuitmux_ewma.c b/src/tor/circuitmux_ewma.c deleted file mode 100644 index 3f37d7b..0000000 --- a/src/tor/circuitmux_ewma.c +++ /dev/null @@ -1,684 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitmux_ewma.c - * \brief EWMA circuit selection as a circuitmux_t policy - **/ - -#define TOR_CIRCUITMUX_EWMA_C_ - -#include - -#include "or.h" -#include "circuitmux.h" -#include "circuitmux_ewma.h" -#include "networkstatus.h" - -/*** EWMA parameter #defines ***/ - -/** How long does a tick last (seconds)? */ -#define EWMA_TICK_LEN 10 - -/** The default per-tick scale factor, if it hasn't been overridden by a - * consensus or a configuration setting. zero means "disabled". */ -#define EWMA_DEFAULT_HALFLIFE 0.0 - -/*** Some useful constant #defines ***/ - -/*DOCDOC*/ -#define EPSILON 0.00001 -/*DOCDOC*/ -#define LOG_ONEHALF -0.69314718055994529 - -/*** EWMA structures ***/ - -typedef struct cell_ewma_s cell_ewma_t; -typedef struct ewma_policy_data_s ewma_policy_data_t; -typedef struct ewma_policy_circ_data_s ewma_policy_circ_data_t; - -/** - * The cell_ewma_t structure keeps track of how many cells a circuit has - * transferred recently. It keeps an EWMA (exponentially weighted moving - * average) of the number of cells flushed from the circuit queue onto a - * connection in channel_flush_from_first_active_circuit(). - */ - -struct cell_ewma_s { - /** The last 'tick' at which we recalibrated cell_count. - * - * A cell sent at exactly the start of this tick has weight 1.0. Cells sent - * since the start of this tick have weight greater than 1.0; ones sent - * earlier have less weight. */ - unsigned int last_adjusted_tick; - /** The EWMA of the cell count. */ - double cell_count; - /** True iff this is the cell count for a circuit's previous - * channel. */ - unsigned int is_for_p_chan : 1; - /** The position of the circuit within the OR connection's priority - * queue. */ - int heap_index; -}; - -struct ewma_policy_data_s { - circuitmux_policy_data_t base_; - - /** - * Priority queue of cell_ewma_t for circuits with queued cells waiting - * for room to free up on the channel that owns this circuitmux. Kept - * in heap order according to EWMA. This was formerly in channel_t, and - * in or_connection_t before that. - */ - smartlist_t *active_circuit_pqueue; - - /** - * The tick on which the cell_ewma_ts in active_circuit_pqueue last had - * their ewma values rescaled. This was formerly in channel_t, and in - * or_connection_t before that. - */ - unsigned int active_circuit_pqueue_last_recalibrated; -}; - -struct ewma_policy_circ_data_s { - circuitmux_policy_circ_data_t base_; - - /** - * The EWMA count for the number of cells flushed from this circuit - * onto this circuitmux. Used to determine which circuit to flush - * from next. This was formerly in circuit_t and or_circuit_t. - */ - cell_ewma_t cell_ewma; - - /** - * Pointer back to the circuit_t this is for; since we're separating - * out circuit selection policy like this, we can't attach cell_ewma_t - * to the circuit_t any more, so we can't use SUBTYPE_P directly to a - * circuit_t like before; instead get it here. - */ - circuit_t *circ; -}; - -#define EWMA_POL_DATA_MAGIC 0x2fd8b16aU -#define EWMA_POL_CIRC_DATA_MAGIC 0x761e7747U - -/*** Downcasts for the above types ***/ - -static ewma_policy_data_t * -TO_EWMA_POL_DATA(circuitmux_policy_data_t *); - -static ewma_policy_circ_data_t * -TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *); - -/** - * Downcast a circuitmux_policy_data_t to an ewma_policy_data_t and assert - * if the cast is impossible. - */ - -static INLINE ewma_policy_data_t * -TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol) -{ - if (!pol) return NULL; - else { - tor_assert(pol->magic == EWMA_POL_DATA_MAGIC); - return DOWNCAST(ewma_policy_data_t, pol); - } -} - -/** - * Downcast a circuitmux_policy_circ_data_t to an ewma_policy_circ_data_t - * and assert if the cast is impossible. - */ - -static INLINE ewma_policy_circ_data_t * -TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol) -{ - if (!pol) return NULL; - else { - tor_assert(pol->magic == EWMA_POL_CIRC_DATA_MAGIC); - return DOWNCAST(ewma_policy_circ_data_t, pol); - } -} - -/*** Static declarations for circuitmux_ewma.c ***/ - -static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); -static int compare_cell_ewma_counts(const void *p1, const void *p2); -static unsigned cell_ewma_tick_from_timeval(const struct timeval *now, - double *remainder_out); -static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma); -static INLINE double get_scale_factor(unsigned from_tick, unsigned to_tick); -static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol); -static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); -static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick); -static void scale_active_circuits(ewma_policy_data_t *pol, - unsigned cur_tick); - -/*** Circuitmux policy methods ***/ - -static circuitmux_policy_data_t * ewma_alloc_cmux_data(circuitmux_t *cmux); -static void ewma_free_cmux_data(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data); -static circuitmux_policy_circ_data_t * -ewma_alloc_circ_data(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, - circuit_t *circ, cell_direction_t direction, - unsigned int cell_count); -static void -ewma_free_circ_data(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); -static void -ewma_notify_circ_active(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); -static void -ewma_notify_circ_inactive(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data); -static void -ewma_notify_xmit_cells(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data, - unsigned int n_cells); -static circuit_t * -ewma_pick_active_circuit(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data); - -/*** EWMA global variables ***/ - -/** The per-tick scale factor to be used when computing cell-count EWMA - * values. (A cell sent N ticks before the start of the current tick - * has value ewma_scale_factor ** N.) - */ -static double ewma_scale_factor = 0.1; -/* DOCDOC ewma_enabled */ -static int ewma_enabled = 0; - -/*** EWMA circuitmux_policy_t method table ***/ - -circuitmux_policy_t ewma_policy = { - /*.alloc_cmux_data =*/ ewma_alloc_cmux_data, - /*.free_cmux_data =*/ ewma_free_cmux_data, - /*.alloc_circ_data =*/ ewma_alloc_circ_data, - /*.free_circ_data =*/ ewma_free_circ_data, - /*.notify_circ_active =*/ ewma_notify_circ_active, - /*.notify_circ_inactive =*/ ewma_notify_circ_inactive, - /*.notify_set_n_cells =*/ NULL, /* EWMA doesn't need this */ - /*.notify_xmit_cells =*/ ewma_notify_xmit_cells, - /*.pick_active_circuit =*/ ewma_pick_active_circuit -}; - -/*** EWMA method implementations using the below EWMA helper functions ***/ - -/** - * Allocate an ewma_policy_data_t and upcast it to a circuitmux_policy_data_t; - * this is called when setting the policy on a circuitmux_t to ewma_policy. - */ - -static circuitmux_policy_data_t * -ewma_alloc_cmux_data(circuitmux_t *cmux) -{ - ewma_policy_data_t *pol = NULL; - - tor_assert(cmux); - - pol = tor_malloc_zero(sizeof(*pol)); - pol->base_.magic = EWMA_POL_DATA_MAGIC; - pol->active_circuit_pqueue = smartlist_new(); - pol->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); - - return TO_CMUX_POL_DATA(pol); -} - -/** - * Free an ewma_policy_data_t allocated with ewma_alloc_cmux_data() - */ - -static void -ewma_free_cmux_data(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data) -{ - ewma_policy_data_t *pol = NULL; - - tor_assert(cmux); - if (!pol_data) return; - - pol = TO_EWMA_POL_DATA(pol_data); - - smartlist_free(pol->active_circuit_pqueue); - tor_free(pol); -} - -/** - * Allocate an ewma_policy_circ_data_t and upcast it to a - * circuitmux_policy_data_t; this is called when attaching a circuit to a - * circuitmux_t with ewma_policy. - */ - -static circuitmux_policy_circ_data_t * -ewma_alloc_circ_data(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - cell_direction_t direction, - unsigned int cell_count) -{ - ewma_policy_circ_data_t *cdata = NULL; - - tor_assert(cmux); - tor_assert(pol_data); - tor_assert(circ); - tor_assert(direction == CELL_DIRECTION_OUT || - direction == CELL_DIRECTION_IN); - /* Shut the compiler up */ - tor_assert(cell_count == cell_count); - - cdata = tor_malloc_zero(sizeof(*cdata)); - cdata->base_.magic = EWMA_POL_CIRC_DATA_MAGIC; - cdata->circ = circ; - - /* - * Initialize the cell_ewma_t structure (formerly in - * init_circuit_base()) - */ - cdata->cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - cdata->cell_ewma.cell_count = 0.0; - cdata->cell_ewma.heap_index = -1; - if (direction == CELL_DIRECTION_IN) { - cdata->cell_ewma.is_for_p_chan = 1; - } else { - cdata->cell_ewma.is_for_p_chan = 0; - } - - return TO_CMUX_POL_CIRC_DATA(cdata); -} - -/** - * Free an ewma_policy_circ_data_t allocated with ewma_alloc_circ_data() - */ - -static void -ewma_free_circ_data(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data) - -{ - ewma_policy_circ_data_t *cdata = NULL; - - tor_assert(cmux); - tor_assert(circ); - tor_assert(pol_data); - - if (!pol_circ_data) return; - - cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); - - tor_free(cdata); -} - -/** - * Handle circuit activation; this inserts the circuit's cell_ewma into - * the active_circuits_pqueue. - */ - -static void -ewma_notify_circ_active(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data) -{ - ewma_policy_data_t *pol = NULL; - ewma_policy_circ_data_t *cdata = NULL; - - tor_assert(cmux); - tor_assert(pol_data); - tor_assert(circ); - tor_assert(pol_circ_data); - - pol = TO_EWMA_POL_DATA(pol_data); - cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); - - add_cell_ewma(pol, &(cdata->cell_ewma)); -} - -/** - * Handle circuit deactivation; this removes the circuit's cell_ewma from - * the active_circuits_pqueue. - */ - -static void -ewma_notify_circ_inactive(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data) -{ - ewma_policy_data_t *pol = NULL; - ewma_policy_circ_data_t *cdata = NULL; - - tor_assert(cmux); - tor_assert(pol_data); - tor_assert(circ); - tor_assert(pol_circ_data); - - pol = TO_EWMA_POL_DATA(pol_data); - cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); - - remove_cell_ewma(pol, &(cdata->cell_ewma)); -} - -/** - * Update cell_ewma for this circuit after we've sent some cells, and - * remove/reinsert it in the queue. This used to be done (brokenly, - * see bug 6816) in channel_flush_from_first_active_circuit(). - */ - -static void -ewma_notify_xmit_cells(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data, - circuit_t *circ, - circuitmux_policy_circ_data_t *pol_circ_data, - unsigned int n_cells) -{ - ewma_policy_data_t *pol = NULL; - ewma_policy_circ_data_t *cdata = NULL; - unsigned int tick; - double fractional_tick, ewma_increment; - /* The current (hi-res) time */ - struct timeval now_hires; - cell_ewma_t *cell_ewma, *tmp; - - tor_assert(cmux); - tor_assert(pol_data); - tor_assert(circ); - tor_assert(pol_circ_data); - tor_assert(n_cells > 0); - - pol = TO_EWMA_POL_DATA(pol_data); - cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); - - /* Rescale the EWMAs if needed */ - tor_gettimeofday_cached(&now_hires); - tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); - - if (tick != pol->active_circuit_pqueue_last_recalibrated) { - scale_active_circuits(pol, tick); - } - - /* How much do we adjust the cell count in cell_ewma by? */ - ewma_increment = - ((double)(n_cells)) * pow(ewma_scale_factor, -fractional_tick); - - /* Do the adjustment */ - cell_ewma = &(cdata->cell_ewma); - cell_ewma->cell_count += ewma_increment; - - /* - * Since we just sent on this circuit, it should be at the head of - * the queue. Pop the head, assert that it matches, then re-add. - */ - tmp = pop_first_cell_ewma(pol); - tor_assert(tmp == cell_ewma); - add_cell_ewma(pol, cell_ewma); -} - -/** - * Pick the preferred circuit to send from; this will be the one with - * the lowest EWMA value in the priority queue. This used to be done - * in channel_flush_from_first_active_circuit(). - */ - -static circuit_t * -ewma_pick_active_circuit(circuitmux_t *cmux, - circuitmux_policy_data_t *pol_data) -{ - ewma_policy_data_t *pol = NULL; - circuit_t *circ = NULL; - cell_ewma_t *cell_ewma = NULL; - - tor_assert(cmux); - tor_assert(pol_data); - - pol = TO_EWMA_POL_DATA(pol_data); - - if (smartlist_len(pol->active_circuit_pqueue) > 0) { - /* Get the head of the queue */ - cell_ewma = smartlist_get(pol->active_circuit_pqueue, 0); - circ = cell_ewma_to_circuit(cell_ewma); - } - - return circ; -} - -/** Helper for sorting cell_ewma_t values in their priority queue. */ -static int -compare_cell_ewma_counts(const void *p1, const void *p2) -{ - const cell_ewma_t *e1 = p1, *e2 = p2; - - if (e1->cell_count < e2->cell_count) - return -1; - else if (e1->cell_count > e2->cell_count) - return 1; - else - return 0; -} - -/** Given a cell_ewma_t, return a pointer to the circuit containing it. */ -static circuit_t * -cell_ewma_to_circuit(cell_ewma_t *ewma) -{ - ewma_policy_circ_data_t *cdata = NULL; - - tor_assert(ewma); - cdata = SUBTYPE_P(ewma, ewma_policy_circ_data_t, cell_ewma); - tor_assert(cdata); - - return cdata->circ; -} - -/* ==== Functions for scaling cell_ewma_t ==== - - When choosing which cells to relay first, we favor circuits that have been - quiet recently. This gives better latency on connections that aren't - pushing lots of data, and makes the network feel more interactive. - - Conceptually, we take an exponentially weighted mean average of the number - of cells a circuit has sent, and allow active circuits (those with cells to - relay) to send cells in reverse order of their exponentially-weighted mean - average (EWMA) cell count. [That is, a cell sent N seconds ago 'counts' - F^N times as much as a cell sent now, for 0now, compute the cell_ewma tick in which it occurs - * and the fraction of the tick that has elapsed between the start of the tick - * and now. Return the former and store the latter in - * *remainder_out. - * - * These tick values are not meant to be shared between Tor instances, or used - * for other purposes. */ - -static unsigned -cell_ewma_tick_from_timeval(const struct timeval *now, - double *remainder_out) -{ - unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN); - /* rem */ - double rem = (now->tv_sec % EWMA_TICK_LEN) + - ((double)(now->tv_usec)) / 1.0e6; - *remainder_out = rem / EWMA_TICK_LEN; - return res; -} - -/** Tell the caller whether ewma_enabled is set */ -int -cell_ewma_enabled(void) -{ - return ewma_enabled; -} - -/** Compute and return the current cell_ewma tick. */ -unsigned int -cell_ewma_get_tick(void) -{ - return ((unsigned)approx_time() / EWMA_TICK_LEN); -} - -/** Adjust the global cell scale factor based on options */ -void -cell_ewma_set_scale_factor(const or_options_t *options, - const networkstatus_t *consensus) -{ - int32_t halflife_ms; - double halflife; - const char *source; - if (options && options->CircuitPriorityHalflife >= -EPSILON) { - halflife = options->CircuitPriorityHalflife; - source = "CircuitPriorityHalflife in configuration"; - } else if (consensus && (halflife_ms = networkstatus_get_param( - consensus, "CircuitPriorityHalflifeMsec", - -1, -1, INT32_MAX)) >= 0) { - halflife = ((double)halflife_ms)/1000.0; - source = "CircuitPriorityHalflifeMsec in consensus"; - } else { - halflife = EWMA_DEFAULT_HALFLIFE; - source = "Default value"; - } - - if (halflife <= EPSILON) { - /* The cell EWMA algorithm is disabled. */ - ewma_scale_factor = 0.1; - ewma_enabled = 0; - log_info(LD_OR, - "Disabled cell_ewma algorithm because of value in %s", - source); - } else { - /* convert halflife into halflife-per-tick. */ - halflife /= EWMA_TICK_LEN; - /* compute per-tick scale factor. */ - ewma_scale_factor = exp( LOG_ONEHALF / halflife ); - ewma_enabled = 1; - log_info(LD_OR, - "Enabled cell_ewma algorithm because of value in %s; " - "scale factor is %f per %d seconds", - source, ewma_scale_factor, EWMA_TICK_LEN); - } -} - -/** Return the multiplier necessary to convert the value of a cell sent in - * 'from_tick' to one sent in 'to_tick'. */ -static INLINE double -get_scale_factor(unsigned from_tick, unsigned to_tick) -{ - /* This math can wrap around, but that's okay: unsigned overflow is - well-defined */ - int diff = (int)(to_tick - from_tick); - return pow(ewma_scale_factor, diff); -} - -/** Adjust the cell count of ewma so that it is scaled with respect to - * cur_tick */ -static void -scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick) -{ - double factor = get_scale_factor(ewma->last_adjusted_tick, cur_tick); - ewma->cell_count *= factor; - ewma->last_adjusted_tick = cur_tick; -} - -/** Adjust the cell count of every active circuit on chan so - * that they are scaled with respect to cur_tick */ -static void -scale_active_circuits(ewma_policy_data_t *pol, unsigned cur_tick) -{ - double factor; - - tor_assert(pol); - tor_assert(pol->active_circuit_pqueue); - - factor = - get_scale_factor( - pol->active_circuit_pqueue_last_recalibrated, - cur_tick); - /** Ordinarily it isn't okay to change the value of an element in a heap, - * but it's okay here, since we are preserving the order. */ - SMARTLIST_FOREACH_BEGIN( - pol->active_circuit_pqueue, - cell_ewma_t *, e) { - tor_assert(e->last_adjusted_tick == - pol->active_circuit_pqueue_last_recalibrated); - e->cell_count *= factor; - e->last_adjusted_tick = cur_tick; - } SMARTLIST_FOREACH_END(e); - pol->active_circuit_pqueue_last_recalibrated = cur_tick; -} - -/** Rescale ewma to the same scale as pol, and add it to - * pol's priority queue of active circuits */ -static void -add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma) -{ - tor_assert(pol); - tor_assert(pol->active_circuit_pqueue); - tor_assert(ewma); - tor_assert(ewma->heap_index == -1); - - scale_single_cell_ewma( - ewma, - pol->active_circuit_pqueue_last_recalibrated); - - smartlist_pqueue_add(pol->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index), - ewma); -} - -/** Remove ewma from pol's priority queue of active circuits */ -static void -remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma) -{ - tor_assert(pol); - tor_assert(pol->active_circuit_pqueue); - tor_assert(ewma); - tor_assert(ewma->heap_index != -1); - - smartlist_pqueue_remove(pol->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index), - ewma); -} - -/** Remove and return the first cell_ewma_t from pol's priority queue of - * active circuits. Requires that the priority queue is nonempty. */ -static cell_ewma_t * -pop_first_cell_ewma(ewma_policy_data_t *pol) -{ - tor_assert(pol); - tor_assert(pol->active_circuit_pqueue); - - return smartlist_pqueue_pop(pol->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index)); -} - diff --git a/src/tor/circuitmux_ewma.h b/src/tor/circuitmux_ewma.h deleted file mode 100644 index a512745..0000000 --- a/src/tor/circuitmux_ewma.h +++ /dev/null @@ -1,29 +0,0 @@ -/* * Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitmux_ewma.h - * \brief Header file for circuitmux_ewma.c - **/ - -#ifndef TOR_CIRCUITMUX_EWMA_H -#define TOR_CIRCUITMUX_EWMA_H - -#include "or.h" -#include "circuitmux.h" - -/* Everything but circuitmux_ewma.c should see this extern */ -#ifndef TOR_CIRCUITMUX_EWMA_C_ - -extern circuitmux_policy_t ewma_policy; - -#endif /* !(TOR_CIRCUITMUX_EWMA_C_) */ - -/* Externally visible EWMA functions */ -int cell_ewma_enabled(void); -unsigned int cell_ewma_get_tick(void); -void cell_ewma_set_scale_factor(const or_options_t *options, - const networkstatus_t *consensus); - -#endif /* TOR_CIRCUITMUX_EWMA_H */ - diff --git a/src/tor/circuitstats.c b/src/tor/circuitstats.c deleted file mode 100644 index eaefc9e..0000000 --- a/src/tor/circuitstats.c +++ /dev/null @@ -1,1644 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#define CIRCUITSTATS_PRIVATE - -#include "or.h" -#include "circuitbuild.h" -#include "circuitstats.h" -#include "config.h" -#include "confparse.h" -#include "control.h" -#include "networkstatus.h" -#include "statefile.h" - -#undef log -#include - -static void cbt_control_event_buildtimeout_set( - const circuit_build_times_t *cbt, - buildtimeout_set_event_t type); - -#define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) - -/** Global list of circuit build times */ -// XXXX: Add this as a member for entry_guard_t instead of global? -// Then we could do per-guard statistics, as guards are likely to -// vary in their own latency. The downside of this is that guards -// can change frequently, so we'd be building a lot more circuits -// most likely. -static circuit_build_times_t circ_times; - -#ifdef TOR_UNIT_TESTS -/** If set, we're running the unit tests: we should avoid clobbering - * our state file or accessing get_options() or get_or_state() */ -static int unit_tests = 0; -#else -#define unit_tests 0 -#endif - -/** Return a pointer to the data structure describing our current circuit - * build time history and computations. */ -const circuit_build_times_t * -get_circuit_build_times(void) -{ - return &circ_times; -} - -/** As get_circuit_build_times, but return a mutable pointer. */ -circuit_build_times_t * -get_circuit_build_times_mutable(void) -{ - return &circ_times; -} - -/** Return the time to wait before actually closing an under-construction, in - * milliseconds. */ -double -get_circuit_build_close_time_ms(void) -{ - return circ_times.close_ms; -} - -/** Return the time to wait before giving up on an under-construction circuit, - * in milliseconds. */ -double -get_circuit_build_timeout_ms(void) -{ - return circ_times.timeout_ms; -} - -/** - * This function decides if CBT learning should be disabled. It returns - * true if one or more of the following four conditions are met: - * - * 1. If the cbtdisabled consensus parameter is set. - * 2. If the torrc option LearnCircuitBuildTimeout is false. - * 3. If we are a directory authority - * 4. If we fail to write circuit build time history to our state file. - */ -int -circuit_build_times_disabled(void) -{ - if (unit_tests) { - return 0; - } else { - int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled", - 0, 0, 1); - int config_disabled = !get_options()->LearnCircuitBuildTimeout; - int dirauth_disabled = get_options()->AuthoritativeDir; - int state_disabled = did_last_state_file_write_fail() ? 1 : 0; - - if (consensus_disabled || config_disabled || dirauth_disabled || - state_disabled) { - log_debug(LD_CIRC, - "CircuitBuildTime learning is disabled. " - "Consensus=%d, Config=%d, AuthDir=%d, StateFile=%d", - consensus_disabled, config_disabled, dirauth_disabled, - state_disabled); - return 1; - } else { - log_debug(LD_CIRC, - "CircuitBuildTime learning is not disabled. " - "Consensus=%d, Config=%d, AuthDir=%d, StateFile=%d", - consensus_disabled, config_disabled, dirauth_disabled, - state_disabled); - return 0; - } - } -} - -/** - * Retrieve and bounds-check the cbtmaxtimeouts consensus paramter. - * - * Effect: When this many timeouts happen in the last 'cbtrecentcount' - * circuit attempts, the client should discard all of its history and - * begin learning a fresh timeout value. - */ -static int32_t -circuit_build_times_max_timeouts(void) -{ - int32_t cbt_maxtimeouts; - - cbt_maxtimeouts = networkstatus_get_param(NULL, "cbtmaxtimeouts", - CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT, - CBT_MIN_MAX_RECENT_TIMEOUT_COUNT, - CBT_MAX_MAX_RECENT_TIMEOUT_COUNT); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_max_timeouts() called, cbtmaxtimeouts is" - " %d", - cbt_maxtimeouts); - } - - return cbt_maxtimeouts; -} - -/** - * Retrieve and bounds-check the cbtnummodes consensus paramter. - * - * Effect: This value governs how many modes to use in the weighted - * average calculation of Pareto parameter Xm. A value of 3 introduces - * some bias (2-5% of CDF) under ideal conditions, but allows for better - * performance in the event that a client chooses guard nodes of radically - * different performance characteristics. - */ -static int32_t -circuit_build_times_default_num_xm_modes(void) -{ - int32_t num = networkstatus_get_param(NULL, "cbtnummodes", - CBT_DEFAULT_NUM_XM_MODES, - CBT_MIN_NUM_XM_MODES, - CBT_MAX_NUM_XM_MODES); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_default_num_xm_modes() called, cbtnummodes" - " is %d", - num); - } - - return num; -} - -/** - * Retrieve and bounds-check the cbtmincircs consensus paramter. - * - * Effect: This is the minimum number of circuits to build before - * computing a timeout. - */ -static int32_t -circuit_build_times_min_circs_to_observe(void) -{ - int32_t num = networkstatus_get_param(NULL, "cbtmincircs", - CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE, - CBT_MIN_MIN_CIRCUITS_TO_OBSERVE, - CBT_MAX_MIN_CIRCUITS_TO_OBSERVE); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_min_circs_to_observe() called, cbtmincircs" - " is %d", - num); - } - - return num; -} - -/** Return true iff cbt has recorded enough build times that we - * want to start acting on the timeout it implies. */ -int -circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt) -{ - return cbt->total_build_times >= circuit_build_times_min_circs_to_observe(); -} - -/** - * Retrieve and bounds-check the cbtquantile consensus paramter. - * - * Effect: This is the position on the quantile curve to use to set the - * timeout value. It is a percent (10-99). - */ -double -circuit_build_times_quantile_cutoff(void) -{ - int32_t num = networkstatus_get_param(NULL, "cbtquantile", - CBT_DEFAULT_QUANTILE_CUTOFF, - CBT_MIN_QUANTILE_CUTOFF, - CBT_MAX_QUANTILE_CUTOFF); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_quantile_cutoff() called, cbtquantile" - " is %d", - num); - } - - return num/100.0; -} - -/** - * Retrieve and bounds-check the cbtclosequantile consensus paramter. - * - * Effect: This is the position on the quantile curve to use to set the - * timeout value to use to actually close circuits. It is a percent - * (0-99). - */ -static double -circuit_build_times_close_quantile(void) -{ - int32_t param; - /* Cast is safe - circuit_build_times_quantile_cutoff() is capped */ - int32_t min = (int)tor_lround(100*circuit_build_times_quantile_cutoff()); - param = networkstatus_get_param(NULL, "cbtclosequantile", - CBT_DEFAULT_CLOSE_QUANTILE, - CBT_MIN_CLOSE_QUANTILE, - CBT_MAX_CLOSE_QUANTILE); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_close_quantile() called, cbtclosequantile" - " is %d", param); - } - - if (param < min) { - log_warn(LD_DIR, "Consensus parameter cbtclosequantile is " - "too small, raising to %d", min); - param = min; - } - return param / 100.0; -} - -/** - * Retrieve and bounds-check the cbttestfreq consensus paramter. - * - * Effect: Describes how often in seconds to build a test circuit to - * gather timeout values. Only applies if less than 'cbtmincircs' - * have been recorded. - */ -static int32_t -circuit_build_times_test_frequency(void) -{ - int32_t num = networkstatus_get_param(NULL, "cbttestfreq", - CBT_DEFAULT_TEST_FREQUENCY, - CBT_MIN_TEST_FREQUENCY, - CBT_MAX_TEST_FREQUENCY); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_test_frequency() called, cbttestfreq is %d", - num); - } - - return num; -} - -/** - * Retrieve and bounds-check the cbtmintimeout consensus parameter. - * - * Effect: This is the minimum allowed timeout value in milliseconds. - * The minimum is to prevent rounding to 0 (we only check once - * per second). - */ -static int32_t -circuit_build_times_min_timeout(void) -{ - int32_t num = networkstatus_get_param(NULL, "cbtmintimeout", - CBT_DEFAULT_TIMEOUT_MIN_VALUE, - CBT_MIN_TIMEOUT_MIN_VALUE, - CBT_MAX_TIMEOUT_MIN_VALUE); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_min_timeout() called, cbtmintimeout is %d", - num); - } - - return num; -} - -/** - * Retrieve and bounds-check the cbtinitialtimeout consensus paramter. - * - * Effect: This is the timeout value to use before computing a timeout, - * in milliseconds. - */ -int32_t -circuit_build_times_initial_timeout(void) -{ - int32_t min = circuit_build_times_min_timeout(); - int32_t param = networkstatus_get_param(NULL, "cbtinitialtimeout", - CBT_DEFAULT_TIMEOUT_INITIAL_VALUE, - CBT_MIN_TIMEOUT_INITIAL_VALUE, - CBT_MAX_TIMEOUT_INITIAL_VALUE); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_initial_timeout() called, " - "cbtinitialtimeout is %d", - param); - } - - if (param < min) { - log_warn(LD_DIR, "Consensus parameter cbtinitialtimeout is too small, " - "raising to %d", min); - param = min; - } - return param; -} - -/** - * Retrieve and bounds-check the cbtrecentcount consensus paramter. - * - * Effect: This is the number of circuit build times to keep track of - * for deciding if we hit cbtmaxtimeouts and need to reset our state - * and learn a new timeout. - */ -static int32_t -circuit_build_times_recent_circuit_count(networkstatus_t *ns) -{ - int32_t num; - num = networkstatus_get_param(ns, "cbtrecentcount", - CBT_DEFAULT_RECENT_CIRCUITS, - CBT_MIN_RECENT_CIRCUITS, - CBT_MAX_RECENT_CIRCUITS); - - if (!(get_options()->LearnCircuitBuildTimeout)) { - log_debug(LD_BUG, - "circuit_build_times_recent_circuit_count() called, " - "cbtrecentcount is %d", - num); - } - - return num; -} - -/** - * This function is called when we get a consensus update. - * - * It checks to see if we have changed any consensus parameters - * that require reallocation or discard of previous stats. - */ -void -circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns) -{ - int32_t num; - - /* - * First check if we're doing adaptive timeouts at all; nothing to - * update if we aren't. - */ - - if (!circuit_build_times_disabled()) { - num = circuit_build_times_recent_circuit_count(ns); - - if (num > 0) { - if (num != cbt->liveness.num_recent_circs) { - int8_t *recent_circs; - log_notice(LD_CIRC, "The Tor Directory Consensus has changed how many " - "circuits we must track to detect network failures from %d " - "to %d.", cbt->liveness.num_recent_circs, num); - - tor_assert(cbt->liveness.timeouts_after_firsthop || - cbt->liveness.num_recent_circs == 0); - - /* - * Technically this is a circular array that we are reallocating - * and memcopying. However, since it only consists of either 1s - * or 0s, and is only used in a statistical test to determine when - * we should discard our history after a sufficient number of 1's - * have been reached, it is fine if order is not preserved or - * elements are lost. - * - * cbtrecentcount should only be changing in cases of severe network - * distress anyway, so memory correctness here is paramount over - * doing acrobatics to preserve the array. - */ - recent_circs = tor_malloc_zero(sizeof(int8_t)*num); - if (cbt->liveness.timeouts_after_firsthop && - cbt->liveness.num_recent_circs > 0) { - memcpy(recent_circs, cbt->liveness.timeouts_after_firsthop, - sizeof(int8_t)*MIN(num, cbt->liveness.num_recent_circs)); - } - - // Adjust the index if it needs it. - if (num < cbt->liveness.num_recent_circs) { - cbt->liveness.after_firsthop_idx = MIN(num-1, - cbt->liveness.after_firsthop_idx); - } - - tor_free(cbt->liveness.timeouts_after_firsthop); - cbt->liveness.timeouts_after_firsthop = recent_circs; - cbt->liveness.num_recent_circs = num; - } - /* else no change, nothing to do */ - } else { /* num == 0 */ - /* - * Weird. This probably shouldn't happen, so log a warning, but try - * to do something sensible anyway. - */ - - log_warn(LD_CIRC, - "The cbtrecentcircs consensus parameter came back zero! " - "This disables adaptive timeouts since we can't keep track of " - "any recent circuits."); - - circuit_build_times_free_timeouts(cbt); - } - } else { - /* - * Adaptive timeouts are disabled; this might be because of the - * LearnCircuitBuildTimes config parameter, and hence permanent, or - * the cbtdisabled consensus parameter, so it may be a new condition. - * Treat it like getting num == 0 above and free the circuit history - * if we have any. - */ - - circuit_build_times_free_timeouts(cbt); - } -} - -/** - * Return the initial default or configured timeout in milliseconds - */ -static double -circuit_build_times_get_initial_timeout(void) -{ - double timeout; - - /* - * Check if we have LearnCircuitBuildTimeout, and if we don't, - * always use CircuitBuildTimeout, no questions asked. - */ - if (!unit_tests && get_options()->CircuitBuildTimeout) { - timeout = get_options()->CircuitBuildTimeout*1000; - if (get_options()->LearnCircuitBuildTimeout && - timeout < circuit_build_times_min_timeout()) { - log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds", - circuit_build_times_min_timeout()/1000); - timeout = circuit_build_times_min_timeout(); - } - } else { - timeout = circuit_build_times_initial_timeout(); - } - - return timeout; -} - -/** - * Reset the build time state. - * - * Leave estimated parameters, timeout and network liveness intact - * for future use. - */ -STATIC void -circuit_build_times_reset(circuit_build_times_t *cbt) -{ - memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times)); - cbt->total_build_times = 0; - cbt->build_times_idx = 0; - cbt->have_computed_timeout = 0; -} - -/** - * Initialize the buildtimes structure for first use. - * - * Sets the initial timeout values based on either the config setting, - * the consensus param, or the default (CBT_DEFAULT_TIMEOUT_INITIAL_VALUE). - */ -void -circuit_build_times_init(circuit_build_times_t *cbt) -{ - memset(cbt, 0, sizeof(*cbt)); - /* - * Check if we really are using adaptive timeouts, and don't keep - * track of this stuff if not. - */ - if (!circuit_build_times_disabled()) { - cbt->liveness.num_recent_circs = - circuit_build_times_recent_circuit_count(NULL); - cbt->liveness.timeouts_after_firsthop = - tor_malloc_zero(sizeof(int8_t)*cbt->liveness.num_recent_circs); - } else { - cbt->liveness.num_recent_circs = 0; - cbt->liveness.timeouts_after_firsthop = NULL; - } - cbt->close_ms = cbt->timeout_ms = circuit_build_times_get_initial_timeout(); - cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET); -} - -/** - * Free the saved timeouts, if the cbtdisabled consensus parameter got turned - * on or something. - */ - -void -circuit_build_times_free_timeouts(circuit_build_times_t *cbt) -{ - if (!cbt) return; - - if (cbt->liveness.timeouts_after_firsthop) { - tor_free(cbt->liveness.timeouts_after_firsthop); - } - - cbt->liveness.num_recent_circs = 0; -} - -#if 0 -/** - * Rewind our build time history by n positions. - */ -static void -circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) -{ - int i = 0; - - cbt->build_times_idx -= n; - cbt->build_times_idx %= CBT_NCIRCUITS_TO_OBSERVE; - - for (i = 0; i < n; i++) { - cbt->circuit_build_times[(i+cbt->build_times_idx) - %CBT_NCIRCUITS_TO_OBSERVE]=0; - } - - if (cbt->total_build_times > n) { - cbt->total_build_times -= n; - } else { - cbt->total_build_times = 0; - } - - log_info(LD_CIRC, - "Rewound history by %d places. Current index: %d. " - "Total: %d", n, cbt->build_times_idx, cbt->total_build_times); -} -#endif - -/** - * Add a new build time value time to the set of build times. Time - * units are milliseconds. - * - * circuit_build_times cbt is a circular array, so loop around when - * array is full. - */ -int -circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time) -{ - if (time <= 0 || time > CBT_BUILD_TIME_MAX) { - log_warn(LD_BUG, "Circuit build time is too large (%u)." - "This is probably a bug.", time); - tor_fragile_assert(); - return -1; - } - - log_debug(LD_CIRC, "Adding circuit build time %u", time); - - cbt->circuit_build_times[cbt->build_times_idx] = time; - cbt->build_times_idx = (cbt->build_times_idx + 1) % CBT_NCIRCUITS_TO_OBSERVE; - if (cbt->total_build_times < CBT_NCIRCUITS_TO_OBSERVE) - cbt->total_build_times++; - - if ((cbt->total_build_times % CBT_SAVE_STATE_EVERY) == 0) { - /* Save state every n circuit builds */ - if (!unit_tests && !get_options()->AvoidDiskWrites) - or_state_mark_dirty(get_or_state(), 0); - } - - return 0; -} - -/** - * Return maximum circuit build time - */ -static build_time_t -circuit_build_times_max(const circuit_build_times_t *cbt) -{ - int i = 0; - build_time_t max_build_time = 0; - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] > max_build_time - && cbt->circuit_build_times[i] != CBT_BUILD_ABANDONED) - max_build_time = cbt->circuit_build_times[i]; - } - return max_build_time; -} - -#if 0 -/** Return minimum circuit build time */ -build_time_t -circuit_build_times_min(circuit_build_times_t *cbt) -{ - int i = 0; - build_time_t min_build_time = CBT_BUILD_TIME_MAX; - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] && /* 0 <-> uninitialized */ - cbt->circuit_build_times[i] < min_build_time) - min_build_time = cbt->circuit_build_times[i]; - } - if (min_build_time == CBT_BUILD_TIME_MAX) { - log_warn(LD_CIRC, "No build times less than CBT_BUILD_TIME_MAX!"); - } - return min_build_time; -} -#endif - -/** - * Calculate and return a histogram for the set of build times. - * - * Returns an allocated array of histrogram bins representing - * the frequency of index*CBT_BIN_WIDTH millisecond - * build times. Also outputs the number of bins in nbins. - * - * The return value must be freed by the caller. - */ -static uint32_t * -circuit_build_times_create_histogram(const circuit_build_times_t *cbt, - build_time_t *nbins) -{ - uint32_t *histogram; - build_time_t max_build_time = circuit_build_times_max(cbt); - int i, c; - - *nbins = 1 + (max_build_time / CBT_BIN_WIDTH); - histogram = tor_malloc_zero(*nbins * sizeof(build_time_t)); - - // calculate histogram - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] == 0 - || cbt->circuit_build_times[i] == CBT_BUILD_ABANDONED) - continue; /* 0 <-> uninitialized */ - - c = (cbt->circuit_build_times[i] / CBT_BIN_WIDTH); - histogram[c]++; - } - - return histogram; -} - -/** - * Return the Pareto start-of-curve parameter Xm. - * - * Because we are not a true Pareto curve, we compute this as the - * weighted average of the N most frequent build time bins. N is either - * 1 if we don't have enough circuit build time data collected, or - * determined by the consensus parameter cbtnummodes (default 3). - */ -static build_time_t -circuit_build_times_get_xm(circuit_build_times_t *cbt) -{ - build_time_t i, nbins; - build_time_t *nth_max_bin; - int32_t bin_counts=0; - build_time_t ret = 0; - uint32_t *histogram = circuit_build_times_create_histogram(cbt, &nbins); - int n=0; - int num_modes = circuit_build_times_default_num_xm_modes(); - - tor_assert(nbins > 0); - tor_assert(num_modes > 0); - - // Only use one mode if < 1000 buildtimes. Not enough data - // for multiple. - if (cbt->total_build_times < CBT_NCIRCUITS_TO_OBSERVE) - num_modes = 1; - - nth_max_bin = (build_time_t*)tor_malloc_zero(num_modes*sizeof(build_time_t)); - - /* Determine the N most common build times */ - for (i = 0; i < nbins; i++) { - if (histogram[i] >= histogram[nth_max_bin[0]]) { - nth_max_bin[0] = i; - } - - for (n = 1; n < num_modes; n++) { - if (histogram[i] >= histogram[nth_max_bin[n]] && - (!histogram[nth_max_bin[n-1]] - || histogram[i] < histogram[nth_max_bin[n-1]])) { - nth_max_bin[n] = i; - } - } - } - - for (n = 0; n < num_modes; n++) { - bin_counts += histogram[nth_max_bin[n]]; - ret += CBT_BIN_TO_MS(nth_max_bin[n])*histogram[nth_max_bin[n]]; - log_info(LD_CIRC, "Xm mode #%d: %u %u", n, CBT_BIN_TO_MS(nth_max_bin[n]), - histogram[nth_max_bin[n]]); - } - - /* The following assert is safe, because we don't get called when we - * haven't observed at least CBT_MIN_MIN_CIRCUITS_TO_OBSERVE circuits. */ - tor_assert(bin_counts > 0); - - ret /= bin_counts; - tor_free(histogram); - tor_free(nth_max_bin); - - return ret; -} - -/** - * Output a histogram of current circuit build times to - * the or_state_t state structure. - */ -void -circuit_build_times_update_state(const circuit_build_times_t *cbt, - or_state_t *state) -{ - uint32_t *histogram; - build_time_t i = 0; - build_time_t nbins = 0; - config_line_t **next, *line; - - histogram = circuit_build_times_create_histogram(cbt, &nbins); - // write to state - config_free_lines(state->BuildtimeHistogram); - next = &state->BuildtimeHistogram; - *next = NULL; - - state->TotalBuildTimes = cbt->total_build_times; - state->CircuitBuildAbandonedCount = 0; - - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] == CBT_BUILD_ABANDONED) - state->CircuitBuildAbandonedCount++; - } - - for (i = 0; i < nbins; i++) { - // compress the histogram by skipping the blanks - if (histogram[i] == 0) continue; - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("CircuitBuildTimeBin"); - tor_asprintf(&line->value, "%d %d", - CBT_BIN_TO_MS(i), histogram[i]); - next = &(line->next); - } - - if (!unit_tests) { - if (!get_options()->AvoidDiskWrites) - or_state_mark_dirty(get_or_state(), 0); - } - - tor_free(histogram); -} - -/** - * Shuffle the build times array. - * - * Adapted from http://en.wikipedia.org/wiki/Fisher-Yates_shuffle - */ -static void -circuit_build_times_shuffle_and_store_array(circuit_build_times_t *cbt, - build_time_t *raw_times, - uint32_t num_times) -{ - uint32_t n = num_times; - if (num_times > CBT_NCIRCUITS_TO_OBSERVE) { - log_notice(LD_CIRC, "The number of circuit times that this Tor version " - "uses to calculate build times is less than the number stored " - "in your state file. Decreasing the circuit time history from " - "%lu to %d.", (unsigned long)num_times, - CBT_NCIRCUITS_TO_OBSERVE); - } - - if (n > INT_MAX-1) { - log_warn(LD_CIRC, "For some insane reasons, you had %lu circuit build " - "observations in your state file. That's far too many; probably " - "there's a bug here.", (unsigned long)n); - n = INT_MAX-1; - } - - /* This code can only be run on a compact array */ - while (n-- > 1) { - int k = crypto_rand_int(n + 1); /* 0 <= k <= n. */ - build_time_t tmp = raw_times[k]; - raw_times[k] = raw_times[n]; - raw_times[n] = tmp; - } - - /* Since the times are now shuffled, take a random CBT_NCIRCUITS_TO_OBSERVE - * subset (ie the first CBT_NCIRCUITS_TO_OBSERVE values) */ - for (n = 0; n < MIN(num_times, CBT_NCIRCUITS_TO_OBSERVE); n++) { - circuit_build_times_add_time(cbt, raw_times[n]); - } -} - -/** - * Filter old synthetic timeouts that were created before the - * new right-censored Pareto calculation was deployed. - * - * Once all clients before 0.2.1.13-alpha are gone, this code - * will be unused. - */ -static int -circuit_build_times_filter_timeouts(circuit_build_times_t *cbt) -{ - int num_filtered=0, i=0; - double timeout_rate = 0; - build_time_t max_timeout = 0; - - timeout_rate = circuit_build_times_timeout_rate(cbt); - max_timeout = (build_time_t)cbt->close_ms; - - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] > max_timeout) { - build_time_t replaced = cbt->circuit_build_times[i]; - num_filtered++; - cbt->circuit_build_times[i] = CBT_BUILD_ABANDONED; - - log_debug(LD_CIRC, "Replaced timeout %d with %d", replaced, - cbt->circuit_build_times[i]); - } - } - - log_info(LD_CIRC, - "We had %d timeouts out of %d build times, " - "and filtered %d above the max of %u", - (int)(cbt->total_build_times*timeout_rate), - cbt->total_build_times, num_filtered, max_timeout); - - return num_filtered; -} - -/** - * Load histogram from state, shuffling the resulting array - * after we do so. Use this result to estimate parameters and - * calculate the timeout. - * - * Return -1 on error. - */ -int -circuit_build_times_parse_state(circuit_build_times_t *cbt, - or_state_t *state) -{ - int tot_values = 0; - uint32_t loaded_cnt = 0, N = 0; - config_line_t *line; - unsigned int i; - build_time_t *loaded_times; - int err = 0; - circuit_build_times_init(cbt); - - if (circuit_build_times_disabled()) { - return 0; - } - - /* build_time_t 0 means uninitialized */ - loaded_times = tor_malloc_zero(sizeof(build_time_t)*state->TotalBuildTimes); - - for (line = state->BuildtimeHistogram; line; line = line->next) { - smartlist_t *args = smartlist_new(); - smartlist_split_string(args, line->value, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (smartlist_len(args) < 2) { - log_warn(LD_GENERAL, "Unable to parse circuit build times: " - "Too few arguments to CircuitBuildTime"); - err = 1; - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - break; - } else { - const char *ms_str = smartlist_get(args,0); - const char *count_str = smartlist_get(args,1); - uint32_t count, k; - build_time_t ms; - int ok; - ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0, - CBT_BUILD_TIME_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_GENERAL, "Unable to parse circuit build times: " - "Unparsable bin number"); - err = 1; - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - break; - } - count = (uint32_t)tor_parse_ulong(count_str, 0, 0, - UINT32_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_GENERAL, "Unable to parse circuit build times: " - "Unparsable bin count"); - err = 1; - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - break; - } - - if (loaded_cnt+count+state->CircuitBuildAbandonedCount - > state->TotalBuildTimes) { - log_warn(LD_CIRC, - "Too many build times in state file. " - "Stopping short before %d", - loaded_cnt+count); - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - break; - } - - for (k = 0; k < count; k++) { - loaded_times[loaded_cnt++] = ms; - } - N++; - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - } - } - - log_info(LD_CIRC, - "Adding %d timeouts.", state->CircuitBuildAbandonedCount); - for (i=0; i < state->CircuitBuildAbandonedCount; i++) { - loaded_times[loaded_cnt++] = CBT_BUILD_ABANDONED; - } - - if (loaded_cnt != state->TotalBuildTimes) { - log_warn(LD_CIRC, - "Corrupt state file? Build times count mismatch. " - "Read %d times, but file says %d", loaded_cnt, - state->TotalBuildTimes); - err = 1; - circuit_build_times_reset(cbt); - goto done; - } - - circuit_build_times_shuffle_and_store_array(cbt, loaded_times, loaded_cnt); - - /* Verify that we didn't overwrite any indexes */ - for (i=0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (!cbt->circuit_build_times[i]) - break; - tot_values++; - } - log_info(LD_CIRC, - "Loaded %d/%d values from %d lines in circuit time histogram", - tot_values, cbt->total_build_times, N); - - if (cbt->total_build_times != tot_values - || cbt->total_build_times > CBT_NCIRCUITS_TO_OBSERVE) { - log_warn(LD_CIRC, - "Corrupt state file? Shuffled build times mismatch. " - "Read %d times, but file says %d", tot_values, - state->TotalBuildTimes); - err = 1; - circuit_build_times_reset(cbt); - goto done; - } - - circuit_build_times_set_timeout(cbt); - - if (!state->CircuitBuildAbandonedCount && cbt->total_build_times) { - circuit_build_times_filter_timeouts(cbt); - } - - done: - tor_free(loaded_times); - return err ? -1 : 0; -} - -/** - * Estimates the Xm and Alpha parameters using - * http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation - * - * The notable difference is that we use mode instead of min to estimate Xm. - * This is because our distribution is frechet-like. We claim this is - * an acceptable approximation because we are only concerned with the - * accuracy of the CDF of the tail. - */ -STATIC int -circuit_build_times_update_alpha(circuit_build_times_t *cbt) -{ - build_time_t *x=cbt->circuit_build_times; - double a = 0; - int n=0,i=0,abandoned_count=0; - build_time_t max_time=0; - - /* http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation */ - /* We sort of cheat here and make our samples slightly more pareto-like - * and less frechet-like. */ - cbt->Xm = circuit_build_times_get_xm(cbt); - - tor_assert(cbt->Xm > 0); - - for (i=0; i< CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (!x[i]) { - continue; - } - - if (x[i] < cbt->Xm) { - a += tor_mathlog(cbt->Xm); - } else if (x[i] == CBT_BUILD_ABANDONED) { - abandoned_count++; - } else { - a += tor_mathlog(x[i]); - if (x[i] > max_time) - max_time = x[i]; - } - n++; - } - - /* - * We are erring and asserting here because this can only happen - * in codepaths other than startup. The startup state parsing code - * performs this same check, and resets state if it hits it. If we - * hit it at runtime, something serious has gone wrong. - */ - if (n!=cbt->total_build_times) { - log_err(LD_CIRC, "Discrepancy in build times count: %d vs %d", n, - cbt->total_build_times); - } - tor_assert(n==cbt->total_build_times); - - if (max_time <= 0) { - /* This can happen if Xm is actually the *maximum* value in the set. - * It can also happen if we've abandoned every single circuit somehow. - * In either case, tell the caller not to compute a new build timeout. */ - log_warn(LD_BUG, - "Could not determine largest build time (%d). " - "Xm is %dms and we've abandoned %d out of %d circuits.", max_time, - cbt->Xm, abandoned_count, n); - return 0; - } - - a += abandoned_count*tor_mathlog(max_time); - - a -= n*tor_mathlog(cbt->Xm); - // Estimator comes from Eq #4 in: - // "Bayesian estimation based on trimmed samples from Pareto populations" - // by Arturo J. Fernández. We are right-censored only. - a = (n-abandoned_count)/a; - - cbt->alpha = a; - - return 1; -} - -/** - * This is the Pareto Quantile Function. It calculates the point x - * in the distribution such that F(x) = quantile (ie quantile*100% - * of the mass of the density function is below x on the curve). - * - * We use it to calculate the timeout and also to generate synthetic - * values of time for circuits that timeout before completion. - * - * See http://en.wikipedia.org/wiki/Quantile_function, - * http://en.wikipedia.org/wiki/Inverse_transform_sampling and - * http://en.wikipedia.org/wiki/Pareto_distribution#Generating_a_ - * random_sample_from_Pareto_distribution - * That's right. I'll cite wikipedia all day long. - * - * Return value is in milliseconds. - */ -STATIC double -circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, - double quantile) -{ - double ret; - tor_assert(quantile >= 0); - tor_assert(1.0-quantile > 0); - tor_assert(cbt->Xm > 0); - - ret = cbt->Xm/pow(1.0-quantile,1.0/cbt->alpha); - if (ret > INT32_MAX) { - ret = INT32_MAX; - } - tor_assert(ret > 0); - return ret; -} - -#ifdef TOR_UNIT_TESTS -/** Pareto CDF */ -double -circuit_build_times_cdf(circuit_build_times_t *cbt, double x) -{ - double ret; - tor_assert(cbt->Xm > 0); - ret = 1.0-pow(cbt->Xm/x,cbt->alpha); - tor_assert(0 <= ret && ret <= 1.0); - return ret; -} -#endif - -#ifdef TOR_UNIT_TESTS -/** - * Generate a synthetic time using our distribution parameters. - * - * The return value will be within the [q_lo, q_hi) quantile points - * on the CDF. - */ -build_time_t -circuit_build_times_generate_sample(circuit_build_times_t *cbt, - double q_lo, double q_hi) -{ - double randval = crypto_rand_double(); - build_time_t ret; - double u; - - /* Generate between [q_lo, q_hi) */ - /*XXXX This is what nextafter is supposed to be for; we should use it on the - * platforms that support it. */ - q_hi -= 1.0/(INT32_MAX); - - tor_assert(q_lo >= 0); - tor_assert(q_hi < 1); - tor_assert(q_lo < q_hi); - - u = q_lo + (q_hi-q_lo)*randval; - - tor_assert(0 <= u && u < 1.0); - /* circuit_build_times_calculate_timeout returns <= INT32_MAX */ - ret = (build_time_t) - tor_lround(circuit_build_times_calculate_timeout(cbt, u)); - tor_assert(ret > 0); - return ret; -} -#endif - -#ifdef TOR_UNIT_TESTS -/** - * Estimate an initial alpha parameter by solving the quantile - * function with a quantile point and a specific timeout value. - */ -void -circuit_build_times_initial_alpha(circuit_build_times_t *cbt, - double quantile, double timeout_ms) -{ - // Q(u) = Xm/((1-u)^(1/a)) - // Q(0.8) = Xm/((1-0.8))^(1/a)) = CircBuildTimeout - // CircBuildTimeout = Xm/((1-0.8))^(1/a)) - // CircBuildTimeout = Xm*((1-0.8))^(-1/a)) - // ln(CircBuildTimeout) = ln(Xm)+ln(((1-0.8)))*(-1/a) - // -ln(1-0.8)/(ln(CircBuildTimeout)-ln(Xm))=a - tor_assert(quantile >= 0); - tor_assert(cbt->Xm > 0); - cbt->alpha = tor_mathlog(1.0-quantile)/ - (tor_mathlog(cbt->Xm)-tor_mathlog(timeout_ms)); - tor_assert(cbt->alpha > 0); -} -#endif - -/** - * Returns true if we need circuits to be built - */ -int -circuit_build_times_needs_circuits(const circuit_build_times_t *cbt) -{ - /* Return true if < MIN_CIRCUITS_TO_OBSERVE */ - return !circuit_build_times_enough_to_compute(cbt); -} - -/** - * Returns true if we should build a timeout test circuit - * right now. - */ -int -circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt) -{ - return circuit_build_times_needs_circuits(cbt) && - approx_time()-cbt->last_circ_at > circuit_build_times_test_frequency(); -} - -/** - * Called to indicate that the network showed some signs of liveness, - * i.e. we received a cell. - * - * This is used by circuit_build_times_network_check_live() to decide - * if we should record the circuit build timeout or not. - * - * This function is called every time we receive a cell. Avoid - * syscalls, events, and other high-intensity work. - */ -void -circuit_build_times_network_is_live(circuit_build_times_t *cbt) -{ - time_t now = approx_time(); - if (cbt->liveness.nonlive_timeouts > 0) { - log_notice(LD_CIRC, - "Tor now sees network activity. Restoring circuit build " - "timeout recording. Network was down for %d seconds " - "during %d circuit attempts.", - (int)(now - cbt->liveness.network_last_live), - cbt->liveness.nonlive_timeouts); - } - cbt->liveness.network_last_live = now; - cbt->liveness.nonlive_timeouts = 0; -} - -/** - * Called to indicate that we completed a circuit. Because this circuit - * succeeded, it doesn't count as a timeout-after-the-first-hop. - * - * This is used by circuit_build_times_network_check_changed() to determine - * if we had too many recent timeouts and need to reset our learned timeout - * to something higher. - */ -void -circuit_build_times_network_circ_success(circuit_build_times_t *cbt) -{ - /* Check for NULLness because we might not be using adaptive timeouts */ - if (cbt->liveness.timeouts_after_firsthop && - cbt->liveness.num_recent_circs > 0) { - cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx] - = 0; - cbt->liveness.after_firsthop_idx++; - cbt->liveness.after_firsthop_idx %= cbt->liveness.num_recent_circs; - } -} - -/** - * A circuit just timed out. If it failed after the first hop, record it - * in our history for later deciding if the network speed has changed. - * - * This is used by circuit_build_times_network_check_changed() to determine - * if we had too many recent timeouts and need to reset our learned timeout - * to something higher. - */ -static void -circuit_build_times_network_timeout(circuit_build_times_t *cbt, - int did_onehop) -{ - /* Check for NULLness because we might not be using adaptive timeouts */ - if (cbt->liveness.timeouts_after_firsthop && - cbt->liveness.num_recent_circs > 0) { - if (did_onehop) { - cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx] - = 1; - cbt->liveness.after_firsthop_idx++; - cbt->liveness.after_firsthop_idx %= cbt->liveness.num_recent_circs; - } - } -} - -/** - * A circuit was just forcibly closed. If there has been no recent network - * activity at all, but this circuit was launched back when we thought the - * network was live, increment the number of "nonlive" circuit timeouts. - * - * This is used by circuit_build_times_network_check_live() to decide - * if we should record the circuit build timeout or not. - */ -static void -circuit_build_times_network_close(circuit_build_times_t *cbt, - int did_onehop, time_t start_time) -{ - time_t now = time(NULL); - /* - * Check if this is a timeout that was for a circuit that spent its - * entire existence during a time where we have had no network activity. - */ - if (cbt->liveness.network_last_live < start_time) { - if (did_onehop) { - char last_live_buf[ISO_TIME_LEN+1]; - char start_time_buf[ISO_TIME_LEN+1]; - char now_buf[ISO_TIME_LEN+1]; - format_local_iso_time(last_live_buf, cbt->liveness.network_last_live); - format_local_iso_time(start_time_buf, start_time); - format_local_iso_time(now_buf, now); - log_notice(LD_CIRC, - "A circuit somehow completed a hop while the network was " - "not live. The network was last live at %s, but the circuit " - "launched at %s. It's now %s. This could mean your clock " - "changed.", last_live_buf, start_time_buf, now_buf); - } - cbt->liveness.nonlive_timeouts++; - if (cbt->liveness.nonlive_timeouts == 1) { - log_notice(LD_CIRC, - "Tor has not observed any network activity for the past %d " - "seconds. Disabling circuit build timeout recording.", - (int)(now - cbt->liveness.network_last_live)); - } else { - log_info(LD_CIRC, - "Got non-live timeout. Current count is: %d", - cbt->liveness.nonlive_timeouts); - } - } -} - -/** - * When the network is not live, we do not record circuit build times. - * - * The network is considered not live if there has been at least one - * circuit build that began and ended (had its close_ms measurement - * period expire) since we last received a cell. - * - * Also has the side effect of rewinding the circuit time history - * in the case of recent liveness changes. - */ -int -circuit_build_times_network_check_live(const circuit_build_times_t *cbt) -{ - if (cbt->liveness.nonlive_timeouts > 0) { - return 0; - } - - return 1; -} - -/** - * Returns true if we have seen more than MAX_RECENT_TIMEOUT_COUNT of - * the past RECENT_CIRCUITS time out after the first hop. Used to detect - * if the network connection has changed significantly, and if so, - * resets our circuit build timeout to the default. - * - * Also resets the entire timeout history in this case and causes us - * to restart the process of building test circuits and estimating a - * new timeout. - */ -STATIC int -circuit_build_times_network_check_changed(circuit_build_times_t *cbt) -{ - int total_build_times = cbt->total_build_times; - int timeout_count=0; - int i; - - if (cbt->liveness.timeouts_after_firsthop && - cbt->liveness.num_recent_circs > 0) { - /* how many of our recent circuits made it to the first hop but then - * timed out? */ - for (i = 0; i < cbt->liveness.num_recent_circs; i++) { - timeout_count += cbt->liveness.timeouts_after_firsthop[i]; - } - } - - /* If 80% of our recent circuits are timing out after the first hop, - * we need to re-estimate a new initial alpha and timeout. */ - if (timeout_count < circuit_build_times_max_timeouts()) { - return 0; - } - - circuit_build_times_reset(cbt); - if (cbt->liveness.timeouts_after_firsthop && - cbt->liveness.num_recent_circs > 0) { - memset(cbt->liveness.timeouts_after_firsthop, 0, - sizeof(*cbt->liveness.timeouts_after_firsthop)* - cbt->liveness.num_recent_circs); - } - cbt->liveness.after_firsthop_idx = 0; - - /* Check to see if this has happened before. If so, double the timeout - * to give people on abysmally bad network connections a shot at access */ - if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) { - if (cbt->timeout_ms > INT32_MAX/2 || cbt->close_ms > INT32_MAX/2) { - log_warn(LD_CIRC, "Insanely large circuit build timeout value. " - "(timeout = %fmsec, close = %fmsec)", - cbt->timeout_ms, cbt->close_ms); - } else { - cbt->timeout_ms *= 2; - cbt->close_ms *= 2; - } - } else { - cbt->close_ms = cbt->timeout_ms - = circuit_build_times_get_initial_timeout(); - } - - cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET); - - log_notice(LD_CIRC, - "Your network connection speed appears to have changed. Resetting " - "timeout to %lds after %d timeouts and %d buildtimes.", - tor_lround(cbt->timeout_ms/1000), timeout_count, - total_build_times); - - return 1; -} - -/** - * Count the number of timeouts in a set of cbt data. - */ -double -circuit_build_times_timeout_rate(const circuit_build_times_t *cbt) -{ - int i=0,timeouts=0; - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] >= cbt->timeout_ms) { - timeouts++; - } - } - - if (!cbt->total_build_times) - return 0; - - return ((double)timeouts)/cbt->total_build_times; -} - -/** - * Count the number of closed circuits in a set of cbt data. - */ -double -circuit_build_times_close_rate(const circuit_build_times_t *cbt) -{ - int i=0,closed=0; - for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { - if (cbt->circuit_build_times[i] == CBT_BUILD_ABANDONED) { - closed++; - } - } - - if (!cbt->total_build_times) - return 0; - - return ((double)closed)/cbt->total_build_times; -} - -/** - * Store a timeout as a synthetic value. - * - * Returns true if the store was successful and we should possibly - * update our timeout estimate. - */ -int -circuit_build_times_count_close(circuit_build_times_t *cbt, - int did_onehop, - time_t start_time) -{ - if (circuit_build_times_disabled()) { - cbt->close_ms = cbt->timeout_ms - = circuit_build_times_get_initial_timeout(); - return 0; - } - - /* Record this force-close to help determine if the network is dead */ - circuit_build_times_network_close(cbt, did_onehop, start_time); - - /* Only count timeouts if network is live.. */ - if (!circuit_build_times_network_check_live(cbt)) { - return 0; - } - - circuit_build_times_add_time(cbt, CBT_BUILD_ABANDONED); - return 1; -} - -/** - * Update timeout counts to determine if we need to expire - * our build time history due to excessive timeouts. - * - * We do not record any actual time values at this stage; - * we are only interested in recording the fact that a timeout - * happened. We record the time values via - * circuit_build_times_count_close() and circuit_build_times_add_time(). - */ -void -circuit_build_times_count_timeout(circuit_build_times_t *cbt, - int did_onehop) -{ - if (circuit_build_times_disabled()) { - cbt->close_ms = cbt->timeout_ms - = circuit_build_times_get_initial_timeout(); - return; - } - - /* Register the fact that a timeout just occurred. */ - circuit_build_times_network_timeout(cbt, did_onehop); - - /* If there are a ton of timeouts, we should reset - * the circuit build timeout. */ - circuit_build_times_network_check_changed(cbt); -} - -/** - * Estimate a new timeout based on history and set our timeout - * variable accordingly. - */ -static int -circuit_build_times_set_timeout_worker(circuit_build_times_t *cbt) -{ - build_time_t max_time; - if (!circuit_build_times_enough_to_compute(cbt)) - return 0; - - if (!circuit_build_times_update_alpha(cbt)) - return 0; - - cbt->timeout_ms = circuit_build_times_calculate_timeout(cbt, - circuit_build_times_quantile_cutoff()); - - cbt->close_ms = circuit_build_times_calculate_timeout(cbt, - circuit_build_times_close_quantile()); - - max_time = circuit_build_times_max(cbt); - - if (cbt->timeout_ms > max_time) { - log_info(LD_CIRC, - "Circuit build timeout of %dms is beyond the maximum build " - "time we have ever observed. Capping it to %dms.", - (int)cbt->timeout_ms, max_time); - cbt->timeout_ms = max_time; - } - - if (max_time < INT32_MAX/2 && cbt->close_ms > 2*max_time) { - log_info(LD_CIRC, - "Circuit build measurement period of %dms is more than twice " - "the maximum build time we have ever observed. Capping it to " - "%dms.", (int)cbt->close_ms, 2*max_time); - cbt->close_ms = 2*max_time; - } - - /* Sometimes really fast guard nodes give us such a steep curve - * that this ends up being not that much greater than timeout_ms. - * Make it be at least 1 min to handle this case. */ - cbt->close_ms = MAX(cbt->close_ms, circuit_build_times_initial_timeout()); - - cbt->have_computed_timeout = 1; - return 1; -} - -/** - * Exposed function to compute a new timeout. Dispatches events and - * also filters out extremely high timeout values. - */ -void -circuit_build_times_set_timeout(circuit_build_times_t *cbt) -{ - long prev_timeout = tor_lround(cbt->timeout_ms/1000); - double timeout_rate; - - /* - * Just return if we aren't using adaptive timeouts - */ - if (circuit_build_times_disabled()) - return; - - if (!circuit_build_times_set_timeout_worker(cbt)) - return; - - if (cbt->timeout_ms < circuit_build_times_min_timeout()) { - 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) { - /* This shouldn't happen because of MAX() in timeout_worker above, - * but doing it just in case */ - cbt->close_ms = circuit_build_times_initial_timeout(); - } - } - - cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_COMPUTED); - - timeout_rate = circuit_build_times_timeout_rate(cbt); - - if (prev_timeout > tor_lround(cbt->timeout_ms/1000)) { - log_info(LD_CIRC, - "Based on %d circuit times, it looks like we don't need to " - "wait so long for circuits to finish. We will now assume a " - "circuit is too slow to use after waiting %ld seconds.", - cbt->total_build_times, - tor_lround(cbt->timeout_ms/1000)); - log_info(LD_CIRC, - "Circuit timeout data: %fms, %fms, Xm: %d, a: %f, r: %f", - cbt->timeout_ms, cbt->close_ms, cbt->Xm, cbt->alpha, - timeout_rate); - } else if (prev_timeout < tor_lround(cbt->timeout_ms/1000)) { - log_info(LD_CIRC, - "Based on %d circuit times, it looks like we need to wait " - "longer for circuits to finish. We will now assume a " - "circuit is too slow to use after waiting %ld seconds.", - cbt->total_build_times, - tor_lround(cbt->timeout_ms/1000)); - log_info(LD_CIRC, - "Circuit timeout data: %fms, %fms, Xm: %d, a: %f, r: %f", - cbt->timeout_ms, cbt->close_ms, cbt->Xm, cbt->alpha, - timeout_rate); - } else { - log_info(LD_CIRC, - "Set circuit build timeout to %lds (%fms, %fms, Xm: %d, a: %f," - " r: %f) based on %d circuit times", - tor_lround(cbt->timeout_ms/1000), - cbt->timeout_ms, cbt->close_ms, cbt->Xm, cbt->alpha, timeout_rate, - cbt->total_build_times); - } -} - -#ifdef TOR_UNIT_TESTS -/** Make a note that we're running unit tests (rather than running Tor - * itself), so we avoid clobbering our state file. */ -void -circuitbuild_running_unit_tests(void) -{ - unit_tests = 1; -} -#endif - -void -circuit_build_times_update_last_circ(circuit_build_times_t *cbt) -{ - cbt->last_circ_at = approx_time(); -} - -static void -cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, - buildtimeout_set_event_t type) -{ - char *args = NULL; - double qnt; - - switch (type) { - case BUILDTIMEOUT_SET_EVENT_RESET: - case BUILDTIMEOUT_SET_EVENT_SUSPENDED: - case BUILDTIMEOUT_SET_EVENT_DISCARD: - qnt = 1.0; - break; - case BUILDTIMEOUT_SET_EVENT_COMPUTED: - case BUILDTIMEOUT_SET_EVENT_RESUME: - default: - qnt = circuit_build_times_quantile_cutoff(); - break; - } - - tor_asprintf(&args, "TOTAL_TIMES=%lu " - "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " - "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", - (unsigned long)cbt->total_build_times, - (unsigned long)cbt->timeout_ms, - (unsigned long)cbt->Xm, cbt->alpha, qnt, - circuit_build_times_timeout_rate(cbt), - (unsigned long)cbt->close_ms, - circuit_build_times_close_rate(cbt)); - - control_event_buildtimeout_set(type, args); - - tor_free(args); -} - diff --git a/src/tor/circuitstats.h b/src/tor/circuitstats.h deleted file mode 100644 index 3343310..0000000 --- a/src/tor/circuitstats.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuitstats.h - * \brief Header file for circuitstats.c - **/ - -#ifndef TOR_CIRCUITSTATS_H -#define TOR_CIRCUITSTATS_H - -const circuit_build_times_t *get_circuit_build_times(void); -circuit_build_times_t *get_circuit_build_times_mutable(void); -double get_circuit_build_close_time_ms(void); -double get_circuit_build_timeout_ms(void); - -int circuit_build_times_disabled(void); -int circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt); -void circuit_build_times_update_state(const circuit_build_times_t *cbt, - or_state_t *state); -int circuit_build_times_parse_state(circuit_build_times_t *cbt, - or_state_t *state); -void circuit_build_times_count_timeout(circuit_build_times_t *cbt, - int did_onehop); -int circuit_build_times_count_close(circuit_build_times_t *cbt, - int did_onehop, time_t start_time); -void circuit_build_times_set_timeout(circuit_build_times_t *cbt); -int circuit_build_times_add_time(circuit_build_times_t *cbt, - build_time_t time); -int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt); - -int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); -void circuit_build_times_init(circuit_build_times_t *cbt); -void circuit_build_times_free_timeouts(circuit_build_times_t *cbt); -void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns); -double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); -double circuit_build_times_close_rate(const circuit_build_times_t *cbt); - -void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); - -#ifdef CIRCUITSTATS_PRIVATE -STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, - double quantile); -STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt); -STATIC void circuit_build_times_reset(circuit_build_times_t *cbt); - -/* Network liveness functions */ -STATIC int circuit_build_times_network_check_changed( - circuit_build_times_t *cbt); -#endif - -#ifdef TOR_UNIT_TESTS -build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt, - double q_lo, double q_hi); -double circuit_build_times_cdf(circuit_build_times_t *cbt, double x); -void circuit_build_times_initial_alpha(circuit_build_times_t *cbt, - double quantile, double time_ms); -void circuitbuild_running_unit_tests(void); -#endif - -/* Network liveness functions */ -void circuit_build_times_network_is_live(circuit_build_times_t *cbt); -int circuit_build_times_network_check_live(const circuit_build_times_t *cbt); -void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); - -#ifdef CIRCUITSTATS_PRIVATE -/** Structure for circuit build times history */ -struct circuit_build_times_s { - /** The circular array of recorded build times in milliseconds */ - build_time_t circuit_build_times[CBT_NCIRCUITS_TO_OBSERVE]; - /** Current index in the circuit_build_times circular array */ - int build_times_idx; - /** Total number of build times accumulated. Max CBT_NCIRCUITS_TO_OBSERVE */ - int total_build_times; - /** Information about the state of our local network connection */ - network_liveness_t liveness; - /** Last time we built a circuit. Used to decide to build new test circs */ - time_t last_circ_at; - /** "Minimum" value of our pareto distribution (actually mode) */ - build_time_t Xm; - /** alpha exponent for pareto dist. */ - double alpha; - /** Have we computed a timeout? */ - int have_computed_timeout; - /** The exact value for that timeout in milliseconds. Stored as a double - * to maintain precision from calculations to and from quantile value. */ - double timeout_ms; - /** How long we wait before actually closing the circuit. */ - double close_ms; -}; -#endif - -#endif - diff --git a/src/tor/circuituse.c b/src/tor/circuituse.c deleted file mode 100644 index 8b82de0..0000000 --- a/src/tor/circuituse.c +++ /dev/null @@ -1,2326 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuituse.c - * \brief Launch the right sort of circuits and attach streams to them. - **/ - -#include "or.h" -#include "addressmap.h" -#include "channel.h" -#include "circpathbias.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitstats.h" -#include "circuituse.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "control.h" -#include "entrynodes.h" -#include "nodelist.h" -#include "networkstatus.h" -#include "policies.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rendservice.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" - -static void circuit_expire_old_circuits_clientside(void); -static void circuit_increment_failure_count(void); - -/** Return 1 if circ could be returned by circuit_get_best(). - * Else return 0. - */ -static int -circuit_is_acceptable(const origin_circuit_t *origin_circ, - const entry_connection_t *conn, - int must_be_open, uint8_t purpose, - int need_uptime, int need_internal, - time_t now) -{ - const circuit_t *circ = TO_CIRCUIT(origin_circ); - const node_t *exitnode; - cpath_build_state_t *build_state; - tor_assert(circ); - tor_assert(conn); - tor_assert(conn->socks_request); - - if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_chan)) - return 0; /* ignore non-open circs */ - if (circ->marked_for_close) - return 0; - - /* if this circ isn't our purpose, skip. */ - if (purpose == CIRCUIT_PURPOSE_C_REND_JOINED && !must_be_open) { - if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND && - circ->purpose != CIRCUIT_PURPOSE_C_REND_READY && - circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED && - circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED) - return 0; - } else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT && - !must_be_open) { - if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCING && - circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) - return 0; - } else { - if (purpose != circ->purpose) - return 0; - } - - /* If this is a timed-out hidden service circuit, skip it. */ - if (origin_circ->hs_circ_has_timed_out) { - return 0; - } - - if (purpose == CIRCUIT_PURPOSE_C_GENERAL || - purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { - if (circ->timestamp_dirty && - circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now) - return 0; - } - - if (origin_circ->unusable_for_new_conns) - return 0; - - /* decide if this circ is suitable for this conn */ - - /* for rend circs, circ->cpath->prev is not the last router in the - * circuit, it's the magical extra bob hop. so just check the nickname - * of the one we meant to finish at. - */ - build_state = origin_circ->build_state; - exitnode = build_state_get_exit_node(build_state); - - if (need_uptime && !build_state->need_uptime) - return 0; - if (need_internal != build_state->is_internal) - return 0; - - if (purpose == CIRCUIT_PURPOSE_C_GENERAL) { - tor_addr_t addr; - const int family = tor_addr_parse(&addr, conn->socks_request->address); - if (!exitnode && !build_state->onehop_tunnel) { - log_debug(LD_CIRC,"Not considering circuit with unknown router."); - return 0; /* this circuit is screwed and doesn't know it yet, - * or is a rendezvous circuit. */ - } - if (build_state->onehop_tunnel) { - if (!conn->want_onehop) { - log_debug(LD_CIRC,"Skipping one-hop circuit."); - return 0; - } - tor_assert(conn->chosen_exit_name); - if (build_state->chosen_exit) { - char digest[DIGEST_LEN]; - if (hexdigest_to_digest(conn->chosen_exit_name, digest) < 0) - return 0; /* broken digest, we don't want it */ - if (tor_memneq(digest, build_state->chosen_exit->identity_digest, - DIGEST_LEN)) - return 0; /* this is a circuit to somewhere else */ - if (tor_digest_is_zero(digest)) { - /* we don't know the digest; have to compare addr:port */ - if (family < 0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != conn->socks_request->port) - return 0; - } - } - } else { - if (conn->want_onehop) { - /* don't use three-hop circuits -- that could hurt our anonymity. */ - return 0; - } - } - if (origin_circ->prepend_policy && family != -1) { - int r = compare_tor_addr_to_addr_policy(&addr, - conn->socks_request->port, - origin_circ->prepend_policy); - if (r == ADDR_POLICY_REJECTED) - return 0; - } - if (exitnode && !connection_ap_can_use_exit(conn, exitnode)) { - /* can't exit from this router */ - return 0; - } - } else { /* not general */ - const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn); - if ((edge_conn->rend_data && !origin_circ->rend_data) || - (!edge_conn->rend_data && origin_circ->rend_data) || - (edge_conn->rend_data && origin_circ->rend_data && - rend_cmp_service_ids(edge_conn->rend_data->onion_address, - origin_circ->rend_data->onion_address))) { - /* this circ is not for this conn */ - return 0; - } - } - - if (!connection_edge_compatible_with_circuit(conn, origin_circ)) { - /* conn needs to be isolated from other conns that have already used - * origin_circ */ - return 0; - } - - return 1; -} - -/** Return 1 if circuit a is better than circuit b for - * conn, and return 0 otherwise. Used by circuit_get_best. - */ -static int -circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob, - const entry_connection_t *conn) -{ - const circuit_t *a = TO_CIRCUIT(oa); - const circuit_t *b = TO_CIRCUIT(ob); - const uint8_t purpose = ENTRY_TO_CONN(conn)->purpose; - int a_bits, b_bits; - - /* If one of the circuits was allowed to live due to relaxing its timeout, - * it is definitely worse (it's probably a much slower path). */ - if (oa->relaxed_timeout && !ob->relaxed_timeout) - return 0; /* ob is better. It's not relaxed. */ - if (!oa->relaxed_timeout && ob->relaxed_timeout) - return 1; /* oa is better. It's not relaxed. */ - - switch (purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - /* if it's used but less dirty it's best; - * else if it's more recently created it's best - */ - if (b->timestamp_dirty) { - if (a->timestamp_dirty && - a->timestamp_dirty > b->timestamp_dirty) - return 1; - } else { - if (a->timestamp_dirty || - timercmp(&a->timestamp_began, &b->timestamp_began, >)) - return 1; - if (ob->build_state->is_internal) - /* XXX023 what the heck is this internal thing doing here. I - * think we can get rid of it. circuit_is_acceptable() already - * makes sure that is_internal is exactly what we need it to - * be. -RD */ - return 1; - } - break; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - /* the closer it is to ack_wait the better it is */ - if (a->purpose > b->purpose) - return 1; - break; - case CIRCUIT_PURPOSE_C_REND_JOINED: - /* the closer it is to rend_joined the better it is */ - if (a->purpose > b->purpose) - return 1; - break; - } - - /* XXXX023 Maybe this check should get a higher priority to avoid - * using up circuits too rapidly. */ - - a_bits = connection_edge_update_circuit_isolation(conn, - (origin_circuit_t*)oa, 1); - b_bits = connection_edge_update_circuit_isolation(conn, - (origin_circuit_t*)ob, 1); - /* if x_bits < 0, then we have not used x for anything; better not to dirty - * a connection if we can help it. */ - if (a_bits < 0) { - return 0; - } else if (b_bits < 0) { - return 1; - } - a_bits &= ~ oa->isolation_flags_mixed; - a_bits &= ~ ob->isolation_flags_mixed; - if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) { - /* The fewer new restrictions we need to make on a circuit for stream - * isolation, the better. */ - return 1; - } - - return 0; -} - -/** Find the best circ that conn can use, preferably one which is - * dirty. Circ must not be too old. - * - * Conn must be defined. - * - * If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN. - * - * circ_purpose specifies what sort of circuit we must have. - * It can be C_GENERAL, C_INTRODUCE_ACK_WAIT, or C_REND_JOINED. - * - * If it's REND_JOINED and must_be_open==0, then return the closest - * rendezvous-purposed circuit that you can find. - * - * If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the - * closest introduce-purposed circuit that you can find. - */ -static origin_circuit_t * -circuit_get_best(const entry_connection_t *conn, - int must_be_open, uint8_t purpose, - int need_uptime, int need_internal) -{ - circuit_t *circ; - origin_circuit_t *best=NULL; - struct timeval now; - int intro_going_on_but_too_old = 0; - - tor_assert(conn); - - tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || - purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || - purpose == CIRCUIT_PURPOSE_C_REND_JOINED); - - tor_gettimeofday(&now); - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - origin_circuit_t *origin_circ; - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - origin_circ = TO_ORIGIN_CIRCUIT(circ); - - /* Log an info message if we're going to launch a new intro circ in - * parallel */ - if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT && - !must_be_open && origin_circ->hs_circ_has_timed_out) { - intro_going_on_but_too_old = 1; - continue; - } - - if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose, - need_uptime,need_internal,now.tv_sec)) - continue; - - /* now this is an acceptable circ to hand back. but that doesn't - * mean it's the *best* circ to hand back. try to decide. - */ - if (!best || circuit_is_better(origin_circ,best,conn)) - best = origin_circ; - } - - if (!best && intro_going_on_but_too_old) - log_info(LD_REND|LD_CIRC, "There is an intro circuit being created " - "right now, but it has already taken quite a while. Starting " - "one in parallel."); - - return best; -} - -/** Return the number of not-yet-open general-purpose origin circuits. */ -static int -count_pending_general_client_circuits(void) -{ - const circuit_t *circ; - - int count = 0; - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (circ->marked_for_close || - circ->state == CIRCUIT_STATE_OPEN || - circ->purpose != CIRCUIT_PURPOSE_C_GENERAL || - !CIRCUIT_IS_ORIGIN(circ)) - continue; - - ++count; - } - - return count; -} - -#if 0 -/** Check whether, according to the policies in options, the - * circuit circ makes sense. */ -/* XXXX currently only checks Exclude{Exit}Nodes; it should check more. - * Also, it doesn't have the right definition of an exit circuit. Also, - * it's never called. */ -int -circuit_conforms_to_options(const origin_circuit_t *circ, - const or_options_t *options) -{ - const crypt_path_t *cpath, *cpath_next = NULL; - - /* first check if it includes any excluded nodes */ - for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) { - cpath_next = cpath->next; - if (routerset_contains_extendinfo(options->ExcludeNodes, - cpath->extend_info)) - return 0; - } - - /* then consider the final hop */ - if (routerset_contains_extendinfo(options->ExcludeExitNodes, - circ->cpath->prev->extend_info)) - return 0; - - return 1; -} -#endif - -/** Close all circuits that start at us, aren't open, and were born - * at least CircuitBuildTimeout seconds ago. - */ -void -circuit_expire_building(void) -{ - circuit_t *victim, *next_circ; - /* circ_times.timeout_ms and circ_times.close_ms are from - * circuit_build_times_get_initial_timeout() if we haven't computed - * custom timeouts yet */ - struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff, - close_cutoff, extremely_old_cutoff, hs_extremely_old_cutoff, - cannibalized_cutoff, c_intro_cutoff, s_intro_cutoff, stream_cutoff; - const or_options_t *options = get_options(); - struct timeval now; - cpath_build_state_t *build_state; - int any_opened_circs = 0; - - tor_gettimeofday(&now); - - /* Check to see if we have any opened circuits. If we don't, - * we want to be more lenient with timeouts, in case the - * user has relocated and/or changed network connections. - * See bug #3443. */ - TOR_LIST_FOREACH(next_circ, circuit_get_global_list(), head) { - if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */ - next_circ->marked_for_close) { /* don't mess with marked circs */ - continue; - } - - if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened && - next_circ->state == CIRCUIT_STATE_OPEN && - TO_ORIGIN_CIRCUIT(next_circ)->build_state && - TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len - == DEFAULT_ROUTE_LEN) { - any_opened_circs = 1; - break; - } - } - -#define SET_CUTOFF(target, msec) do { \ - long ms = tor_lround(msec); \ - struct timeval diff; \ - diff.tv_sec = ms / 1000; \ - diff.tv_usec = (int)((ms % 1000) * 1000); \ - timersub(&now, &diff, &target); \ - } while (0) - - /** - * Because circuit build timeout is calculated only based on 3 hop - * general purpose circuit construction, we need to scale the timeout - * to make it properly apply to longer circuits, and circuits of - * certain usage types. The following diagram illustrates how we - * derive the scaling below. In short, we calculate the number - * of times our telescoping-based circuit construction causes cells - * to traverse each link for the circuit purpose types in question, - * and then assume each link is equivalent. - * - * OP --a--> A --b--> B --c--> C - * OP --a--> A --b--> B --c--> C --d--> D - * - * Let h = a = b = c = d - * - * Three hops (general_cutoff) - * RTTs = 3a + 2b + c - * RTTs = 6h - * Cannibalized: - * RTTs = a+b+c+d - * RTTs = 4h - * Four hops: - * RTTs = 4a + 3b + 2c + d - * RTTs = 10h - * Client INTRODUCE1+ACK: // XXX: correct? - * RTTs = 5a + 4b + 3c + 2d - * RTTs = 14h - * Server intro: - * RTTs = 4a + 3b + 2c - * RTTs = 9h - */ - SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms()); - SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms()); - - /* > 3hop circs seem to have a 1.0 second delay on their cannibalized - * 4th hop. */ - SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000); - - /* CIRCUIT_PURPOSE_C_ESTABLISH_REND behaves more like a RELAY cell. - * Use the stream cutoff (more or less). */ - SET_CUTOFF(stream_cutoff, MAX(options->CircuitStreamTimeout,15)*1000 + 1000); - - /* Be lenient with cannibalized circs. They already survived the official - * CBT, and they're usually not performance-critical. */ - SET_CUTOFF(cannibalized_cutoff, - MAX(get_circuit_build_close_time_ms()*(4/6.0), - options->CircuitStreamTimeout * 1000) + 1000); - - /* Intro circs have an extra round trip (and are also 4 hops long) */ - SET_CUTOFF(c_intro_cutoff, get_circuit_build_timeout_ms() * (14/6.0) + 1000); - - /* Server intro circs have an extra round trip */ - SET_CUTOFF(s_intro_cutoff, get_circuit_build_timeout_ms() * (9/6.0) + 1000); - - SET_CUTOFF(close_cutoff, get_circuit_build_close_time_ms()); - SET_CUTOFF(extremely_old_cutoff, get_circuit_build_close_time_ms()*2 + 1000); - - SET_CUTOFF(hs_extremely_old_cutoff, - MAX(get_circuit_build_close_time_ms()*2 + 1000, - options->SocksTimeout * 1000)); - - TOR_LIST_FOREACH(next_circ, circuit_get_global_list(), head) { - struct timeval cutoff; - victim = next_circ; - if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */ - victim->marked_for_close) /* don't mess with marked circs */ - continue; - - /* If we haven't yet started the first hop, it means we don't have - * any orconns available, and thus have not started counting time yet - * for this circuit. See circuit_deliver_create_cell() and uses of - * timestamp_began. - * - * Continue to wait in this case. The ORConn should timeout - * independently and kill us then. - */ - if (TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_CLOSED) { - continue; - } - - build_state = TO_ORIGIN_CIRCUIT(victim)->build_state; - if (build_state && build_state->onehop_tunnel) - cutoff = begindir_cutoff; - else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) - cutoff = close_cutoff; - else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING || - victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) - cutoff = c_intro_cutoff; - else if (victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) - cutoff = s_intro_cutoff; - else if (victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND) - cutoff = stream_cutoff; - else if (victim->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) - cutoff = close_cutoff; - else if (TO_ORIGIN_CIRCUIT(victim)->has_opened && - victim->state != CIRCUIT_STATE_OPEN) - cutoff = cannibalized_cutoff; - else if (build_state && build_state->desired_path_len >= 4) - cutoff = fourhop_cutoff; - else - cutoff = general_cutoff; - - if (TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out) - cutoff = hs_extremely_old_cutoff; - - if (timercmp(&victim->timestamp_began, &cutoff, >)) - continue; /* it's still young, leave it alone */ - - /* We need to double-check the opened state here because - * we don't want to consider opened 1-hop dircon circuits for - * deciding when to relax the timeout, but we *do* want to relax - * those circuits too if nothing else is opened *and* they still - * aren't either. */ - if (!any_opened_circs && victim->state != CIRCUIT_STATE_OPEN) { - /* It's still young enough that we wouldn't close it, right? */ - if (timercmp(&victim->timestamp_began, &close_cutoff, >)) { - if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) { - int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath->state - == CPATH_STATE_OPEN; - log_info(LD_CIRC, - "No circuits are opened. Relaxing timeout for circuit %d " - "(a %s %d-hop circuit in state %s with channel state %s). " - "%d guards are live.", - TO_ORIGIN_CIRCUIT(victim)->global_identifier, - circuit_purpose_to_string(victim->purpose), - TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len, - circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), - num_live_entry_guards(0)); - - /* We count the timeout here for CBT, because technically this - * was a timeout, and the timeout value needs to reset if we - * see enough of them. Note this means we also need to avoid - * double-counting below, too. */ - circuit_build_times_count_timeout(get_circuit_build_times_mutable(), - first_hop_succeeded); - TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout = 1; - } - continue; - } else { - static ratelim_t relax_timeout_limit = RATELIM_INIT(3600); - const double build_close_ms = get_circuit_build_close_time_ms(); - log_fn_ratelim(&relax_timeout_limit, LOG_NOTICE, LD_CIRC, - "No circuits are opened. Relaxed timeout for circuit %d " - "(a %s %d-hop circuit in state %s with channel state %s) to " - "%ldms. However, it appears the circuit has timed out " - "anyway. %d guards are live.", - TO_ORIGIN_CIRCUIT(victim)->global_identifier, - circuit_purpose_to_string(victim->purpose), - TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len, - circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), - (long)build_close_ms, - num_live_entry_guards(0)); - } - } - -#if 0 - /* some debug logs, to help track bugs */ - if (victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING && - victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - if (!victim->timestamp_dirty) - log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d)." - "(clean).", - victim->state == CIRCUIT_STATE_OPEN ? "" : "non", - victim->purpose, victim->build_state->chosen_exit_name, - victim->n_circ_id); - else - log_fn(LOG_DEBUG,"Considering %sopen purpose %d to %s (circid %d). " - "%d secs since dirty.", - victim->state == CIRCUIT_STATE_OPEN ? "" : "non", - victim->purpose, victim->build_state->chosen_exit_name, - victim->n_circ_id, - (int)(now - victim->timestamp_dirty)); - } -#endif - - /* if circ is !open, or if it's open but purpose is a non-finished - * intro or rend, then mark it for close */ - if (victim->state == CIRCUIT_STATE_OPEN) { - switch (victim->purpose) { - default: /* most open circuits can be left alone. */ - continue; /* yes, continue inside a switch refers to the nearest - * enclosing loop. C is smart. */ - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - break; /* too old, need to die */ - case CIRCUIT_PURPOSE_C_REND_READY: - /* it's a rend_ready circ -- has it already picked a query? */ - /* c_rend_ready circs measure age since timestamp_dirty, - * because that's set when they switch purposes - */ - if (TO_ORIGIN_CIRCUIT(victim)->rend_data || - victim->timestamp_dirty > cutoff.tv_sec) - continue; - break; - case CIRCUIT_PURPOSE_PATH_BIAS_TESTING: - /* Open path bias testing circuits are given a long - * time to complete the test, but not forever */ - TO_ORIGIN_CIRCUIT(victim)->path_state = PATH_STATE_USE_FAILED; - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - /* We keep old introducing circuits around for - * a while in parallel, and they can end up "opened". - * We decide below if we're going to mark them timed - * out and eventually close them. - */ - break; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - /* rend and intro circs become dirty each time they - * make an introduction attempt. so timestamp_dirty - * will reflect the time since the last attempt. - */ - if (victim->timestamp_dirty > cutoff.tv_sec) - continue; - break; - } - } else { /* circuit not open, consider recording failure as timeout */ - int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath && - TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN; - - if (TO_ORIGIN_CIRCUIT(victim)->p_streams != NULL) { - log_warn(LD_BUG, "Circuit %d (purpose %d, %s) has timed out, " - "yet has attached streams!", - TO_ORIGIN_CIRCUIT(victim)->global_identifier, - victim->purpose, - circuit_purpose_to_string(victim->purpose)); - tor_fragile_assert(); - continue; - } - - if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) && - circuit_build_times_enough_to_compute(get_circuit_build_times())) { - /* Circuits are allowed to last longer for measurement. - * Switch their purpose and wait. */ - if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim), - CIRC_EVENT_FAILED, - END_CIRC_REASON_TIMEOUT); - circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); - /* Record this failure to check for too many timeouts - * in a row. This function does not record a time value yet - * (we do that later); it only counts the fact that we did - * have a timeout. We also want to avoid double-counting - * already "relaxed" circuits, which are counted above. */ - if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) { - circuit_build_times_count_timeout( - get_circuit_build_times_mutable(), - first_hop_succeeded); - } - continue; - } - - /* - * If the circuit build time is much greater than we would have cut - * it off at, we probably had a suspend event along this codepath, - * and we should discard the value. - */ - if (timercmp(&victim->timestamp_began, &extremely_old_cutoff, <)) { - log_notice(LD_CIRC, - "Extremely large value for circuit build timeout: %lds. " - "Assuming clock jump. Purpose %d (%s)", - (long)(now.tv_sec - victim->timestamp_began.tv_sec), - victim->purpose, - circuit_purpose_to_string(victim->purpose)); - } else if (circuit_build_times_count_close( - get_circuit_build_times_mutable(), - first_hop_succeeded, - victim->timestamp_created.tv_sec)) { - circuit_build_times_set_timeout(get_circuit_build_times_mutable()); - } - } - } - - /* If this is a hidden service client circuit which is far enough - * along in connecting to its destination, and we haven't already - * flagged it as 'timed out', and the user has not told us to - * close such circs immediately on timeout, flag it as 'timed out' - * so we'll launch another intro or rend circ, but don't mark it - * for close yet. - * - * (Circs flagged as 'timed out' are given a much longer timeout - * period above, so we won't close them in the next call to - * circuit_expire_building.) */ - if (!(options->CloseHSClientCircuitsImmediatelyOnTimeout) && - !(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)) { - switch (victim->purpose) { - case CIRCUIT_PURPOSE_C_REND_READY: - /* We only want to spare a rend circ if it has been specified in - * an INTRODUCE1 cell sent to a hidden service. A circ's - * pending_final_cpath field is non-NULL iff it is a rend circ - * and we have tried to send an INTRODUCE1 cell specifying it. - * Thus, if the pending_final_cpath field *is* NULL, then we - * want to not spare it. */ - if (TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath == - NULL) - break; - /* fallthrough! */ - case CIRCUIT_PURPOSE_C_INTRODUCING: - /* connection_ap_handshake_attach_circuit() will relaunch for us */ - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: - /* If we have reached this line, we want to spare the circ for now. */ - log_info(LD_CIRC,"Marking circ %u (state %d:%s, purpose %d) " - "as timed-out HS circ", - (unsigned)victim->n_circ_id, - victim->state, circuit_state_to_string(victim->state), - victim->purpose); - TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1; - continue; - default: - break; - } - } - - /* If this is a service-side rendezvous circuit which is far - * enough along in connecting to its destination, consider sparing - * it. */ - if (!(options->CloseHSServiceRendCircuitsImmediatelyOnTimeout) && - !(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out) && - victim->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) { - log_info(LD_CIRC,"Marking circ %u (state %d:%s, purpose %d) " - "as timed-out HS circ; relaunching rendezvous attempt.", - (unsigned)victim->n_circ_id, - victim->state, circuit_state_to_string(victim->state), - victim->purpose); - TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1; - rend_service_relaunch_rendezvous(TO_ORIGIN_CIRCUIT(victim)); - continue; - } - - if (victim->n_chan) - log_info(LD_CIRC, - "Abandoning circ %u %s:%u (state %d,%d:%s, purpose %d, " - "len %d)", TO_ORIGIN_CIRCUIT(victim)->global_identifier, - channel_get_canonical_remote_descr(victim->n_chan), - (unsigned)victim->n_circ_id, - TO_ORIGIN_CIRCUIT(victim)->has_opened, - victim->state, circuit_state_to_string(victim->state), - victim->purpose, - TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len); - else - log_info(LD_CIRC, - "Abandoning circ %u %u (state %d,%d:%s, purpose %d, len %d)", - TO_ORIGIN_CIRCUIT(victim)->global_identifier, - (unsigned)victim->n_circ_id, - TO_ORIGIN_CIRCUIT(victim)->has_opened, - victim->state, - circuit_state_to_string(victim->state), victim->purpose, - TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len); - - circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim)); - if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) - circuit_mark_for_close(victim, END_CIRC_REASON_MEASUREMENT_EXPIRED); - else - circuit_mark_for_close(victim, END_CIRC_REASON_TIMEOUT); - - pathbias_count_timeout(TO_ORIGIN_CIRCUIT(victim)); - } -} - -/** Remove any elements in needed_ports that are handled by an - * open or in-progress circuit. - */ -void -circuit_remove_handled_ports(smartlist_t *needed_ports) -{ - int i; - uint16_t *port; - - for (i = 0; i < smartlist_len(needed_ports); ++i) { - port = smartlist_get(needed_ports, i); - tor_assert(*port); - if (circuit_stream_is_being_handled(NULL, *port, - MIN_CIRCUITS_HANDLING_STREAM)) { -// log_debug(LD_CIRC,"Port %d is already being handled; removing.", port); - smartlist_del(needed_ports, i--); - tor_free(port); - } else { - log_debug(LD_CIRC,"Port %d is not handled.", *port); - } - } -} - -/** Return 1 if at least min general-purpose non-internal circuits - * will have an acceptable exit node for exit stream conn if it - * is defined, else for "*:port". - * Else return 0. - */ -int -circuit_stream_is_being_handled(entry_connection_t *conn, - uint16_t port, int min) -{ - circuit_t *circ; - const node_t *exitnode; - int num=0; - time_t now = time(NULL); - int need_uptime = smartlist_contains_int_as_string( - get_options()->LongLivedPorts, - conn ? conn->socks_request->port : port); - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (CIRCUIT_IS_ORIGIN(circ) && - !circ->marked_for_close && - circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && - (!circ->timestamp_dirty || - circ->timestamp_dirty + get_options()->MaxCircuitDirtiness > now)) { - origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); - cpath_build_state_t *build_state = origin_circ->build_state; - if (build_state->is_internal || build_state->onehop_tunnel) - continue; - if (origin_circ->unusable_for_new_conns) - continue; - - exitnode = build_state_get_exit_node(build_state); - if (exitnode && (!need_uptime || build_state->need_uptime)) { - int ok; - if (conn) { - ok = connection_ap_can_use_exit(conn, exitnode); - } else { - addr_policy_result_t r; - r = compare_tor_addr_to_node_policy(NULL, port, exitnode); - ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED; - } - if (ok) { - if (++num >= min) - return 1; - } - } - } - } - return 0; -} - -/** Don't keep more than this many unused open circuits around. */ -#define MAX_UNUSED_OPEN_CIRCUITS 14 - -/** Figure out how many circuits we have open that are clean. Make - * sure it's enough for all the upcoming behaviors we predict we'll have. - * But put an upper bound on the total number of circuits. - */ -static void -circuit_predict_and_launch_new(void) -{ - circuit_t *circ; - int num=0, num_internal=0, num_uptime_internal=0; - int hidserv_needs_uptime=0, hidserv_needs_capacity=1; - int port_needs_uptime=0, port_needs_capacity=1; - time_t now = time(NULL); - int flags = 0; - - /* First, count how many of each type of circuit we have already. */ - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - cpath_build_state_t *build_state; - origin_circuit_t *origin_circ; - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - if (circ->marked_for_close) - continue; /* don't mess with marked circs */ - if (circ->timestamp_dirty) - continue; /* only count clean circs */ - if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) - continue; /* only pay attention to general-purpose circs */ - origin_circ = TO_ORIGIN_CIRCUIT(circ); - if (origin_circ->unusable_for_new_conns) - continue; - build_state = origin_circ->build_state; - if (build_state->onehop_tunnel) - continue; - num++; - if (build_state->is_internal) - num_internal++; - if (build_state->need_uptime && build_state->is_internal) - num_uptime_internal++; - } - - /* If that's enough, then stop now. */ - if (num >= MAX_UNUSED_OPEN_CIRCUITS) - return; /* we already have many, making more probably will hurt */ - - /* Second, see if we need any more exit circuits. */ - /* check if we know of a port that's been requested recently - * and no circuit is currently available that can handle it. */ - if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime, - &port_needs_capacity)) { - if (port_needs_uptime) - flags |= CIRCLAUNCH_NEED_UPTIME; - if (port_needs_capacity) - flags |= CIRCLAUNCH_NEED_CAPACITY; - log_info(LD_CIRC, - "Have %d clean circs (%d internal), need another exit circ.", - num, num_internal); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); - return; - } - - /* Third, see if we need any more hidden service (server) circuits. */ - if (num_rend_services() && num_uptime_internal < 3) { - flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME | - CIRCLAUNCH_IS_INTERNAL); - log_info(LD_CIRC, - "Have %d clean circs (%d internal), need another internal " - "circ for my hidden service.", - num, num_internal); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); - return; - } - - /* Fourth, see if we need any more hidden service (client) circuits. */ - if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime, - &hidserv_needs_capacity) && - ((num_uptime_internal<2 && hidserv_needs_uptime) || - num_internal<2)) { - if (hidserv_needs_uptime) - flags |= CIRCLAUNCH_NEED_UPTIME; - if (hidserv_needs_capacity) - flags |= CIRCLAUNCH_NEED_CAPACITY; - flags |= CIRCLAUNCH_IS_INTERNAL; - log_info(LD_CIRC, - "Have %d clean circs (%d uptime-internal, %d internal), need" - " another hidden service circ.", - num, num_uptime_internal, num_internal); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); - return; - } - - /* Finally, check to see if we still need more circuits to learn - * a good build timeout. But if we're close to our max number we - * want, don't do another -- we want to leave a few slots open so - * we can still build circuits preemptively as needed. */ - if (num < MAX_UNUSED_OPEN_CIRCUITS-2 && - ! circuit_build_times_disabled() && - circuit_build_times_needs_circuits_now(get_circuit_build_times())) { - flags = CIRCLAUNCH_NEED_CAPACITY; - log_info(LD_CIRC, - "Have %d clean circs need another buildtime test circ.", num); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); - return; - } -} - -/** Build a new test circuit every 5 minutes */ -#define TESTING_CIRCUIT_INTERVAL 300 - -/** This function is called once a second, if router_have_min_dir_info() is - * true. Its job is to make sure all services we offer have enough circuits - * available. Some services just want enough circuits for current tasks, - * whereas others want a minimum set of idle circuits hanging around. - */ -void -circuit_build_needed_circs(time_t now) -{ - static time_t time_to_new_circuit = 0; - const or_options_t *options = get_options(); - - /* launch a new circ for any pending streams that need one */ - connection_ap_attach_pending(); - - /* make sure any hidden services have enough intro points */ - rend_services_introduce(); - - if (time_to_new_circuit < now) { - circuit_reset_failure_count(1); - time_to_new_circuit = now + options->NewCircuitPeriod; - if (proxy_mode(get_options())) - addressmap_clean(now); - circuit_expire_old_circuits_clientside(); - -#if 0 /* disable for now, until predict-and-launch-new can cull leftovers */ - circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL); - if (get_options()->RunTesting && - circ && - circ->timestamp_began.tv_sec + TESTING_CIRCUIT_INTERVAL < now) { - log_fn(LOG_INFO,"Creating a new testing circuit."); - circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0); - } -#endif - } - if (!options->DisablePredictedCircuits) - circuit_predict_and_launch_new(); -} - -/** If the stream conn is a member of any of the linked - * lists of circ, then remove it from the list. - */ -void -circuit_detach_stream(circuit_t *circ, edge_connection_t *conn) -{ - edge_connection_t *prevconn; - - tor_assert(circ); - tor_assert(conn); - - if (conn->base_.type == CONN_TYPE_AP) { - entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn); - entry_conn->may_use_optimistic_data = 0; - } - conn->cpath_layer = NULL; /* don't keep a stale pointer */ - conn->on_circuit = NULL; - - if (CIRCUIT_IS_ORIGIN(circ)) { - origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); - if (conn == origin_circ->p_streams) { - origin_circ->p_streams = conn->next_stream; - return; - } - - for (prevconn = origin_circ->p_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if (prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - } else { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (conn == or_circ->n_streams) { - or_circ->n_streams = conn->next_stream; - return; - } - if (conn == or_circ->resolving_streams) { - or_circ->resolving_streams = conn->next_stream; - return; - } - - for (prevconn = or_circ->n_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if (prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - - for (prevconn = or_circ->resolving_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if (prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - } - - log_warn(LD_BUG,"Edge connection not in circuit's list."); - /* Don't give an error here; it's harmless. */ - tor_fragile_assert(); -} - -/** If we haven't yet decided on a good timeout value for circuit - * building, we close idles circuits aggressively so we can get more - * data points. */ -#define IDLE_TIMEOUT_WHILE_LEARNING (10*60) - -/** Find each circuit that has been unused for too long, or dirty - * for too long and has no streams on it: mark it for close. - */ -static void -circuit_expire_old_circuits_clientside(void) -{ - circuit_t *circ; - struct timeval cutoff, now; - - tor_gettimeofday(&now); - cutoff = now; - - if (! circuit_build_times_disabled() && - circuit_build_times_needs_circuits(get_circuit_build_times())) { - /* Circuits should be shorter lived if we need more of them - * for learning a good build timeout */ - cutoff.tv_sec -= IDLE_TIMEOUT_WHILE_LEARNING; - } else { - cutoff.tv_sec -= get_options()->CircuitIdleTimeout; - } - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (circ->marked_for_close || !CIRCUIT_IS_ORIGIN(circ)) - continue; - /* If the circuit has been dirty for too long, and there are no streams - * on it, mark it for close. - */ - if (circ->timestamp_dirty && - circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < - now.tv_sec && - !TO_ORIGIN_CIRCUIT(circ)->p_streams /* nothing attached */ ) { - log_debug(LD_CIRC, "Closing n_circ_id %u (dirty %ld sec ago, " - "purpose %d)", - (unsigned)circ->n_circ_id, - (long)(now.tv_sec - circ->timestamp_dirty), - circ->purpose); - /* Don't do this magic for testing circuits. Their death is governed - * by circuit_expire_building */ - if (circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - } else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) { - if (timercmp(&circ->timestamp_began, &cutoff, <)) { - if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL || - circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT || - circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || - circ->purpose == CIRCUIT_PURPOSE_TESTING || - (circ->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING && - circ->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) || - circ->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) { - log_debug(LD_CIRC, - "Closing circuit that has been unused for %ld msec.", - tv_mdiff(&circ->timestamp_began, &now)); - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - } else if (!TO_ORIGIN_CIRCUIT(circ)->is_ancient) { - /* Server-side rend joined circuits can end up really old, because - * they are reused by clients for longer than normal. The client - * controls their lifespan. (They never become dirty, because - * connection_exit_begin_conn() never marks anything as dirty.) - * Similarly, server-side intro circuits last a long time. */ - if (circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED && - circ->purpose != CIRCUIT_PURPOSE_S_INTRO) { - log_notice(LD_CIRC, - "Ancient non-dirty circuit %d is still around after " - "%ld milliseconds. Purpose: %d (%s)", - TO_ORIGIN_CIRCUIT(circ)->global_identifier, - tv_mdiff(&circ->timestamp_began, &now), - circ->purpose, - circuit_purpose_to_string(circ->purpose)); - TO_ORIGIN_CIRCUIT(circ)->is_ancient = 1; - } - } - } - } - } -} - -/** How long do we wait before killing circuits with the properties - * described below? - * - * Probably we could choose a number here as low as 5 to 10 seconds, - * since these circs are used for begindir, and a) generally you either - * ask another begindir question right after or you don't for a long time, - * b) clients at least through 0.2.1.x choose from the whole set of - * directory mirrors at each choice, and c) re-establishing a one-hop - * circuit via create-fast is a light operation assuming the TLS conn is - * still there. - * - * I expect "b" to go away one day when we move to using directory - * guards, but I think "a" and "c" are good enough reasons that a low - * number is safe even then. - */ -#define IDLE_ONE_HOP_CIRC_TIMEOUT 60 - -/** Find each non-origin circuit that has been unused for too long, - * has no streams on it, used a create_fast, and ends here: mark it - * for close. - */ -void -circuit_expire_old_circuits_serverside(time_t now) -{ - circuit_t *circ; - or_circuit_t *or_circ; - time_t cutoff = now - IDLE_ONE_HOP_CIRC_TIMEOUT; - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (circ->marked_for_close || CIRCUIT_IS_ORIGIN(circ)) - continue; - or_circ = TO_OR_CIRCUIT(circ); - /* If the circuit has been idle for too long, and there are no streams - * on it, and it ends here, and it used a create_fast, mark it for close. - */ - if (or_circ->is_first_hop && !circ->n_chan && - !or_circ->n_streams && !or_circ->resolving_streams && - or_circ->p_chan && - channel_when_last_xmit(or_circ->p_chan) <= cutoff) { - log_info(LD_CIRC, "Closing circ_id %u (empty %d secs ago)", - (unsigned)or_circ->p_circ_id, - (int)(now - channel_when_last_xmit(or_circ->p_chan))); - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - } - } -} - -/** Number of testing circuits we want open before testing our bandwidth. */ -#define NUM_PARALLEL_TESTING_CIRCS 4 - -/** True iff we've ever had enough testing circuits open to test our - * bandwidth. */ -static int have_performed_bandwidth_test = 0; - -/** Reset have_performed_bandwidth_test, so we'll start building - * testing circuits again so we can exercise our bandwidth. */ -void -reset_bandwidth_test(void) -{ - have_performed_bandwidth_test = 0; -} - -/** Return 1 if we've already exercised our bandwidth, or if we - * have fewer than NUM_PARALLEL_TESTING_CIRCS testing circuits - * established or on the way. Else return 0. - */ -int -circuit_enough_testing_circs(void) -{ - circuit_t *circ; - int num = 0; - - if (have_performed_bandwidth_test) - return 1; - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (!circ->marked_for_close && CIRCUIT_IS_ORIGIN(circ) && - circ->purpose == CIRCUIT_PURPOSE_TESTING && - circ->state == CIRCUIT_STATE_OPEN) - num++; - } - return num >= NUM_PARALLEL_TESTING_CIRCS; -} - -/** A testing circuit has completed. Take whatever stats we want. - * Noticing reachability is taken care of in onionskin_answer(), - * so there's no need to record anything here. But if we still want - * to do the bandwidth test, and we now have enough testing circuits - * open, do it. - */ -static void -circuit_testing_opened(origin_circuit_t *circ) -{ - if (have_performed_bandwidth_test || - !check_whether_orport_reachable()) { - /* either we've already done everything we want with testing circuits, - * or this testing circuit became open due to a fluke, e.g. we picked - * a last hop where we already had the connection open due to an - * outgoing local circuit. */ - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN); - } else if (circuit_enough_testing_circs()) { - router_perform_bandwidth_test(NUM_PARALLEL_TESTING_CIRCS, time(NULL)); - have_performed_bandwidth_test = 1; - } else - consider_testing_reachability(1, 0); -} - -/** A testing circuit has failed to build. Take whatever stats we want. */ -static void -circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) -{ - if (server_mode(get_options()) && check_whether_orport_reachable()) - return; - - log_info(LD_GENERAL, - "Our testing circuit (to see if your ORPort is reachable) " - "has failed. I'll try again later."); - - /* These aren't used yet. */ - (void)circ; - (void)at_last_hop; -} - -/** The circuit circ has just become open. Take the next - * step: for rendezvous circuits, we pass circ to the appropriate - * function in rendclient or rendservice. For general circuits, we - * call connection_ap_attach_pending, which looks for pending streams - * that could use circ. - */ -void -circuit_has_opened(origin_circuit_t *circ) -{ - control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0); - - /* Remember that this circuit has finished building. Now if we start - * it building again later (e.g. by extending it), we will know not - * to consider its build time. */ - circ->has_opened = 1; - - switch (TO_CIRCUIT(circ)->purpose) { - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - rend_client_rendcirc_has_opened(circ); - /* Start building an intro circ if we don't have one yet. */ - connection_ap_attach_pending(); - /* This isn't a call to circuit_try_attaching_streams because a - * circuit in _C_ESTABLISH_REND state isn't connected to its - * hidden service yet, thus we can't attach streams to it yet, - * thus circuit_try_attaching_streams would always clear the - * circuit's isolation state. circuit_try_attaching_streams is - * called later, when the rend circ enters _C_REND_JOINED - * state. */ - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - rend_client_introcirc_has_opened(circ); - break; - case CIRCUIT_PURPOSE_C_GENERAL: - /* Tell any AP connections that have been waiting for a new - * circuit that one is ready. */ - circuit_try_attaching_streams(circ); - break; - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ - rend_service_intro_has_opened(circ); - break; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ - rend_service_rendezvous_has_opened(circ); - break; - case CIRCUIT_PURPOSE_TESTING: - circuit_testing_opened(circ); - break; - /* default: - * This won't happen in normal operation, but might happen if the - * controller did it. Just let it slide. */ - } -} - -/** If the stream-isolation state of circ can be cleared, clear - * it. Return non-zero iff circ's isolation state was cleared. */ -static int -circuit_try_clearing_isolation_state(origin_circuit_t *circ) -{ - if (/* The circuit may have become non-open if it was cannibalized.*/ - circ->base_.state == CIRCUIT_STATE_OPEN && - /* If !isolation_values_set, there is nothing to clear. */ - circ->isolation_values_set && - /* It's not legal to clear a circuit's isolation info if it's ever had - * streams attached */ - !circ->isolation_any_streams_attached) { - /* If we have any isolation information set on this circuit, and - * we didn't manage to attach any streams to it, then we can - * and should clear it and try again. */ - circuit_clear_isolation(circ); - return 1; - } else { - return 0; - } -} - -/** Called when a circuit becomes ready for streams to be attached to - * it. */ -void -circuit_try_attaching_streams(origin_circuit_t *circ) -{ - /* Attach streams to this circuit if we can. */ - connection_ap_attach_pending(); - - /* The call to circuit_try_clearing_isolation_state here will do - * nothing and return 0 if we didn't attach any streams to circ - * above. */ - if (circuit_try_clearing_isolation_state(circ)) { - /* Maybe *now* we can attach some streams to this circuit. */ - connection_ap_attach_pending(); - } -} - -/** Called whenever a circuit could not be successfully built. - */ -void -circuit_build_failed(origin_circuit_t *circ) -{ - channel_t *n_chan = NULL; - /* we should examine circ and see if it failed because of - * the last hop or an earlier hop. then use this info below. - */ - int failed_at_last_hop = 0; - /* If the last hop isn't open, and the second-to-last is, we failed - * at the last hop. */ - if (circ->cpath && - circ->cpath->prev->state != CPATH_STATE_OPEN && - circ->cpath->prev->prev->state == CPATH_STATE_OPEN) { - failed_at_last_hop = 1; - } - if (circ->cpath && - circ->cpath->state != CPATH_STATE_OPEN && - ! circ->base_.received_destroy) { - /* We failed at the first hop for some reason other than a DESTROY cell. - * If there's an OR connection to blame, blame it. Also, avoid this relay - * for a while, and fail any one-hop directory fetches destined for it. */ - const char *n_chan_id = circ->cpath->extend_info->identity_digest; - int already_marked = 0; - if (circ->base_.n_chan) { - n_chan = circ->base_.n_chan; - - if (n_chan->is_bad_for_new_circs) { - /* We only want to blame this router when a fresh healthy - * connection fails. So don't mark this router as newly failed, - * since maybe this was just an old circuit attempt that's - * finally timing out now. Also, there's no need to blow away - * circuits/streams/etc, since the failure of an unhealthy conn - * doesn't tell us much about whether a healthy conn would - * succeed. */ - already_marked = 1; - } - log_info(LD_OR, - "Our circuit failed to get a response from the first hop " - "(%s). I'm going to try to rotate to a better connection.", - channel_get_canonical_remote_descr(n_chan)); - n_chan->is_bad_for_new_circs = 1; - } else { - log_info(LD_OR, - "Our circuit died before the first hop with no connection"); - } - if (n_chan_id && !already_marked) { - entry_guard_register_connect_status(n_chan_id, 0, 1, time(NULL)); - /* if there are any one-hop streams waiting on this circuit, fail - * them now so they can retry elsewhere. */ - connection_ap_fail_onehop(n_chan_id, circ->build_state); - } - } - - switch (circ->base_.purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - /* If we never built the circuit, note it as a failure. */ - circuit_increment_failure_count(); - if (failed_at_last_hop) { - /* Make sure any streams that demand our last hop as their exit - * know that it's unlikely to happen. */ - circuit_discard_optional_exit_enclaves(circ->cpath->prev->extend_info); - } - break; - case CIRCUIT_PURPOSE_TESTING: - circuit_testing_failed(circ, failed_at_last_hop); - break; - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ - if (circ->base_.state != CIRCUIT_STATE_OPEN) { - circuit_increment_failure_count(); - } - /* no need to care here, because bob will rebuild intro - * points periodically. */ - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - /* at Alice, connecting to intro point */ - /* Don't increment failure count, since Bob may have picked - * the introduction point maliciously */ - /* Alice will pick a new intro point when this one dies, if - * the stream in question still cares. No need to act here. */ - break; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - /* at Alice, waiting for Bob */ - circuit_increment_failure_count(); - /* Alice will pick a new rend point when this one dies, if - * the stream in question still cares. No need to act here. */ - break; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ - /* Don't increment failure count, since Alice may have picked - * the rendezvous point maliciously */ - log_info(LD_REND, - "Couldn't connect to Alice's chosen rend point %s " - "(%s hop failed).", - escaped(build_state_get_exit_nickname(circ->build_state)), - failed_at_last_hop?"last":"non-last"); - rend_service_relaunch_rendezvous(circ); - break; - /* default: - * This won't happen in normal operation, but might happen if the - * controller did it. Just let it slide. */ - } -} - -/** Number of consecutive failures so far; should only be touched by - * circuit_launch_new and circuit_*_failure_count. - */ -static int n_circuit_failures = 0; -/** Before the last time we called circuit_reset_failure_count(), were - * there a lot of failures? */ -static int did_circs_fail_last_period = 0; - -/** Don't retry launching a new circuit if we try this many times with no - * success. */ -#define MAX_CIRCUIT_FAILURES 5 - -/** Launch a new circuit; see circuit_launch_by_extend_info() for - * details on arguments. */ -origin_circuit_t * -circuit_launch(uint8_t purpose, int flags) -{ - return circuit_launch_by_extend_info(purpose, NULL, flags); -} - -/** Launch a new circuit with purpose purpose and exit node - * extend_info (or NULL to select a random exit node). If flags - * contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If - * CIRCLAUNCH_NEED_CAPACITY is set, choose among routers with high bandwidth. - * If CIRCLAUNCH_IS_INTERNAL is true, the last hop need not be an exit node. - * If CIRCLAUNCH_ONEHOP_TUNNEL is set, the circuit will have only one hop. - * Return the newly allocated circuit on success, or NULL on failure. */ -origin_circuit_t * -circuit_launch_by_extend_info(uint8_t purpose, - extend_info_t *extend_info, - int flags) -{ - origin_circuit_t *circ; - int onehop_tunnel = (flags & CIRCLAUNCH_ONEHOP_TUNNEL) != 0; - - if (!onehop_tunnel && !router_have_minimum_dir_info()) { - log_debug(LD_CIRC,"Haven't fetched enough directory info yet; canceling " - "circuit launch."); - return NULL; - } - - if ((extend_info || purpose != CIRCUIT_PURPOSE_C_GENERAL) && - purpose != CIRCUIT_PURPOSE_TESTING && !onehop_tunnel) { - /* see if there are appropriate circs available to cannibalize. */ - /* XXX if we're planning to add a hop, perhaps we want to look for - * internal circs rather than exit circs? -RD */ - circ = circuit_find_to_cannibalize(purpose, extend_info, flags); - if (circ) { - uint8_t old_purpose = circ->base_.purpose; - struct timeval old_timestamp_began; - - log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)", - build_state_get_exit_nickname(circ->build_state), purpose, - circuit_purpose_to_string(purpose)); - - if ((purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || - purpose == CIRCUIT_PURPOSE_C_INTRODUCING) && - circ->path_state == PATH_STATE_BUILD_SUCCEEDED) { - /* Path bias: Cannibalized rends pre-emptively count as a - * successfully built but unused closed circuit. We don't - * wait until the extend (or the close) because the rend - * point could be malicious. - * - * Same deal goes for client side introductions. Clients - * can be manipulated to connect repeatedly to them - * (especially web clients). - * - * If we decide to probe the initial portion of these circs, - * (up to the adversary's final hop), we need to remove this, - * or somehow mark the circuit with a special path state. - */ - - /* This must be called before the purpose change */ - pathbias_check_close(circ, END_CIRC_REASON_FINISHED); - } - - circuit_change_purpose(TO_CIRCUIT(circ), purpose); - /* Reset the start date of this circ, else expire_building - * will see it and think it's been trying to build since it - * began. - * - * Technically, the code should reset this when the - * create cell is finally sent, but we're close enough - * here. */ - tor_gettimeofday(&circ->base_.timestamp_began); - - control_event_circuit_cannibalized(circ, old_purpose, - &old_timestamp_began); - - switch (purpose) { - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* it's ready right now */ - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - case CIRCUIT_PURPOSE_S_CONNECT_REND: - case CIRCUIT_PURPOSE_C_GENERAL: - /* need to add a new hop */ - tor_assert(extend_info); - if (circuit_extend_to_new_exit(circ, extend_info) < 0) - return NULL; - break; - default: - log_warn(LD_BUG, - "unexpected purpose %d when cannibalizing a circ.", - purpose); - tor_fragile_assert(); - return NULL; - } - return circ; - } - } - - if (did_circs_fail_last_period && - n_circuit_failures > MAX_CIRCUIT_FAILURES) { - /* too many failed circs in a row. don't try. */ -// log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures); - return NULL; - } - - /* try a circ. if it fails, circuit_mark_for_close will increment - * n_circuit_failures */ - return circuit_establish_circuit(purpose, extend_info, flags); -} - -/** Record another failure at opening a general circuit. When we have - * too many, we'll stop trying for the remainder of this minute. - */ -static void -circuit_increment_failure_count(void) -{ - ++n_circuit_failures; - log_debug(LD_CIRC,"n_circuit_failures now %d.",n_circuit_failures); -} - -/** Reset the failure count for opening general circuits. This means - * we will try MAX_CIRCUIT_FAILURES times more (if necessary) before - * stopping again. - */ -void -circuit_reset_failure_count(int timeout) -{ - if (timeout && n_circuit_failures > MAX_CIRCUIT_FAILURES) - did_circs_fail_last_period = 1; - else - did_circs_fail_last_period = 0; - n_circuit_failures = 0; -} - -/** Find an open circ that we're happy to use for conn and return 1. If - * there isn't one, and there isn't one on the way, launch one and return - * 0. If it will never work, return -1. - * - * Write the found or in-progress or launched circ into *circp. - */ -static int -circuit_get_open_circ_or_launch(entry_connection_t *conn, - uint8_t desired_circuit_purpose, - origin_circuit_t **circp) -{ - origin_circuit_t *circ; - int check_exit_policy; - int need_uptime, need_internal; - int want_onehop; - const or_options_t *options = get_options(); - - tor_assert(conn); - tor_assert(circp); - tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT); - check_exit_policy = - conn->socks_request->command == SOCKS_COMMAND_CONNECT && - !conn->use_begindir && - !connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn)); - want_onehop = conn->want_onehop; - - need_uptime = !conn->want_onehop && !conn->use_begindir && - smartlist_contains_int_as_string(options->LongLivedPorts, - conn->socks_request->port); - - if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) - need_internal = 1; - else if (conn->use_begindir || conn->want_onehop) - need_internal = 1; - else - need_internal = 0; - - circ = circuit_get_best(conn, 1, desired_circuit_purpose, - need_uptime, need_internal); - - if (circ) { - *circp = circ; - return 1; /* we're happy */ - } - - if (!want_onehop && !router_have_minimum_dir_info()) { - if (!connection_get_by_type(CONN_TYPE_DIR)) { - int severity = LOG_NOTICE; - /* FFFF if this is a tunneled directory fetch, don't yell - * as loudly. the user doesn't even know it's happening. */ - if (entry_list_is_constrained(options) && - entries_known_but_down(options)) { - log_fn(severity, LD_APP|LD_DIR, - "Application request when we haven't used client functionality " - "lately. Optimistically trying known %s again.", - options->UseBridges ? "bridges" : "entrynodes"); - entries_retry_all(options); - } else if (!options->UseBridges || any_bridge_descriptors_known()) { - log_fn(severity, LD_APP|LD_DIR, - "Application request when we haven't used client functionality " - "lately. Optimistically trying directory fetches again."); - routerlist_retry_directory_downloads(time(NULL)); - } - } - /* the stream will be dealt with when router_have_minimum_dir_info becomes - * 1, or when all directory attempts fail and directory_all_unreachable() - * kills it. - */ - return 0; - } - - /* Do we need to check exit policy? */ - if (check_exit_policy) { - if (!conn->chosen_exit_name) { - struct in_addr in; - tor_addr_t addr, *addrp=NULL; - if (tor_inet_aton(conn->socks_request->address, &in)) { - tor_addr_from_in(&addr, &in); - addrp = &addr; - } - if (router_exit_policy_all_nodes_reject(addrp, - conn->socks_request->port, - need_uptime)) { - log_notice(LD_APP, - "No Tor server allows exit to %s:%d. Rejecting.", - safe_str_client(conn->socks_request->address), - conn->socks_request->port); - return -1; - } - } else { - /* XXXX024 Duplicates checks in connection_ap_handshake_attach_circuit: - * refactor into a single function? */ - const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); - int opt = conn->chosen_exit_optional; - if (node && !connection_ap_can_use_exit(conn, node)) { - log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' is excluded or " - "would refuse request. %s.", - conn->chosen_exit_name, opt ? "Trying others" : "Closing"); - if (opt) { - conn->chosen_exit_optional = 0; - tor_free(conn->chosen_exit_name); - /* Try again. */ - return circuit_get_open_circ_or_launch(conn, - desired_circuit_purpose, - circp); - } - return -1; - } - } - } - - /* is one already on the way? */ - circ = circuit_get_best(conn, 0, desired_circuit_purpose, - need_uptime, need_internal); - if (circ) - log_debug(LD_CIRC, "one on the way!"); - if (!circ) { - extend_info_t *extend_info=NULL; - uint8_t new_circ_purpose; - const int n_pending = count_pending_general_client_circuits(); - - if (n_pending >= options->MaxClientCircuitsPending) { - static ratelim_t delay_limit = RATELIM_INIT(10*60); - char *m; - if ((m = rate_limit_log(&delay_limit, approx_time()))) { - log_notice(LD_APP, "We'd like to launch a circuit to handle a " - "connection, but we already have %d general-purpose client " - "circuits pending. Waiting until some finish.%s", - n_pending, m); - tor_free(m); - } - return 0; - } - - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { - /* need to pick an intro point */ - rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data; - tor_assert(rend_data); - extend_info = rend_client_get_random_intro(rend_data); - if (!extend_info) { - log_info(LD_REND, - "No intro points for '%s': re-fetching service descriptor.", - safe_str_client(rend_data->onion_address)); - rend_client_refetch_v2_renddesc(rend_data); - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT; - return 0; - } - log_info(LD_REND,"Chose %s as intro point for '%s'.", - extend_info_describe(extend_info), - safe_str_client(rend_data->onion_address)); - } - - /* If we have specified a particular exit node for our - * connection, then be sure to open a circuit to that exit node. - */ - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { - if (conn->chosen_exit_name) { - const node_t *r; - int opt = conn->chosen_exit_optional; - r = node_get_by_nickname(conn->chosen_exit_name, 1); - if (r && node_has_descriptor(r)) { - /* We might want to connect to an IPv6 bridge for loading - descriptors so we use the preferred address rather than - the primary. */ - extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0); - } else { - log_debug(LD_DIR, "considering %d, %s", - want_onehop, conn->chosen_exit_name); - if (want_onehop && conn->chosen_exit_name[0] == '$') { - /* We're asking for a one-hop circuit to a router that - * we don't have a routerinfo about. Make up an extend_info. */ - char digest[DIGEST_LEN]; - char *hexdigest = conn->chosen_exit_name+1; - tor_addr_t addr; - if (strlen(hexdigest) < HEX_DIGEST_LEN || - base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN)<0) { - log_info(LD_DIR, "Broken exit digest on tunnel conn. Closing."); - return -1; - } - if (tor_addr_parse(&addr, conn->socks_request->address) < 0) { - log_info(LD_DIR, "Broken address %s on tunnel conn. Closing.", - escaped_safe_str_client(conn->socks_request->address)); - return -1; - } - extend_info = extend_info_new(conn->chosen_exit_name+1, - digest, NULL, NULL, &addr, - conn->socks_request->port); - } else { - /* We will need an onion key for the router, and we - * don't have one. Refuse or relax requirements. */ - log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' is not known. %s.", - conn->chosen_exit_name, opt ? "Trying others" : "Closing"); - if (opt) { - conn->chosen_exit_optional = 0; - tor_free(conn->chosen_exit_name); - /* Try again with no requested exit */ - return circuit_get_open_circ_or_launch(conn, - desired_circuit_purpose, - circp); - } - return -1; - } - } - } - } - - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED) - new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; - else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) - new_circ_purpose = CIRCUIT_PURPOSE_C_INTRODUCING; - else - new_circ_purpose = desired_circuit_purpose; - - if (options->Tor2webMode && - (new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || - new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) { - want_onehop = 1; - } - - { - int flags = CIRCLAUNCH_NEED_CAPACITY; - if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL; - if (need_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; - if (need_internal) flags |= CIRCLAUNCH_IS_INTERNAL; - circ = circuit_launch_by_extend_info(new_circ_purpose, extend_info, - flags); - } - - extend_info_free(extend_info); - - if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { - /* We just caused a circuit to get built because of this stream. - * If this stream has caused a _lot_ of circuits to be built, that's - * a bad sign: we should tell the user. */ - if (conn->num_circuits_launched < NUM_CIRCUITS_LAUNCHED_THRESHOLD && - ++conn->num_circuits_launched == NUM_CIRCUITS_LAUNCHED_THRESHOLD) - log_info(LD_CIRC, "The application request to %s:%d has launched " - "%d circuits without finding one it likes.", - escaped_safe_str_client(conn->socks_request->address), - conn->socks_request->port, - conn->num_circuits_launched); - } else { - /* help predict this next time */ - rep_hist_note_used_internal(time(NULL), need_uptime, 1); - if (circ) { - /* write the service_id into circ */ - circ->rend_data = rend_data_dup(ENTRY_TO_EDGE_CONN(conn)->rend_data); - if (circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND && - circ->base_.state == CIRCUIT_STATE_OPEN) - rend_client_rendcirc_has_opened(circ); - } - } - } /* endif (!circ) */ - if (circ) { - /* Mark the circuit with the isolation fields for this connection. - * When the circuit arrives, we'll clear these flags: this is - * just some internal bookkeeping to make sure that we have - * launched enough circuits. - */ - connection_edge_update_circuit_isolation(conn, circ, 0); - } else { - log_info(LD_APP, - "No safe circuit (purpose %d) ready for edge " - "connection; delaying.", - desired_circuit_purpose); - } - *circp = circ; - return 0; -} - -/** Return true iff crypt_path is one of the crypt_paths for - * circ. */ -static int -cpath_is_on_circuit(origin_circuit_t *circ, crypt_path_t *crypt_path) -{ - crypt_path_t *cpath, *cpath_next = NULL; - for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) { - cpath_next = cpath->next; - if (crypt_path == cpath) - return 1; - } - return 0; -} - -/** Return true iff client-side optimistic data is supported. */ -static int -optimistic_data_enabled(void) -{ - const or_options_t *options = get_options(); - if (options->OptimisticData < 0) { - /* XXX023 consider having auto default to 1 rather than 0 before - * the 0.2.3 branch goes stable. See bug 3617. -RD */ - const int32_t enabled = - networkstatus_get_param(NULL, "UseOptimisticData", 0, 0, 1); - return (int)enabled; - } - return options->OptimisticData; -} - -/** Attach the AP stream apconn to circ's linked list of - * p_streams. Also set apconn's cpath_layer to cpath, or to the last - * hop in circ's cpath if cpath is NULL. - */ -static void -link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ, - crypt_path_t *cpath) -{ - const node_t *exitnode; - - /* add it into the linked list of streams on this circuit */ - log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %u.", - (unsigned)circ->base_.n_circ_id); - /* reset it, so we can measure circ timeouts */ - ENTRY_TO_CONN(apconn)->timestamp_lastread = time(NULL); - ENTRY_TO_EDGE_CONN(apconn)->next_stream = circ->p_streams; - ENTRY_TO_EDGE_CONN(apconn)->on_circuit = TO_CIRCUIT(circ); - /* assert_connection_ok(conn, time(NULL)); */ - circ->p_streams = ENTRY_TO_EDGE_CONN(apconn); - - if (connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(apconn))) { - /* We are attaching a stream to a rendezvous circuit. That means - * that an attempt to connect to a hidden service just - * succeeded. Tell rendclient.c. */ - rend_client_note_connection_attempt_ended( - ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address); - } - - if (cpath) { /* we were given one; use it */ - tor_assert(cpath_is_on_circuit(circ, cpath)); - } else { - /* use the last hop in the circuit */ - tor_assert(circ->cpath); - tor_assert(circ->cpath->prev); - tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN); - cpath = circ->cpath->prev; - } - ENTRY_TO_EDGE_CONN(apconn)->cpath_layer = cpath; - - circ->isolation_any_streams_attached = 1; - connection_edge_update_circuit_isolation(apconn, circ, 0); - - /* See if we can use optimistic data on this circuit */ - if (cpath->extend_info && - (exitnode = node_get_by_id(cpath->extend_info->identity_digest)) && - exitnode->rs) { - /* Okay; we know what exit node this is. */ - if (optimistic_data_enabled() && - circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL && - exitnode->rs->version_supports_optimistic_data) - apconn->may_use_optimistic_data = 1; - else - apconn->may_use_optimistic_data = 0; - log_info(LD_APP, "Looks like completed circuit to %s %s allow " - "optimistic data for connection to %s", - safe_str_client(node_describe(exitnode)), - apconn->may_use_optimistic_data ? "does" : "doesn't", - safe_str_client(apconn->socks_request->address)); - } -} - -/** Return true iff address is matched by one of the entries in - * TrackHostExits. */ -int -hostname_in_track_host_exits(const or_options_t *options, const char *address) -{ - if (!options->TrackHostExits) - return 0; - SMARTLIST_FOREACH_BEGIN(options->TrackHostExits, const char *, cp) { - if (cp[0] == '.') { /* match end */ - if (cp[1] == '\0' || - !strcasecmpend(address, cp) || - !strcasecmp(address, &cp[1])) - return 1; - } else if (strcasecmp(cp, address) == 0) { - return 1; - } - } SMARTLIST_FOREACH_END(cp); - return 0; -} - -/** If an exit wasn't explicitly specified for conn, consider saving - * the exit that we *did* choose for use by future connections to - * conn's destination. - */ -static void -consider_recording_trackhost(const entry_connection_t *conn, - const origin_circuit_t *circ) -{ - const or_options_t *options = get_options(); - char *new_address = NULL; - char fp[HEX_DIGEST_LEN+1]; - - /* Search the addressmap for this conn's destination. */ - /* If he's not in the address map.. */ - if (!options->TrackHostExits || - addressmap_have_mapping(conn->socks_request->address, - options->TrackHostExitsExpire)) - return; /* nothing to track, or already mapped */ - - if (!hostname_in_track_host_exits(options, conn->socks_request->address) || - !circ->build_state->chosen_exit) - return; - - /* write down the fingerprint of the chosen exit, not the nickname, - * because the chosen exit might not be named. */ - base16_encode(fp, sizeof(fp), - circ->build_state->chosen_exit->identity_digest, DIGEST_LEN); - - /* Add this exit/hostname pair to the addressmap. */ - tor_asprintf(&new_address, "%s.%s.exit", - conn->socks_request->address, fp); - - addressmap_register(conn->socks_request->address, new_address, - time(NULL) + options->TrackHostExitsExpire, - ADDRMAPSRC_TRACKEXIT, 0, 0); -} - -/** Attempt to attach the connection conn to circ, and send a - * begin or resolve cell as appropriate. Return values are as for - * connection_ap_handshake_attach_circuit. The stream will exit from the hop - * indicated by cpath, or from the last hop in circ's cpath if - * cpath is NULL. */ -int -connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath) -{ - connection_t *base_conn = ENTRY_TO_CONN(conn); - tor_assert(conn); - tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT || - base_conn->state == AP_CONN_STATE_CONTROLLER_WAIT); - tor_assert(conn->socks_request); - tor_assert(circ); - tor_assert(circ->base_.state == CIRCUIT_STATE_OPEN); - - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - - if (!circ->base_.timestamp_dirty) - circ->base_.timestamp_dirty = time(NULL); - - pathbias_count_use_attempt(circ); - - link_apconn_to_circ(conn, circ, cpath); - tor_assert(conn->socks_request); - if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) { - if (!conn->use_begindir) - consider_recording_trackhost(conn, circ); - if (connection_ap_handshake_send_begin(conn) < 0) - return -1; - } else { - if (connection_ap_handshake_send_resolve(conn) < 0) - return -1; - } - - return 1; -} - -/** Try to find a safe live circuit for CONN_TYPE_AP connection conn. If - * we don't find one: if conn cannot be handled by any known nodes, - * warn and return -1 (conn needs to die, and is maybe already marked); - * else launch new circuit (if necessary) and return 0. - * Otherwise, associate conn with a safe live circuit, do the - * right next step, and return 1. - */ -/* XXXX this function should mark for close whenever it returns -1; - * its callers shouldn't have to worry about that. */ -int -connection_ap_handshake_attach_circuit(entry_connection_t *conn) -{ - connection_t *base_conn = ENTRY_TO_CONN(conn); - int retval; - int conn_age; - int want_onehop; - - tor_assert(conn); - tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - tor_assert(conn->socks_request); - want_onehop = conn->want_onehop; - - conn_age = (int)(time(NULL) - base_conn->timestamp_created); - - if (conn_age >= get_options()->SocksTimeout) { - int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ? - LOG_INFO : LOG_NOTICE; - log_fn(severity, LD_APP, - "Tried for %d seconds to get a connection to %s:%d. Giving up.", - conn_age, safe_str_client(conn->socks_request->address), - conn->socks_request->port); - return -1; - } - - if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn))) { - /* we're a general conn */ - origin_circuit_t *circ=NULL; - - if (conn->chosen_exit_name) { - const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); - int opt = conn->chosen_exit_optional; - if (!node && !want_onehop) { - /* We ran into this warning when trying to extend a circuit to a - * hidden service directory for which we didn't have a router - * descriptor. See flyspray task 767 for more details. We should - * keep this in mind when deciding to use BEGIN_DIR cells for other - * directory requests as well. -KL*/ - log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' is not known. %s.", - conn->chosen_exit_name, opt ? "Trying others" : "Closing"); - if (opt) { - conn->chosen_exit_optional = 0; - tor_free(conn->chosen_exit_name); - return 0; - } - return -1; - } - if (node && !connection_ap_can_use_exit(conn, node)) { - log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' is excluded or " - "would refuse request. %s.", - conn->chosen_exit_name, opt ? "Trying others" : "Closing"); - if (opt) { - conn->chosen_exit_optional = 0; - tor_free(conn->chosen_exit_name); - return 0; - } - return -1; - } - } - - /* find the circuit that we should use, if there is one. */ - retval = circuit_get_open_circ_or_launch( - conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); - if (retval < 1) // XXX023 if we totally fail, this still returns 0 -RD - return retval; - - log_debug(LD_APP|LD_CIRC, - "Attaching apconn to circ %u (stream %d sec old).", - (unsigned)circ->base_.n_circ_id, conn_age); - /* print the circ's path, so people can figure out which circs are - * sucking. */ - circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ); - - /* We have found a suitable circuit for our conn. Hurray. */ - return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL); - - } else { /* we're a rendezvous conn */ - origin_circuit_t *rendcirc=NULL, *introcirc=NULL; - - tor_assert(!ENTRY_TO_EDGE_CONN(conn)->cpath_layer); - - /* start by finding a rendezvous circuit for us */ - - retval = circuit_get_open_circ_or_launch( - conn, CIRCUIT_PURPOSE_C_REND_JOINED, &rendcirc); - if (retval < 0) return -1; /* failed */ - - if (retval > 0) { - tor_assert(rendcirc); - /* one is already established, attach */ - log_info(LD_REND, - "rend joined circ %d already here. attaching. " - "(stream %d sec old)", - (unsigned)rendcirc->base_.n_circ_id, conn_age); - /* Mark rendezvous circuits as 'newly dirty' every time you use - * them, since the process of rebuilding a rendezvous circ is so - * expensive. There is a tradeoff between linkability and - * feasibility, at this point. - */ - rendcirc->base_.timestamp_dirty = time(NULL); - - /* We've also attempted to use them. If they fail, we need to - * probe them for path bias */ - pathbias_count_use_attempt(rendcirc); - - link_apconn_to_circ(conn, rendcirc, NULL); - if (connection_ap_handshake_send_begin(conn) < 0) - return 0; /* already marked, let them fade away */ - return 1; - } - - if (rendcirc && (rendcirc->base_.purpose == - CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)) { - log_info(LD_REND, - "pending-join circ %u already here, with intro ack. " - "Stalling. (stream %d sec old)", - (unsigned)rendcirc->base_.n_circ_id, conn_age); - return 0; - } - - /* it's on its way. find an intro circ. */ - retval = circuit_get_open_circ_or_launch( - conn, CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, &introcirc); - if (retval < 0) return -1; /* failed */ - - if (retval > 0) { - /* one has already sent the intro. keep waiting. */ - tor_assert(introcirc); - log_info(LD_REND, "Intro circ %u present and awaiting ack (rend %u). " - "Stalling. (stream %d sec old)", - (unsigned)introcirc->base_.n_circ_id, - rendcirc ? (unsigned)rendcirc->base_.n_circ_id : 0, - conn_age); - return 0; - } - - /* now rendcirc and introcirc are each either undefined or not finished */ - - if (rendcirc && introcirc && - rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY) { - log_info(LD_REND, - "ready rend circ %u already here (no intro-ack yet on " - "intro %u). (stream %d sec old)", - (unsigned)rendcirc->base_.n_circ_id, - (unsigned)introcirc->base_.n_circ_id, conn_age); - - tor_assert(introcirc->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING); - if (introcirc->base_.state == CIRCUIT_STATE_OPEN) { - log_info(LD_REND,"found open intro circ %u (rend %u); sending " - "introduction. (stream %d sec old)", - (unsigned)introcirc->base_.n_circ_id, - (unsigned)rendcirc->base_.n_circ_id, - conn_age); - switch (rend_client_send_introduction(introcirc, rendcirc)) { - case 0: /* success */ - rendcirc->base_.timestamp_dirty = time(NULL); - introcirc->base_.timestamp_dirty = time(NULL); - - pathbias_count_use_attempt(introcirc); - pathbias_count_use_attempt(rendcirc); - - assert_circuit_ok(TO_CIRCUIT(rendcirc)); - assert_circuit_ok(TO_CIRCUIT(introcirc)); - return 0; - case -1: /* transient error */ - return 0; - case -2: /* permanent error */ - return -1; - default: /* oops */ - tor_fragile_assert(); - return -1; - } - } - } - - log_info(LD_REND, "Intro (%u) and rend (%u) circs are not both ready. " - "Stalling conn. (%d sec old)", - introcirc ? (unsigned)introcirc->base_.n_circ_id : 0, - rendcirc ? (unsigned)rendcirc->base_.n_circ_id : 0, conn_age); - return 0; - } -} - -/** Change circ's purpose to new_purpose. */ -void -circuit_change_purpose(circuit_t *circ, uint8_t new_purpose) -{ - uint8_t old_purpose; - /* Don't allow an OR circ to become an origin circ or vice versa. */ - tor_assert(!!(CIRCUIT_IS_ORIGIN(circ)) == - !!(CIRCUIT_PURPOSE_IS_ORIGIN(new_purpose))); - - if (circ->purpose == new_purpose) return; - - if (CIRCUIT_IS_ORIGIN(circ)) { - char old_purpose_desc[80] = ""; - - strncpy(old_purpose_desc, circuit_purpose_to_string(circ->purpose), 80-1); - old_purpose_desc[80-1] = '\0'; - - log_debug(LD_CIRC, - "changing purpose of origin circ %d " - "from \"%s\" (%d) to \"%s\" (%d)", - TO_ORIGIN_CIRCUIT(circ)->global_identifier, - old_purpose_desc, - circ->purpose, - circuit_purpose_to_string(new_purpose), - new_purpose); - } - - old_purpose = circ->purpose; - circ->purpose = new_purpose; - - if (CIRCUIT_IS_ORIGIN(circ)) { - control_event_circuit_purpose_changed(TO_ORIGIN_CIRCUIT(circ), - old_purpose); - } -} - -/** Mark circ so that no more connections can be attached to it. */ -void -mark_circuit_unusable_for_new_conns(origin_circuit_t *circ) -{ - const or_options_t *options = get_options(); - tor_assert(circ); - - /* XXXX025 This is a kludge; we're only keeping it around in case there's - * something that doesn't check unusable_for_new_conns, and to avoid - * deeper refactoring of our expiration logic. */ - if (! circ->base_.timestamp_dirty) - circ->base_.timestamp_dirty = approx_time(); - if (options->MaxCircuitDirtiness >= circ->base_.timestamp_dirty) - circ->base_.timestamp_dirty = 1; /* prevent underflow */ - else - circ->base_.timestamp_dirty -= options->MaxCircuitDirtiness; - - circ->unusable_for_new_conns = 1; -} - diff --git a/src/tor/circuituse.h b/src/tor/circuituse.h deleted file mode 100644 index 11e5a64..0000000 --- a/src/tor/circuituse.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file circuituse.h - * \brief Header file for circuituse.c. - **/ - -#ifndef TOR_CIRCUITUSE_H -#define TOR_CIRCUITUSE_H - -void circuit_expire_building(void); -void circuit_remove_handled_ports(smartlist_t *needed_ports); -int circuit_stream_is_being_handled(entry_connection_t *conn, uint16_t port, - int min); -#if 0 -int circuit_conforms_to_options(const origin_circuit_t *circ, - const or_options_t *options); -#endif -void circuit_build_needed_circs(time_t now); -void circuit_detach_stream(circuit_t *circ, edge_connection_t *conn); - -void circuit_expire_old_circuits_serverside(time_t now); - -void reset_bandwidth_test(void); -int circuit_enough_testing_circs(void); - -void circuit_has_opened(origin_circuit_t *circ); -void circuit_try_attaching_streams(origin_circuit_t *circ); -void circuit_build_failed(origin_circuit_t *circ); - -/** Flag to set when a circuit should have only a single hop. */ -#define CIRCLAUNCH_ONEHOP_TUNNEL (1<<0) -/** Flag to set when a circuit needs to be built of high-uptime nodes */ -#define CIRCLAUNCH_NEED_UPTIME (1<<1) -/** Flag to set when a circuit needs to be built of high-capacity nodes */ -#define CIRCLAUNCH_NEED_CAPACITY (1<<2) -/** Flag to set when the last hop of a circuit doesn't need to be an - * exit node. */ -#define CIRCLAUNCH_IS_INTERNAL (1<<3) -origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose, - extend_info_t *info, - int flags); -origin_circuit_t *circuit_launch(uint8_t purpose, int flags); -void circuit_reset_failure_count(int timeout); -int connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath); -int connection_ap_handshake_attach_circuit(entry_connection_t *conn); - -void circuit_change_purpose(circuit_t *circ, uint8_t new_purpose); - -int hostname_in_track_host_exits(const or_options_t *options, - const char *address); -void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ); - -#endif - diff --git a/src/tor/command.c b/src/tor/command.c deleted file mode 100644 index 9b3ff16..0000000 --- a/src/tor/command.c +++ /dev/null @@ -1,589 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file command.c - * \brief Functions for processing incoming cells. - **/ - -/* In-points to command.c: - * - * - command_process_cell(), called from - * incoming cell handlers of channel_t instances; - * callbacks registered in command_setup_channel(), - * called when channels are created in circuitbuild.c - */ -#include "or.h" -#include "channel.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "command.h" -#include "connection.h" -#include "connection_or.h" -#include "config.h" -#include "control.h" -#include "cpuworker.h" -#include "hibernate.h" -#include "nodelist.h" -#include "onion.h" -#include "rephist.h" -#include "relay.h" -#include "router.h" -#include "routerlist.h" - -/** How many CELL_CREATE cells have we received, ever? */ -uint64_t stats_n_create_cells_processed = 0; -/** How many CELL_CREATED cells have we received, ever? */ -uint64_t stats_n_created_cells_processed = 0; -/** How many CELL_RELAY cells have we received, ever? */ -uint64_t stats_n_relay_cells_processed = 0; -/** How many CELL_DESTROY cells have we received, ever? */ -uint64_t stats_n_destroy_cells_processed = 0; - -/* Handle an incoming channel */ -static void command_handle_incoming_channel(channel_listener_t *listener, - channel_t *chan); - -/* These are the main functions for processing cells */ -static void command_process_create_cell(cell_t *cell, channel_t *chan); -static void command_process_created_cell(cell_t *cell, channel_t *chan); -static void command_process_relay_cell(cell_t *cell, channel_t *chan); -static void command_process_destroy_cell(cell_t *cell, channel_t *chan); - -/** Convert the cell command into a lower-case, human-readable - * string. */ -const char * -cell_command_to_string(uint8_t command) -{ - switch (command) { - case CELL_PADDING: return "padding"; - case CELL_CREATE: return "create"; - case CELL_CREATED: return "created"; - case CELL_RELAY: return "relay"; - case CELL_DESTROY: return "destroy"; - case CELL_CREATE_FAST: return "create_fast"; - case CELL_CREATED_FAST: return "created_fast"; - case CELL_VERSIONS: return "versions"; - case CELL_NETINFO: return "netinfo"; - case CELL_RELAY_EARLY: return "relay_early"; - case CELL_CREATE2: return "create2"; - case CELL_CREATED2: return "created2"; - case CELL_VPADDING: return "vpadding"; - case CELL_CERTS: return "certs"; - case CELL_AUTH_CHALLENGE: return "auth_challenge"; - case CELL_AUTHENTICATE: return "authenticate"; - case CELL_AUTHORIZE: return "authorize"; - default: return "unrecognized"; - } -} - -#ifdef KEEP_TIMING_STATS -/** This is a wrapper function around the actual function that processes the - * cell that just arrived on conn. Increment *time - * by the number of microseconds used by the call to *func(cell, conn). - */ -static void -command_time_process_cell(cell_t *cell, channel_t *chan, int *time, - void (*func)(cell_t *, channel_t *)) -{ - struct timeval start, end; - long time_passed; - - tor_gettimeofday(&start); - - (*func)(cell, chan); - - tor_gettimeofday(&end); - time_passed = tv_udiff(&start, &end) ; - - if (time_passed > 10000) { /* more than 10ms */ - log_debug(LD_OR,"That call just took %ld ms.",time_passed/1000); - } - if (time_passed < 0) { - log_info(LD_GENERAL,"That call took us back in time!"); - time_passed = 0; - } - *time += time_passed; -} -#endif - -/** Process a cell that was just received on chan. Keep internal - * statistics about how many of each cell we've processed so far - * this second, and the total number of microseconds it took to - * process each type of cell. - */ -void -command_process_cell(channel_t *chan, cell_t *cell) -{ -#ifdef KEEP_TIMING_STATS - /* how many of each cell have we seen so far this second? needs better - * name. */ - static int num_create=0, num_created=0, num_relay=0, num_destroy=0; - /* how long has it taken to process each type of cell? */ - static int create_time=0, created_time=0, relay_time=0, destroy_time=0; - static time_t current_second = 0; /* from previous calls to time */ - - time_t now = time(NULL); - - if (now > current_second) { /* the second has rolled over */ - /* print stats */ - log_info(LD_OR, - "At end of second: %d creates (%d ms), %d createds (%d ms), " - "%d relays (%d ms), %d destroys (%d ms)", - num_create, create_time/1000, - num_created, created_time/1000, - num_relay, relay_time/1000, - num_destroy, destroy_time/1000); - - /* zero out stats */ - num_create = num_created = num_relay = num_destroy = 0; - create_time = created_time = relay_time = destroy_time = 0; - - /* remember which second it is, for next time */ - current_second = now; - } -#endif - -#ifdef KEEP_TIMING_STATS -#define PROCESS_CELL(tp, cl, cn) STMT_BEGIN { \ - ++num ## tp; \ - command_time_process_cell(cl, cn, & tp ## time , \ - command_process_ ## tp ## _cell); \ - } STMT_END -#else -#define PROCESS_CELL(tp, cl, cn) command_process_ ## tp ## _cell(cl, cn) -#endif - - switch (cell->command) { - case CELL_CREATE: - case CELL_CREATE_FAST: - case CELL_CREATE2: - ++stats_n_create_cells_processed; - PROCESS_CELL(create, cell, chan); - break; - case CELL_CREATED: - case CELL_CREATED_FAST: - case CELL_CREATED2: - ++stats_n_created_cells_processed; - PROCESS_CELL(created, cell, chan); - break; - case CELL_RELAY: - case CELL_RELAY_EARLY: - ++stats_n_relay_cells_processed; - PROCESS_CELL(relay, cell, chan); - break; - case CELL_DESTROY: - ++stats_n_destroy_cells_processed; - PROCESS_CELL(destroy, cell, chan); - break; - default: - log_fn(LOG_INFO, LD_PROTOCOL, - "Cell of unknown or unexpected type (%d) received. " - "Dropping.", - cell->command); - break; - } -} - -/** Process an incoming var_cell from a channel; in the current protocol all - * the var_cells are handshake-related and handled below the channel layer, - * so this just logs a warning and drops the cell. - */ - -void -command_process_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - tor_assert(chan); - tor_assert(var_cell); - - log_info(LD_PROTOCOL, - "Received unexpected var_cell above the channel layer of type %d" - "; dropping it.", - var_cell->command); -} - -/** Process a 'create' cell that just arrived from chan. Make a - * new circuit with the p_circ_id specified in cell. Put the circuit in state - * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get - * picked up again when the cpuworker finishes decrypting it. - */ -static void -command_process_create_cell(cell_t *cell, channel_t *chan) -{ - or_circuit_t *circ; - const or_options_t *options = get_options(); - int id_is_high; - create_cell_t *create_cell; - - tor_assert(cell); - tor_assert(chan); - - log_debug(LD_OR, - "Got a CREATE cell for circ_id %u on channel " U64_FORMAT - " (%p)", - (unsigned)cell->circ_id, - U64_PRINTF_ARG(chan->global_identifier), chan); - - if (we_are_hibernating()) { - log_info(LD_OR, - "Received create cell but we're shutting down. Sending back " - "destroy."); - channel_send_destroy(cell->circ_id, chan, - END_CIRC_REASON_HIBERNATING); - return; - } - - if (!server_mode(options) || - (!public_server_mode(options) && channel_is_outgoing(chan))) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received create cell (type %d) from %s, but we're connected " - "to it as a client. " - "Sending back a destroy.", - (int)cell->command, channel_get_canonical_remote_descr(chan)); - channel_send_destroy(cell->circ_id, chan, - END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (cell->circ_id == 0) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a create cell (type %d) from %s with zero circID; " - " ignoring.", (int)cell->command, - channel_get_actual_remote_descr(chan)); - return; - } - - /* If the high bit of the circuit ID is not as expected, close the - * circ. */ - if (chan->wide_circ_ids) - id_is_high = cell->circ_id & (1u<<31); - else - id_is_high = cell->circ_id & (1u<<15); - if ((id_is_high && - chan->circ_id_type == CIRC_ID_TYPE_HIGHER) || - (!id_is_high && - chan->circ_id_type == CIRC_ID_TYPE_LOWER)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received create cell with unexpected circ_id %u. Closing.", - (unsigned)cell->circ_id); - channel_send_destroy(cell->circ_id, chan, - END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { - const node_t *node = node_get_by_id(chan->identity_digest); - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received CREATE cell (circID %u) for known circ. " - "Dropping (age %d).", - (unsigned)cell->circ_id, - (int)(time(NULL) - channel_when_created(chan))); - if (node) { - char *p = esc_for_log(node_get_platform(node)); - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Details: router %s, platform %s.", - node_describe(node), p); - tor_free(p); - } - return; - } - - circ = or_circuit_new(cell->circ_id, chan); - circ->base_.purpose = CIRCUIT_PURPOSE_OR; - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); - create_cell = tor_malloc_zero(sizeof(create_cell_t)); - if (create_cell_parse(create_cell, cell) < 0) { - tor_free(create_cell); - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Bogus/unrecognized create cell; closing."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) { - /* hand it off to the cpuworkers, and then return. */ - if (connection_or_digest_is_known_relay(chan->identity_digest)) - rep_hist_note_circuit_handshake_requested(create_cell->handshake_type); - if (assign_onionskin_to_cpuworker(NULL, circ, create_cell) < 0) { - log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); - return; - } - log_debug(LD_OR,"success: handed off onionskin."); - } else { - /* This is a CREATE_FAST cell; we can handle it immediately without using - * a CPU worker. */ - uint8_t keys[CPATH_KEY_MATERIAL_LEN]; - uint8_t rend_circ_nonce[DIGEST_LEN]; - int len; - created_cell_t created_cell; - - /* Make sure we never try to use the OR connection on which we - * received this cell to satisfy an EXTEND request, */ - channel_mark_client(chan); - - memset(&created_cell, 0, sizeof(created_cell)); - len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST, - create_cell->onionskin, - create_cell->handshake_len, - NULL, - created_cell.reply, - keys, CPATH_KEY_MATERIAL_LEN, - rend_circ_nonce); - tor_free(create_cell); - if (len < 0) { - log_warn(LD_OR,"Failed to generate key material. Closing."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - tor_free(create_cell); - return; - } - created_cell.cell_type = CELL_CREATED_FAST; - created_cell.handshake_len = len; - - if (onionskin_answer(circ, &created_cell, - (const char *)keys, rend_circ_nonce)<0) { - log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - return; - } - memwipe(keys, 0, sizeof(keys)); - } -} - -/** Process a 'created' cell that just arrived from chan. - * Find the circuit - * that it's intended for. If we're not the origin of the circuit, package - * the 'created' cell in an 'extended' relay cell and pass it back. If we - * are the origin of the circuit, send it to circuit_finish_handshake() to - * finish processing keys, and then call circuit_send_next_onion_skin() to - * extend to the next hop in the circuit if necessary. - */ -static void -command_process_created_cell(cell_t *cell, channel_t *chan) -{ - circuit_t *circ; - extended_cell_t extended_cell; - - circ = circuit_get_by_circid_channel(cell->circ_id, chan); - - if (!circ) { - log_info(LD_OR, - "(circID %u) unknown circ (probably got a destroy earlier). " - "Dropping.", (unsigned)cell->circ_id); - return; - } - - if (circ->n_circ_id != cell->circ_id) { - log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, - "got created cell from Tor client? Closing."); - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (created_cell_parse(&extended_cell.created_cell, cell) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, "Unparseable created cell."); - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ - origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); - int err_reason = 0; - log_debug(LD_OR,"at OP. Finishing handshake."); - if ((err_reason = circuit_finish_handshake(origin_circ, - &extended_cell.created_cell)) < 0) { - log_warn(LD_OR,"circuit_finish_handshake failed."); - circuit_mark_for_close(circ, -err_reason); - return; - } - log_debug(LD_OR,"Moving to next skin."); - if ((err_reason = circuit_send_next_onion_skin(origin_circ)) < 0) { - log_info(LD_OR,"circuit_send_next_onion_skin failed."); - /* XXX push this circuit_close lower */ - circuit_mark_for_close(circ, -err_reason); - return; - } - } else { /* pack it into an extended relay cell, and send it. */ - uint8_t command=0; - uint16_t len=0; - uint8_t payload[RELAY_PAYLOAD_SIZE]; - log_debug(LD_OR, - "Converting created cell to extended relay cell, sending."); - memset(payload, 0, sizeof(payload)); - if (extended_cell.created_cell.cell_type == CELL_CREATED2) - extended_cell.cell_type = RELAY_COMMAND_EXTENDED2; - else - extended_cell.cell_type = RELAY_COMMAND_EXTENDED; - if (extended_cell_format(&command, &len, payload, &extended_cell) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, "Can't format extended cell."); - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - return; - } - - relay_send_command_from_edge(0, circ, command, - (const char*)payload, len, NULL); - } -} - -/** Process a 'relay' or 'relay_early' cell that just arrived from - * conn. Make sure it came in with a recognized circ_id. Pass it on to - * circuit_receive_relay_cell() for actual processing. - */ -static void -command_process_relay_cell(cell_t *cell, channel_t *chan) -{ - circuit_t *circ; - int reason, direction; - - circ = circuit_get_by_circid_channel(cell->circ_id, chan); - - if (!circ) { - log_debug(LD_OR, - "unknown circuit %u on connection from %s. Dropping.", - (unsigned)cell->circ_id, - channel_get_canonical_remote_descr(chan)); - return; - } - - if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { - log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit in create_wait. Closing."); - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - return; - } - - if (CIRCUIT_IS_ORIGIN(circ)) { - /* if we're a relay and treating connections with recent local - * traffic better, then this is one of them. */ - channel_timestamp_client(chan); - } - - if (!CIRCUIT_IS_ORIGIN(circ) && - cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) - direction = CELL_DIRECTION_OUT; - else - direction = CELL_DIRECTION_IN; - - /* If we have a relay_early cell, make sure that it's outbound, and we've - * gotten no more than MAX_RELAY_EARLY_CELLS_PER_CIRCUIT of them. */ - if (cell->command == CELL_RELAY_EARLY) { - if (direction == CELL_DIRECTION_IN) { - /* Allow an unlimited number of inbound relay_early cells, - * for hidden service compatibility. There isn't any way to make - * a long circuit through inbound relay_early cells anyway. See - * bug 1038. -RD */ - } else { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->remaining_relay_early_cells == 0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received too many RELAY_EARLY cells on circ %u from %s." - " Closing circuit.", - (unsigned)cell->circ_id, - safe_str(channel_get_canonical_remote_descr(chan))); - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - return; - } - --or_circ->remaining_relay_early_cells; - } - } - - if ((reason = circuit_receive_relay_cell(cell, circ, direction)) < 0) { - log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell " - "(%s) failed. Closing.", - direction==CELL_DIRECTION_OUT?"forward":"backward"); - circuit_mark_for_close(circ, -reason); - } -} - -/** Process a 'destroy' cell that just arrived from - * chan. Find the circ that it refers to (if any). - * - * If the circ is in state - * onionskin_pending, then call onion_pending_remove() to remove it - * from the pending onion list (note that if it's already being - * processed by the cpuworker, it won't be in the list anymore; but - * when the cpuworker returns it, the circuit will be gone, and the - * cpuworker response will be dropped). - * - * Then mark the circuit for close (which marks all edges for close, - * and passes the destroy cell onward if necessary). - */ -static void -command_process_destroy_cell(cell_t *cell, channel_t *chan) -{ - circuit_t *circ; - int reason; - - circ = circuit_get_by_circid_channel(cell->circ_id, chan); - if (!circ) { - log_info(LD_OR,"unknown circuit %u on connection from %s. Dropping.", - (unsigned)cell->circ_id, - channel_get_canonical_remote_descr(chan)); - return; - } - log_debug(LD_OR,"Received for circID %u.",(unsigned)cell->circ_id); - - reason = (uint8_t)cell->payload[0]; - circ->received_destroy = 1; - - if (!CIRCUIT_IS_ORIGIN(circ) && - cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) { - /* the destroy came from behind */ - circuit_set_p_circid_chan(TO_OR_CIRCUIT(circ), 0, NULL); - circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); - } else { /* the destroy came from ahead */ - circuit_set_n_circid_chan(circ, 0, NULL); - if (CIRCUIT_IS_ORIGIN(circ)) { - circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); - } else { - char payload[1]; - log_debug(LD_OR, "Delivering 'truncated' back."); - payload[0] = (char)reason; - relay_send_command_from_edge(0, circ, RELAY_COMMAND_TRUNCATED, - payload, sizeof(payload), NULL); - } - } -} - -/** Callback to handle a new channel; call command_setup_channel() to give - * it the right cell handlers. - */ - -static void -command_handle_incoming_channel(channel_listener_t *listener, channel_t *chan) -{ - tor_assert(listener); - tor_assert(chan); - - command_setup_channel(chan); -} - -/** Given a channel, install the right handlers to process incoming - * cells on it. - */ - -void -command_setup_channel(channel_t *chan) -{ - tor_assert(chan); - - channel_set_cell_handlers(chan, - command_process_cell, - command_process_var_cell); -} - -/** Given a listener, install the right handler to process incoming - * channels on it. - */ - -void -command_setup_listener(channel_listener_t *listener) -{ - tor_assert(listener); - tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING); - - channel_listener_set_listener_fn(listener, command_handle_incoming_channel); -} - diff --git a/src/tor/command.h b/src/tor/command.h deleted file mode 100644 index adea6ad..0000000 --- a/src/tor/command.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file command.h - * \brief Header file for command.c. - **/ - -#ifndef TOR_COMMAND_H -#define TOR_COMMAND_H - -#include "channel.h" - -void command_process_cell(channel_t *chan, cell_t *cell); -void command_process_var_cell(channel_t *chan, var_cell_t *cell); -void command_setup_channel(channel_t *chan); -void command_setup_listener(channel_listener_t *chan_l); - -const char *cell_command_to_string(uint8_t command); - -extern uint64_t stats_n_padding_cells_processed; -extern uint64_t stats_n_create_cells_processed; -extern uint64_t stats_n_created_cells_processed; -extern uint64_t stats_n_relay_cells_processed; -extern uint64_t stats_n_destroy_cells_processed; - -#endif - diff --git a/src/tor/common_sha1.i b/src/tor/common_sha1.i deleted file mode 100644 index 80ea224..0000000 --- a/src/tor/common_sha1.i +++ /dev/null @@ -1,26 +0,0 @@ -"10e0ec8ee3318e6a5ebbedc35d45ad4ec2fe5bb2 src/common/aes.c\n" -"7fdacbf7fc104d4e118ce06cf823b2f3cb145291 src/common/crypto.c\n" -"401d6c1243b8d99ad8f64dba232e234e60a59eb7 src/common/crypto_format.c\n" -"64a20b8425300b0bf2373a4fe4eb74b47c4baf50 src/common/torgzip.c\n" -"f0e8fd88f7198ad4adbd5c28e4af909b2d553b5a src/common/tortls.c\n" -"05dc726d9d47888cc75a1c3bd31c8214dc2cb581 src/common/crypto_curve25519.c\n" -"d233965a57506745525c7a78ff2911d59f5e3743 src/common/address.h\n" -"ac6a50ceb318ed6907b5804034b98172ffc5ff83 src/common/backtrace.h\n" -"947ef902f15f556f176b1115f09d9966e377347d src/common/aes.h\n" -"6fb51902eea04b5c33a99a754845958fec43d912 src/common/ciphers.inc\n" -"4618a9860688c2cb12d37d8172317324c10f0a92 src/common/tor_compat.h\n" -"e427c754391f1282a98cbbb387bae2ae403cbde7 src/common/compat_libevent.h\n" -"faaa0bcfcc0cbc61f6d092b9b36e56cb89b090b7 src/common/container.h\n" -"7196fde86ec70bd579e8fcd546817d285d936b18 src/common/crypto.h\n" -"1260154e3b65f2586a54986deffec6963a4c7204 src/common/crypto_curve25519.h\n" -"9ed1bb165e8d0532cae5bfd17e27d037d2a05bf4 src/common/di_ops.h\n" -"697be45dc2e1ae6537b34dc72abf6394952a1b87 src/common/memarea.h\n" -"0b594bada47b6e23358fa924cffbf24e01180d60 src/common/mempool.h\n" -"2cd7af59a82d4e1ca2873d2801c44176605a545a src/common/procmon.h\n" -"ed8b5d4225ceaf11e29fe635a091e2b2f3fe3ae8 src/common/sandbox.h\n" -"ddfdca2f5d52acc27214a3c91c0ca73c81b526eb src/common/testsupport.h\n" -"13108dc9184a7ece65685e0724e7e8770acd576f src/common/torgzip.h\n" -"8d71f0488728c324a5aabfd92d209350973b490a src/common/torint.h\n" -"b86f76bfdfdc3bd841233c662761e857cb830c88 src/common/torlog.h\n" -"27209a8a0e9b8c61bbbb2d50241a2a4b4595ce19 src/common/tortls.h\n" -"e5cb074466d9f59f1988e8964fed1476c238ff14 src/common/tor_util.h\n" diff --git a/src/tor/compat.c b/src/tor/compat.c deleted file mode 100644 index 541f8d2..0000000 --- a/src/tor/compat.c +++ /dev/null @@ -1,3254 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file compat.c - * \brief Wrappers to make calls more portable. This code defines - * functions such as tor_malloc, tor_snprintf, get/set various data types, - * renaming, setting socket options, switching user IDs. It is basically - * where the non-portable items are conditionally included depending on - * the platform. - **/ - -/* This is required on rh7 to make strptime not complain. - * We also need it to make memmem get defined (where available) - */ -/* 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.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 - -#define COMPAT_PRIVATE -#include "tor_compat.h" - -#ifdef _WIN32 -#include -#include -#include -#endif - -#ifdef HAVE_UNAME -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_FCNTL_H -#include -#endif -#ifdef HAVE_PWD_H -#include -#endif -#ifdef HAVE_GRP_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_CRT_EXTERNS_H -#include -#endif - -#ifndef HAVE_GETTIMEOFDAY -#ifdef HAVE_FTIME -#include -#endif -#endif - -/* Includes for the process attaching prevention */ -#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) -#include -#elif defined(__APPLE__) -#include -#include -#endif - -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include /* FreeBSD needs this to know what version it is */ -#endif -#include -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#ifdef HAVE_UTIME_H -#include -#endif -#ifdef HAVE_SYS_UTIME_H -#include -#endif -#ifdef HAVE_SYS_MMAN_H -#include -#endif -#ifdef HAVE_SYS_SYSLIMITS_H -#include -#endif -#ifdef HAVE_SYS_FILE_H -#include -#endif -#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) -/* Only use the linux prctl; the IRIX prctl is totally different */ -#include -#endif - -#include "torlog.h" -#include "tor_util.h" -#include "container.h" -#include "address.h" -#include "sandbox.h" - -/* Inline the strl functions if the platform doesn't have them. */ -#ifndef HAVE_STRLCPY -#include "strlcpy.c" -#endif -#ifndef HAVE_STRLCAT -#include "strlcat.c" -#endif - -/** As open(path, flags, mode), but return an fd with the close-on-exec mode - * set. */ -int -tor_open_cloexec(const char *path, int flags, unsigned mode) -{ - int fd; -#ifdef O_CLOEXEC - path = sandbox_intern_string(path); - fd = open(path, flags|O_CLOEXEC, mode); - if (fd >= 0) - return fd; - /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with O_CLOEXEC support, we - * are running on one without. */ - if (errno != EINVAL) - return -1; -#endif - - fd = open(path, flags, mode); -#ifdef FD_CLOEXEC - if (fd >= 0) { - if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); - close(fd); - return -1; - } - } -#endif - return fd; -} - -/** As fopen(path,mode), but ensures that the O_CLOEXEC bit is set on the - * underlying file handle. */ -FILE * -tor_fopen_cloexec(const char *path, const char *mode) -{ - FILE *result = fopen(path, mode); -#ifdef FD_CLOEXEC - if (result != NULL) { - if (fcntl(fileno(result), F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); - fclose(result); - return NULL; - } - } -#endif - return result; -} - -#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) -/** Try to create a memory mapping for filename and return it. On - * failure, return NULL. Sets errno properly, using ERANGE to mean - * "empty file". */ -tor_mmap_t * -tor_mmap_file(const char *filename) -{ - int fd; /* router file */ - char *string; - int page_size; - tor_mmap_t *res; - size_t size, filesize; - - tor_assert(filename); - - fd = tor_open_cloexec(filename, O_RDONLY, 0); - if (fd<0) { - int save_errno = errno; - int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN; - log_fn(severity, LD_FS,"Could not open \"%s\" for mmap(): %s",filename, - strerror(errno)); - errno = save_errno; - return NULL; - } - - /* XXXX why not just do fstat here? */ - size = filesize = (size_t) lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - /* ensure page alignment */ - page_size = getpagesize(); - size += (size%page_size) ? page_size-(size%page_size) : 0; - - if (!size) { - /* Zero-length file. If we call mmap on it, it will succeed but - * return NULL, and bad things will happen. So just fail. */ - log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename); - errno = ERANGE; - close(fd); - return NULL; - } - - string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if (string == MAP_FAILED) { - int save_errno = errno; - log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename, - strerror(errno)); - errno = save_errno; - return NULL; - } - - res = tor_malloc_zero(sizeof(tor_mmap_t)); - res->data = string; - res->size = filesize; - res->mapping_size = size; - - return res; -} -/** Release storage held for a memory mapping. */ -void -tor_munmap_file(tor_mmap_t *handle) -{ - munmap((char*)handle->data, handle->mapping_size); - tor_free(handle); -} -#elif defined(_WIN32) -tor_mmap_t * -tor_mmap_file(const char *filename) -{ - TCHAR tfilename[MAX_PATH]= {0}; - tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t)); - int empty = 0; - HANDLE file_handle = INVALID_HANDLE_VALUE; - DWORD size_low, size_high; - uint64_t real_size; - res->mmap_handle = NULL; -#ifdef UNICODE - mbstowcs(tfilename,filename,MAX_PATH); -#else - strlcpy(tfilename,filename,MAX_PATH); -#endif - file_handle = CreateFile(tfilename, - GENERIC_READ, FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); - - if (file_handle == INVALID_HANDLE_VALUE) - goto win_err; - - size_low = GetFileSize(file_handle, &size_high); - - if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { - log_warn(LD_FS,"Error getting size of \"%s\".",filename); - goto win_err; - } - if (size_low == 0 && size_high == 0) { - log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename); - empty = 1; - goto err; - } - real_size = (((uint64_t)size_high)<<32) | size_low; - if (real_size > SIZE_MAX) { - log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename); - goto err; - } - res->size = real_size; - - res->mmap_handle = CreateFileMapping(file_handle, - NULL, - PAGE_READONLY, - size_high, - size_low, - NULL); - if (res->mmap_handle == NULL) - goto win_err; - res->data = (char*) MapViewOfFile(res->mmap_handle, - FILE_MAP_READ, - 0, 0, 0); - if (!res->data) - goto win_err; - - CloseHandle(file_handle); - return res; - win_err: { - DWORD e = GetLastError(); - int severity = (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) ? - LOG_INFO : LOG_WARN; - char *msg = format_win32_error(e); - log_fn(severity, LD_FS, "Couldn't mmap file \"%s\": %s", filename, msg); - tor_free(msg); - if (e == ERROR_FILE_NOT_FOUND || e == ERROR_PATH_NOT_FOUND) - errno = ENOENT; - else - errno = EINVAL; - } - err: - if (empty) - errno = ERANGE; - if (file_handle != INVALID_HANDLE_VALUE) - CloseHandle(file_handle); - tor_munmap_file(res); - return NULL; -} -void -tor_munmap_file(tor_mmap_t *handle) -{ - if (handle->data) - /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would - have to be redefined as non-const. */ - UnmapViewOfFile( (LPVOID) handle->data); - - if (handle->mmap_handle != NULL) - CloseHandle(handle->mmap_handle); - tor_free(handle); -} -#else -tor_mmap_t * -tor_mmap_file(const char *filename) -{ - struct stat st; - char *res = read_file_to_str(filename, RFTS_BIN|RFTS_IGNORE_MISSING, &st); - tor_mmap_t *handle; - if (! res) - return NULL; - handle = tor_malloc_zero(sizeof(tor_mmap_t)); - handle->data = res; - handle->size = st.st_size; - return handle; -} -void -tor_munmap_file(tor_mmap_t *handle) -{ - char *d = (char*)handle->data; - tor_free(d); - memwipe(handle, 0, sizeof(tor_mmap_t)); - tor_free(handle); -} -#endif - -/** Replacement for snprintf. Differs from platform snprintf in two - * ways: First, always NUL-terminates its output. Second, always - * returns -1 if the result is truncated. (Note that this return - * behavior does not conform to C99; it just happens to be - * easier to emulate "return -1" with conformant implementations than - * it is to emulate "return number that would be written" with - * non-conformant implementations.) */ -int -tor_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int r; - va_start(ap,format); - r = tor_vsnprintf(str,size,format,ap); - va_end(ap); - return r; -} - -/** Replacement for vsnprintf; behavior differs as tor_snprintf differs from - * snprintf. - */ -int -tor_vsnprintf(char *str, size_t size, const char *format, va_list args) -{ - int r; - if (size == 0) - return -1; /* no place for the NUL */ - if (size > SIZE_T_CEILING) - return -1; -#ifdef _WIN32 - r = _vsnprintf(str, size, format, args); -#else - r = vsnprintf(str, size, format, args); -#endif - str[size-1] = '\0'; - if (r < 0 || r >= (ssize_t)size) - return -1; - return r; -} - -/** - * Portable asprintf implementation. Does a printf() into a newly malloc'd - * string. Sets *strp to this string, and returns its length (not - * including the terminating NUL character). - * - * You can treat this function as if its implementation were something like -
-     char buf[_INFINITY_];
-     tor_snprintf(buf, sizeof(buf), fmt, args);
-     *strp = tor_strdup(buf);
-     return strlen(*strp):
-   
- * Where _INFINITY_ is an imaginary constant so big that any string can fit - * into it. - */ -int -tor_asprintf(char **strp, const char *fmt, ...) -{ - int r; - va_list args; - va_start(args, fmt); - r = tor_vasprintf(strp, fmt, args); - va_end(args); - if (!*strp || r < 0) { - log_err(LD_BUG, "Internal error in asprintf"); - tor_assert(0); - } - return r; -} - -/** - * Portable vasprintf implementation. Does a printf() into a newly malloc'd - * string. Differs from regular vasprintf in the same ways that - * tor_asprintf() differs from regular asprintf. - */ -int -tor_vasprintf(char **strp, const char *fmt, va_list args) -{ - /* use a temporary variable in case *strp is in args. */ - char *strp_tmp=NULL; -#ifdef HAVE_VASPRINTF - /* If the platform gives us one, use it. */ - int r = vasprintf(&strp_tmp, fmt, args); - if (r < 0) - *strp = NULL; - else - *strp = strp_tmp; - return r; -#elif defined(HAVE__VSCPRINTF) - /* On Windows, _vsnprintf won't tell us the length of the string if it - * overflows, so we need to use _vcsprintf to tell how much to allocate */ - int len, r; - len = _vscprintf(fmt, args); - if (len < 0) { - *strp = NULL; - return -1; - } - strp_tmp = tor_malloc(len + 1); - r = _vsnprintf(strp_tmp, len+1, fmt, args); - if (r != len) { - tor_free(strp_tmp); - *strp = NULL; - return -1; - } - *strp = strp_tmp; - return len; -#else - /* Everywhere else, we have a decent vsnprintf that tells us how many - * characters we need. We give it a try on a short buffer first, since - * it might be nice to avoid the second vsnprintf call. - */ - char buf[128]; - int len, r; - va_list tmp_args; - va_copy(tmp_args, args); - len = vsnprintf(buf, sizeof(buf), fmt, tmp_args); - va_end(tmp_args); - if (len < (int)sizeof(buf)) { - *strp = tor_strdup(buf); - return len; - } - strp_tmp = tor_malloc(len+1); - r = vsnprintf(strp_tmp, len+1, fmt, args); - if (r != len) { - tor_free(strp_tmp); - *strp = NULL; - return -1; - } - *strp = strp_tmp; - return len; -#endif -} - -/** Given hlen bytes at haystack and nlen bytes at - * needle, return a pointer to the first occurrence of the needle - * within the haystack, or NULL if there is no such occurrence. - * - * This function is not timing-safe. - * - * Requires that nlen be greater than zero. - */ -const void * -tor_memmem(const void *_haystack, size_t hlen, - const void *_needle, size_t nlen) -{ -#if defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2) && !defined(__APPLE__) - tor_assert(nlen); - return memmem(_haystack, hlen, _needle, nlen); -#else - /* This isn't as fast as the GLIBC implementation, but it doesn't need to - * be. */ - const char *p, *end; - const char *haystack = (const char*)_haystack; - const char *needle = (const char*)_needle; - char first; - tor_assert(nlen); - - p = haystack; - end = haystack + hlen; - first = *(const char*)needle; - while ((p = memchr(p, first, end-p))) { - if (p+nlen > end) - return NULL; - if (fast_memeq(p, needle, nlen)) - return p; - ++p; - } - return NULL; -#endif -} - -/** - * Tables to implement ctypes-replacement TOR_IS*() functions. Each table - * has 256 bits to look up whether a character is in some set or not. This - * fails on non-ASCII platforms, but it is hard to find a platform whose - * character set is not a superset of ASCII nowadays. */ - -/**@{*/ -const uint32_t TOR_ISALPHA_TABLE[8] = - { 0, 0, 0x7fffffe, 0x7fffffe, 0, 0, 0, 0 }; -const uint32_t TOR_ISALNUM_TABLE[8] = - { 0, 0x3ff0000, 0x7fffffe, 0x7fffffe, 0, 0, 0, 0 }; -const uint32_t TOR_ISSPACE_TABLE[8] = { 0x3e00, 0x1, 0, 0, 0, 0, 0, 0 }; -const uint32_t TOR_ISXDIGIT_TABLE[8] = - { 0, 0x3ff0000, 0x7e, 0x7e, 0, 0, 0, 0 }; -const uint32_t TOR_ISDIGIT_TABLE[8] = { 0, 0x3ff0000, 0, 0, 0, 0, 0, 0 }; -const uint32_t TOR_ISPRINT_TABLE[8] = - { 0, 0xffffffff, 0xffffffff, 0x7fffffff, 0, 0, 0, 0x0 }; -const uint32_t TOR_ISUPPER_TABLE[8] = { 0, 0, 0x7fffffe, 0, 0, 0, 0, 0 }; -const uint32_t TOR_ISLOWER_TABLE[8] = { 0, 0, 0, 0x7fffffe, 0, 0, 0, 0 }; - -/** Upper-casing and lowercasing tables to map characters to upper/lowercase - * equivalents. Used by tor_toupper() and tor_tolower(). */ -/**@{*/ -const char TOR_TOUPPER_TABLE[256] = { - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, - 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, - 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, - 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, - 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, - 96,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, - 80,81,82,83,84,85,86,87,88,89,90,123,124,125,126,127, - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, - 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, - 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, - 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, - 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, -}; -const char TOR_TOLOWER_TABLE[256] = { - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, - 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, - 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, - 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, - 64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95, - 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, - 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, - 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, - 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, - 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, -}; -/**@}*/ - -/** Helper for tor_strtok_r_impl: Advances cp past all characters in - * sep, and returns its new value. */ -static char * -strtok_helper(char *cp, const char *sep) -{ - if (sep[1]) { - while (*cp && strchr(sep, *cp)) - ++cp; - } else { - while (*cp && *cp == *sep) - ++cp; - } - return cp; -} - -/** Implementation of strtok_r for platforms whose coders haven't figured out - * how to write one. Hey guys! You can use this code here for free! */ -char * -tor_strtok_r_impl(char *str, const char *sep, char **lasts) -{ - char *cp, *start; - tor_assert(*sep); - if (str) { - str = strtok_helper(str, sep); - if (!*str) - return NULL; - start = cp = *lasts = str; - } else if (!*lasts || !**lasts) { - return NULL; - } else { - start = cp = *lasts; - } - - if (sep[1]) { - while (*cp && !strchr(sep, *cp)) - ++cp; - } else { - cp = strchr(cp, *sep); - } - - if (!cp || !*cp) { - *lasts = NULL; - } else { - *cp++ = '\0'; - *lasts = strtok_helper(cp, sep); - } - return start; -} - -#ifdef _WIN32 -/** Take a filename and return a pointer to its final element. This - * function is called on __FILE__ to fix a MSVC nit where __FILE__ - * contains the full path to the file. This is bad, because it - * confuses users to find the home directory of the person who - * compiled the binary in their warning messages. - */ -const char * -tor_fix_source_file(const char *fname) -{ - const char *cp1, *cp2, *r; - cp1 = strrchr(fname, '/'); - cp2 = strrchr(fname, '\\'); - if (cp1 && cp2) { - r = (cp1cp. Equivalent to - * *(uint16_t*)(cp), but will not cause segfaults on platforms that forbid - * unaligned memory access. - */ -uint16_t -get_uint16(const void *cp) -{ - uint16_t v; - memcpy(&v,cp,2); - return v; -} -/** - * Read a 32-bit value beginning at cp. Equivalent to - * *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid - * unaligned memory access. - */ -uint32_t -get_uint32(const void *cp) -{ - uint32_t v; - memcpy(&v,cp,4); - return v; -} -/** - * Read a 64-bit value beginning at cp. Equivalent to - * *(uint64_t*)(cp), but will not cause segfaults on platforms that forbid - * unaligned memory access. - */ -uint64_t -get_uint64(const void *cp) -{ - uint64_t v; - memcpy(&v,cp,8); - return v; -} - -/** - * Set a 16-bit value beginning at cp to v. Equivalent to - * *(uint16_t*)(cp) = v, but will not cause segfaults on platforms that forbid - * unaligned memory access. */ -void -set_uint16(void *cp, uint16_t v) -{ - memcpy(cp,&v,2); -} -/** - * Set a 32-bit value beginning at cp to v. Equivalent to - * *(uint32_t*)(cp) = v, but will not cause segfaults on platforms that forbid - * unaligned memory access. */ -void -set_uint32(void *cp, uint32_t v) -{ - memcpy(cp,&v,4); -} -/** - * Set a 64-bit value beginning at cp to v. Equivalent to - * *(uint64_t*)(cp) = v, but will not cause segfaults on platforms that forbid - * unaligned memory access. */ -void -set_uint64(void *cp, uint64_t v) -{ - memcpy(cp,&v,8); -} - -/** - * Rename the file from to the file to. On Unix, this is - * the same as rename(2). On windows, this removes to first if - * it already exists. - * Returns 0 on success. Returns -1 and sets errno on failure. - */ -int -replace_file(const char *from, const char *to) -{ -#ifndef _WIN32 - return rename(from,to); -#else - switch (file_status(to)) - { - case FN_NOENT: - break; - case FN_FILE: - if (unlink(to)) return -1; - break; - case FN_ERROR: - return -1; - case FN_DIR: - errno = EISDIR; - return -1; - } - return rename(from,to); -#endif -} - -/** Change fname's modification time to now. */ -int -touch_file(const char *fname) -{ - if (utime(fname, NULL)!=0) - return -1; - return 0; -} - -/** Represents a lockfile on which we hold the lock. */ -struct tor_lockfile_t { - /** Name of the file */ - char *filename; - /** File descriptor used to hold the file open */ - int fd; -}; - -/** Try to get a lock on the lockfile filename, creating it as - * necessary. If someone else has the lock and blocking is true, - * wait until the lock is available. Otherwise return immediately whether - * we succeeded or not. - * - * Set *locked_out to true if somebody else had the lock, and to false - * otherwise. - * - * Return a tor_lockfile_t on success, NULL on failure. - * - * (Implementation note: because we need to fall back to fcntl on some - * platforms, these locks are per-process, not per-thread. If you want - * to do in-process locking, use tor_mutex_t like a normal person. - * On Windows, when blocking is true, the maximum time that - * is actually waited is 10 seconds, after which NULL is returned - * and locked_out is set to 1.) - */ -tor_lockfile_t * -tor_lockfile_lock(const char *filename, int blocking, int *locked_out) -{ - tor_lockfile_t *result; - int fd; - *locked_out = 0; - - log_info(LD_FS, "Locking \"%s\"", filename); - fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); - if (fd < 0) { - log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, - strerror(errno)); - return NULL; - } - -#ifdef _WIN32 - _lseek(fd, 0, SEEK_SET); - if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) { - if (errno != EACCES && errno != EDEADLOCK) - log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); - else - *locked_out = 1; - close(fd); - return NULL; - } -#elif defined(HAVE_FLOCK) - if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) { - if (errno != EWOULDBLOCK) - log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); - else - *locked_out = 1; - close(fd); - return NULL; - } -#else - { - struct flock lock; - memset(&lock, 0, sizeof(lock)); - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - if (fcntl(fd, blocking ? F_SETLKW : F_SETLK, &lock) < 0) { - if (errno != EACCES && errno != EAGAIN) - log_warn(LD_FS, "Couldn't lock \"%s\": %s", filename, strerror(errno)); - else - *locked_out = 1; - close(fd); - return NULL; - } - } -#endif - - result = tor_malloc(sizeof(tor_lockfile_t)); - result->filename = tor_strdup(filename); - result->fd = fd; - return result; -} - -/** Release the lock held as lockfile. */ -void -tor_lockfile_unlock(tor_lockfile_t *lockfile) -{ - tor_assert(lockfile); - - log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); -#ifdef _WIN32 - _lseek(lockfile->fd, 0, SEEK_SET); - if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) { - log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, - strerror(errno)); - } -#elif defined(HAVE_FLOCK) - if (flock(lockfile->fd, LOCK_UN) < 0) { - log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename, - strerror(errno)); - } -#else - /* Closing the lockfile is sufficient. */ -#endif - - close(lockfile->fd); - lockfile->fd = -1; - tor_free(lockfile->filename); - tor_free(lockfile); -} - -/** @{ */ -/** Some old versions of Unix didn't define constants for these values, - * and instead expect you to say 0, 1, or 2. */ -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif -#ifndef SEEK_CUR -#define SEEK_CUR 1 -#endif -#ifndef SEEK_END -#define SEEK_END 2 -#endif -/** @} */ - -/** Return the position of fd with respect to the start of the file. */ -off_t -tor_fd_getpos(int fd) -{ -#ifdef _WIN32 - return (off_t) _lseek(fd, 0, SEEK_CUR); -#else - return (off_t) lseek(fd, 0, SEEK_CUR); -#endif -} - -/** Move fd to the end of the file. Return -1 on error, 0 on success. */ -int -tor_fd_seekend(int fd) -{ -#ifdef _WIN32 - return _lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; -#else - return lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; -#endif -} - -/** Move fd to position pos in the file. Return -1 on error, 0 - * on success. */ -int -tor_fd_setpos(int fd, off_t pos) -{ -#ifdef _WIN32 - return _lseek(fd, pos, SEEK_SET) < 0 ? -1 : 0; -#else - return lseek(fd, pos, SEEK_SET) < 0 ? -1 : 0; -#endif -} - -#undef DEBUG_SOCKET_COUNTING -#ifdef DEBUG_SOCKET_COUNTING -/** A bitarray of all fds that should be passed to tor_socket_close(). Only - * used if DEBUG_SOCKET_COUNTING is defined. */ -static bitarray_t *open_sockets = NULL; -/** The size of open_sockets, in bits. */ -static int max_socket = -1; -#endif - -/** Count of number of sockets currently open. (Undercounts sockets opened by - * eventdns and libevent.) */ -static int n_sockets_open = 0; - -/** Mutex to protect open_sockets, max_socket, and n_sockets_open. */ -static tor_mutex_t *socket_accounting_mutex = NULL; - -/** Helper: acquire the socket accounting lock. */ -static INLINE void -socket_accounting_lock(void) -{ - if (PREDICT_UNLIKELY(!socket_accounting_mutex)) - socket_accounting_mutex = tor_mutex_new(); - tor_mutex_acquire(socket_accounting_mutex); -} - -/** Helper: release the socket accounting lock. */ -static INLINE void -socket_accounting_unlock(void) -{ - tor_mutex_release(socket_accounting_mutex); -} - -/** As close(), but guaranteed to work for sockets across platforms (including - * Windows, where close()ing a socket doesn't work. Returns 0 on success and - * the socket error code on failure. */ -int -tor_close_socket_simple(tor_socket_t s) -{ - int r = 0; - - /* On Windows, you have to call close() on fds returned by open(), - * and closesocket() on fds returned by socket(). On Unix, everything - * gets close()'d. We abstract this difference by always using - * tor_close_socket to close sockets, and always using close() on - * files. - */ - #if defined(_WIN32) - r = closesocket(s); - #else - r = close(s); - #endif - - if (r != 0) { - int err = tor_socket_errno(-1); - log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); - return err; - } - - return r; -} - -/** As tor_close_socket_simple(), but keeps track of the number - * of open sockets. Returns 0 on success, -1 on failure. */ -int -tor_close_socket(tor_socket_t s) -{ - int r = tor_close_socket_simple(s); - - socket_accounting_lock(); -#ifdef DEBUG_SOCKET_COUNTING - if (s > max_socket || ! bitarray_is_set(open_sockets, s)) { - log_warn(LD_BUG, "Closing a socket (%d) that wasn't returned by tor_open_" - "socket(), or that was already closed or something.", s); - } else { - tor_assert(open_sockets && s <= max_socket); - bitarray_clear(open_sockets, s); - } -#endif - if (r == 0) { - --n_sockets_open; - } else { -#ifdef _WIN32 - if (r != WSAENOTSOCK) - --n_sockets_open; -#else - if (r != EBADF) - --n_sockets_open; -#endif - r = -1; - } - - if (n_sockets_open < 0) - log_warn(LD_BUG, "Our socket count is below zero: %d. Please submit a " - "bug report.", n_sockets_open); - socket_accounting_unlock(); - return r; -} - -/** @{ */ -#ifdef DEBUG_SOCKET_COUNTING -/** Helper: if DEBUG_SOCKET_COUNTING is enabled, remember that s is - * now an open socket. */ -static INLINE void -mark_socket_open(tor_socket_t s) -{ - /* XXXX This bitarray business will NOT work on windows: sockets aren't - small ints there. */ - if (s > max_socket) { - if (max_socket == -1) { - open_sockets = bitarray_init_zero(s+128); - max_socket = s+128; - } else { - open_sockets = bitarray_expand(open_sockets, max_socket, s+128); - max_socket = s+128; - } - } - if (bitarray_is_set(open_sockets, s)) { - log_warn(LD_BUG, "I thought that %d was already open, but socket() just " - "gave it to me!", s); - } - bitarray_set(open_sockets, s); -} -#else -#define mark_socket_open(s) STMT_NIL -#endif -/** @} */ - -/** As socket(), but counts the number of open sockets. */ -tor_socket_t -tor_open_socket(int domain, int type, int protocol) -{ - return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); -} - -/** As socket(), but creates a nonblocking socket and - * counts the number of open sockets. */ -tor_socket_t -tor_open_socket_nonblocking(int domain, int type, int protocol) -{ - return tor_open_socket_with_extensions(domain, type, protocol, 1, 1); -} - -/** As socket(), but counts the number of open sockets and handles - * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. - * cloexec and nonblock should be either 0 or 1 to indicate - * if the corresponding extension should be used.*/ -tor_socket_t -tor_open_socket_with_extensions(int domain, int type, int protocol, - int cloexec, int nonblock) -{ - tor_socket_t s; -#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) - int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | - (nonblock ? SOCK_NONBLOCK : 0); - s = socket(domain, type|ext_flags, protocol); - if (SOCKET_OK(s)) - goto socket_ok; - /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK - * support, we are running on one without. */ - if (errno != EINVAL) - return s; -#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */ - - s = socket(domain, type, protocol); - if (! SOCKET_OK(s)) - return s; - -#if defined(FD_CLOEXEC) - if (cloexec) { - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } -#else - (void)cloexec; -#endif - - if (nonblock) { - if (set_socket_nonblocking(s) == -1) { - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } - - goto socket_ok; /* So that socket_ok will not be unused. */ - - socket_ok: - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - return s; -} - -/** As accept(), but counts the number of open sockets. */ -tor_socket_t -tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) -{ - return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0); -} - -/** As accept(), but returns a nonblocking socket and - * counts the number of open sockets. */ -tor_socket_t -tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr, - socklen_t *len) -{ - return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1); -} - -/** As accept(), but counts the number of open sockets and handles - * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. - * cloexec and nonblock should be either 0 or 1 to indicate - * if the corresponding extension should be used.*/ -tor_socket_t -tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, - socklen_t *len, int cloexec, int nonblock) -{ - tor_socket_t s; -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) - int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | - (nonblock ? SOCK_NONBLOCK : 0); - s = accept4(sockfd, addr, len, ext_flags); - if (SOCKET_OK(s)) - goto socket_ok; - /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, - * even though we were built on a system with accept4 support, we - * are running on one without. Also, check for EINVAL, which indicates that - * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */ - if (errno != EINVAL && errno != ENOSYS) - return s; -#endif - - s = accept(sockfd, addr, len); - if (!SOCKET_OK(s)) - return s; - -#if defined(FD_CLOEXEC) - if (cloexec) { - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } -#else - (void)cloexec; -#endif - - if (nonblock) { - if (set_socket_nonblocking(s) == -1) { - tor_close_socket_simple(s); - return TOR_INVALID_SOCKET; - } - } - - goto socket_ok; /* So that socket_ok will not be unused. */ - - socket_ok: - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - return s; -} - -/** Return the number of sockets we currently have opened. */ -int -get_n_open_sockets(void) -{ - int n; - socket_accounting_lock(); - n = n_sockets_open; - socket_accounting_unlock(); - return n; -} - -/** Turn socket into a nonblocking socket. Return 0 on success, -1 - * on failure. - */ -int -set_socket_nonblocking(tor_socket_t socket) -{ -#if defined(_WIN32) - unsigned long nonblocking = 1; - ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking); -#else - int flags; - - flags = fcntl(socket, F_GETFL, 0); - if (flags == -1) { - log_warn(LD_NET, "Couldn't get file status flags: %s", strerror(errno)); - return -1; - } - flags |= O_NONBLOCK; - if (fcntl(socket, F_SETFL, flags) == -1) { - log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno)); - return -1; - } -#endif - - return 0; -} - -/** - * Allocate a pair of connected sockets. (Like socketpair(family, - * type,protocol,fd), but works on systems that don't have - * socketpair.) - * - * Currently, only (AF_UNIX, SOCK_STREAM, 0) sockets are supported. - * - * Note that on systems without socketpair, this call will fail if - * localhost is inaccessible (for example, if the networking - * stack is down). And even if it succeeds, the socket pair will not - * be able to read while localhost is down later (the socket pair may - * even close, depending on OS-specific timeouts). - * - * Returns 0 on success and -errno on failure; do not rely on the value - * of errno or WSAGetLastError(). - **/ -/* It would be nicer just to set errno, but that won't work for windows. */ -int -tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) -{ -//don't use win32 socketpairs (they are always bad) -#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32) - int r; - -#ifdef SOCK_CLOEXEC - r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd); - if (r == 0) - goto sockets_ok; - /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC support, we - * are running on one without. */ - if (errno != EINVAL) - return -errno; -#endif - - r = socketpair(family, type, protocol, fd); - if (r < 0) - return -errno; - -#if defined(FD_CLOEXEC) - if (SOCKET_OK(fd[0])) { - r = fcntl(fd[0], F_SETFD, FD_CLOEXEC); - if (r == -1) { - close(fd[0]); - close(fd[1]); - return -errno; - } - } - if (SOCKET_OK(fd[1])) { - r = fcntl(fd[1], F_SETFD, FD_CLOEXEC); - if (r == -1) { - close(fd[0]); - close(fd[1]); - return -errno; - } - } -#endif - goto sockets_ok; /* So that sockets_ok will not be unused. */ - - sockets_ok: - socket_accounting_lock(); - if (SOCKET_OK(fd[0])) { - ++n_sockets_open; - mark_socket_open(fd[0]); - } - if (SOCKET_OK(fd[1])) { - ++n_sockets_open; - mark_socket_open(fd[1]); - } - socket_accounting_unlock(); - - return 0; -#else - return tor_ersatz_socketpair(family, type, protocol, fd); -#endif -} - -#ifdef NEED_ERSATZ_SOCKETPAIR -/** - * Helper used to implement socketpair on systems that lack it, by - * making a direct connection to localhost. - */ -STATIC int -tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) -{ - /* This socketpair does not work when localhost is down. So - * it's really not the same thing at all. But it's close enough - * for now, and really, when localhost is down sometimes, we - * have other problems too. - */ - tor_socket_t listener = TOR_INVALID_SOCKET; - tor_socket_t connector = TOR_INVALID_SOCKET; - tor_socket_t acceptor = TOR_INVALID_SOCKET; - struct sockaddr_in listen_addr; - struct sockaddr_in connect_addr; - socklen_t size; - int saved_errno = -1; - - if (protocol -#ifdef AF_UNIX - || family != AF_UNIX -#endif - ) { -#ifdef _WIN32 - return -WSAEAFNOSUPPORT; -#else - return -EAFNOSUPPORT; -#endif - } - if (!fd) { - return -EINVAL; - } - - listener = tor_open_socket(AF_INET, type, 0); - if (!SOCKET_OK(listener)) - return -tor_socket_errno(-1); - memset(&listen_addr, 0, sizeof(listen_addr)); - listen_addr.sin_family = AF_INET; - listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - listen_addr.sin_port = 0; /* kernel chooses port. */ - if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr)) - == -1) - goto tidy_up_and_fail; - if (listen(listener, 1) == -1) - goto tidy_up_and_fail; - - connector = tor_open_socket(AF_INET, type, 0); - if (!SOCKET_OK(connector)) - goto tidy_up_and_fail; - /* We want to find out the port number to connect to. */ - size = sizeof(connect_addr); - if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1) - goto tidy_up_and_fail; - if (size != sizeof (connect_addr)) - goto abort_tidy_up_and_fail; - if (connect(connector, (struct sockaddr *) &connect_addr, - sizeof(connect_addr)) == -1) - goto tidy_up_and_fail; - - size = sizeof(listen_addr); - acceptor = tor_accept_socket(listener, - (struct sockaddr *) &listen_addr, &size); - if (!SOCKET_OK(acceptor)) - goto tidy_up_and_fail; - if (size != sizeof(listen_addr)) - goto abort_tidy_up_and_fail; - /* Now check we are talking to ourself by matching port and host on the - two sockets. */ - if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1) - goto tidy_up_and_fail; - if (size != sizeof (connect_addr) - || listen_addr.sin_family != connect_addr.sin_family - || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr - || listen_addr.sin_port != connect_addr.sin_port) { - goto abort_tidy_up_and_fail; - } - tor_close_socket(listener); - fd[0] = connector; - fd[1] = acceptor; - - return 0; - - abort_tidy_up_and_fail: -#ifdef _WIN32 - saved_errno = WSAECONNABORTED; -#else - saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ -#endif - tidy_up_and_fail: - if (saved_errno < 0) - saved_errno = errno; - if (SOCKET_OK(listener)) - tor_close_socket(listener); - if (SOCKET_OK(connector)) - tor_close_socket(connector); - if (SOCKET_OK(acceptor)) - tor_close_socket(acceptor); - return -saved_errno; -} -#endif - -/** Number of extra file descriptors to keep in reserve beyond those that we - * tell Tor it's allowed to use. */ -#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */ - -/** Learn the maximum allowed number of file descriptors, and tell the system - * we want to use up to that number. (Some systems have a low soft limit, and - * let us set it higher.) - * - * We compute this by finding the largest number that we can use. - * If we can't find a number greater than or equal to limit, - * then we fail: return -1. - * - * If limit is 0, then do not adjust the current maximum. - * - * Otherwise, return 0 and store the maximum we found inside max_out.*/ -int -set_max_file_descriptors(rlim_t limit, int *max_out) -{ - /* Define some maximum connections values for systems where we cannot - * automatically determine a limit. Re Cygwin, see - * http://archives.seul.org/or/talk/Aug-2006/msg00210.html - * For an iPhone, 9999 should work. For Windows and all other unknown - * systems we use 15000 as the default. */ -#ifndef HAVE_GETRLIMIT -#if defined(CYGWIN) || defined(__CYGWIN__) - const char *platform = "Cygwin"; - const unsigned long MAX_CONNECTIONS = 3200; -#elif defined(_WIN32) - const char *platform = "Windows"; - const unsigned long MAX_CONNECTIONS = 15000; -#else - const char *platform = "unknown platforms with no getrlimit()"; - const unsigned long MAX_CONNECTIONS = 15000; -#endif - log_fn(LOG_INFO, LD_NET, - "This platform is missing getrlimit(). Proceeding."); - if (limit > MAX_CONNECTIONS) { - log_warn(LD_CONFIG, - "We do not support more than %lu file descriptors " - "on %s. Tried to raise to %lu.", - (unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit); - return -1; - } - limit = MAX_CONNECTIONS; -#else /* HAVE_GETRLIMIT */ - struct rlimit rlim; - - if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { - log_warn(LD_NET, "Could not get maximum number of file descriptors: %s", - strerror(errno)); - return -1; - } - if (limit == 0) { - /* If limit == 0, return the maximum value without setting it. */ - limit = rlim.rlim_max; - if (limit > INT_MAX) - limit = INT_MAX; - *max_out = (int)limit - ULIMIT_BUFFER; - return 0; - } - if (rlim.rlim_max < limit) { - log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're " - "limited to %lu. Please change your ulimit -n.", - (unsigned long)limit, (unsigned long)rlim.rlim_max); - return -1; - } - - if (rlim.rlim_max > rlim.rlim_cur) { - log_info(LD_NET,"Raising max file descriptors from %lu to %lu.", - (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max); - } - rlim.rlim_cur = rlim.rlim_max; - - if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { - int bad = 1; -#ifdef OPEN_MAX - if (errno == EINVAL && OPEN_MAX < rlim.rlim_cur) { - /* On some platforms, OPEN_MAX is the real limit, and getrlimit() is - * full of nasty lies. I'm looking at you, OSX 10.5.... */ - rlim.rlim_cur = OPEN_MAX; - if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) { - if (rlim.rlim_cur < (rlim_t)limit) { - log_warn(LD_CONFIG, "We are limited to %lu file descriptors by " - "OPEN_MAX, and ConnLimit is %lu. Changing ConnLimit; sorry.", - (unsigned long)OPEN_MAX, (unsigned long)limit); - } else { - log_info(LD_CONFIG, "Dropped connection limit to OPEN_MAX (%lu); " - "Apparently, %lu was too high and rlimit lied to us.", - (unsigned long)OPEN_MAX, (unsigned long)rlim.rlim_max); - } - bad = 0; - } - } -#endif /* OPEN_MAX */ - if (bad) { - log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s", - strerror(errno)); - return -1; - } - } - /* leave some overhead for logs, etc, */ - limit = rlim.rlim_cur; -#endif /* HAVE_GETRLIMIT */ - - if (limit < ULIMIT_BUFFER) { - log_warn(LD_CONFIG, - "ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER); - return -1; - } - if (limit > INT_MAX) - limit = INT_MAX; - tor_assert(max_out); - *max_out = (int)limit - ULIMIT_BUFFER; - return 0; -} - -#ifndef _WIN32 -/** Log details of current user and group credentials. Return 0 on - * success. Logs and return -1 on failure. - */ -static int -log_credential_status(void) -{ -/** Log level to use when describing non-error UID/GID status. */ -#define CREDENTIAL_LOG_LEVEL LOG_INFO - /* Real, effective and saved UIDs */ - uid_t ruid, euid, suid; - /* Read, effective and saved GIDs */ - gid_t rgid, egid, sgid; - /* Supplementary groups */ - gid_t *sup_gids = NULL; - int sup_gids_size; - /* Number of supplementary groups */ - int ngids; - - /* log UIDs */ -#ifdef HAVE_GETRESUID - if (getresuid(&ruid, &euid, &suid) != 0 ) { - log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno)); - return -1; - } else { - log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, - "UID is %u (real), %u (effective), %u (saved)", - (unsigned)ruid, (unsigned)euid, (unsigned)suid); - } -#else - /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */ - ruid = getuid(); - euid = geteuid(); - (void)suid; - - log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, - "UID is %u (real), %u (effective), unknown (saved)", - (unsigned)ruid, (unsigned)euid); -#endif - - /* log GIDs */ -#ifdef HAVE_GETRESGID - if (getresgid(&rgid, &egid, &sgid) != 0 ) { - log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno)); - return -1; - } else { - log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, - "GID is %u (real), %u (effective), %u (saved)", - (unsigned)rgid, (unsigned)egid, (unsigned)sgid); - } -#else - /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */ - rgid = getgid(); - egid = getegid(); - (void)sgid; - log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, - "GID is %u (real), %u (effective), unknown (saved)", - (unsigned)rgid, (unsigned)egid); -#endif - - /* log supplementary groups */ - sup_gids_size = 64; - sup_gids = tor_malloc(sizeof(gid_t) * 64); - while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 && - errno == EINVAL && - sup_gids_size < NGROUPS_MAX) { - sup_gids_size *= 2; - sup_gids = tor_realloc(sup_gids, sizeof(gid_t) * sup_gids_size); - } - - if (ngids < 0) { - log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s", - strerror(errno)); - tor_free(sup_gids); - return -1; - } else { - int i, retval = 0; - char *s = NULL; - smartlist_t *elts = smartlist_new(); - - for (i = 0; iuser and switch to their - * primary group. Return 0 on success. On failure, log and return -1. - */ -int -switch_id(const char *user) -{ -#ifndef _WIN32 - struct passwd *pw = NULL; - uid_t old_uid; - gid_t old_gid; - static int have_already_switched_id = 0; - - tor_assert(user); - - if (have_already_switched_id) - return 0; - - /* Log the initial credential state */ - if (log_credential_status()) - return -1; - - log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups"); - - /* Get old UID/GID to check if we changed correctly */ - old_uid = getuid(); - old_gid = getgid(); - - /* Lookup the user and group information, if we have a problem, bail out. */ - pw = getpwnam(user); - if (pw == NULL) { - log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); - return -1; - } - - /* Properly switch egid,gid,euid,uid here or bail out */ - if (setgroups(1, &pw->pw_gid)) { - log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".", - (int)pw->pw_gid, strerror(errno)); - if (old_uid == pw->pw_uid) { - log_warn(LD_GENERAL, "Tor is already running as %s. You do not need " - "the \"User\" option if you are already running as the user " - "you want to be. (If you did not set the User option in your " - "torrc, check whether it was specified on the command line " - "by a startup script.)", user); - } else { - log_warn(LD_GENERAL, "If you set the \"User\" option, you must start Tor" - " as root."); - } - return -1; - } - - if (setegid(pw->pw_gid)) { - log_warn(LD_GENERAL, "Error setting egid to %d: %s", - (int)pw->pw_gid, strerror(errno)); - return -1; - } - - if (setgid(pw->pw_gid)) { - log_warn(LD_GENERAL, "Error setting gid to %d: %s", - (int)pw->pw_gid, strerror(errno)); - return -1; - } - - if (setuid(pw->pw_uid)) { - log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s", - user, (int)pw->pw_uid, strerror(errno)); - return -1; - } - - if (seteuid(pw->pw_uid)) { - log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s", - user, (int)pw->pw_uid, strerror(errno)); - return -1; - } - - /* This is how OpenBSD rolls: - if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) || - setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) { - setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { - log_warn(LD_GENERAL, "Error setting configured UID/GID: %s", - strerror(errno)); - return -1; - } - */ - - /* We've properly switched egid, gid, euid, uid, and supplementary groups if - * we're here. */ - -#if !defined(CYGWIN) && !defined(__CYGWIN__) - /* If we tried to drop privilege to a group/user other than root, attempt to - * restore root (E)(U|G)ID, and abort if the operation succeeds */ - - /* Only check for privilege dropping if we were asked to be non-root */ - if (pw->pw_uid) { - /* Try changing GID/EGID */ - if (pw->pw_gid != old_gid && - (setgid(old_gid) != -1 || setegid(old_gid) != -1)) { - log_warn(LD_GENERAL, "Was able to restore group credentials even after " - "switching GID: this means that the setgid code didn't work."); - return -1; - } - - /* Try changing UID/EUID */ - if (pw->pw_uid != old_uid && - (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) { - log_warn(LD_GENERAL, "Was able to restore user credentials even after " - "switching UID: this means that the setuid code didn't work."); - return -1; - } - } -#endif - - /* Check what really happened */ - if (log_credential_status()) { - return -1; - } - - have_already_switched_id = 1; /* mark success so we never try again */ - -#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL) -#ifdef PR_SET_DUMPABLE - if (pw->pw_uid) { - /* Re-enable core dumps if we're not running as root. */ - log_info(LD_CONFIG, "Re-enabling coredumps"); - if (prctl(PR_SET_DUMPABLE, 1)) { - log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno)); - } - } -#endif -#endif - return 0; - -#else - (void)user; - - log_warn(LD_CONFIG, - "User specified but switching users is unsupported on your OS."); - return -1; -#endif -} - -/* We only use the linux prctl for now. There is no Win32 support; this may - * also work on various BSD systems and Mac OS X - send testing feedback! - * - * On recent Gnu/Linux kernels it is possible to create a system-wide policy - * that will prevent non-root processes from attaching to other processes - * unless they are the parent process; thus gdb can attach to programs that - * they execute but they cannot attach to other processes running as the same - * user. The system wide policy may be set with the sysctl - * kernel.yama.ptrace_scope or by inspecting - * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04. - * - * This ptrace scope will be ignored on Gnu/Linux for users with - * CAP_SYS_PTRACE and so it is very likely that root will still be able to - * attach to the Tor process. - */ -/** Attempt to disable debugger attachment: return 1 on success, -1 on - * failure, and 0 if we don't know how to try on this platform. */ -int -tor_disable_debugger_attach(void) -{ - int r, attempted; - r = -1; - attempted = 0; - log_debug(LD_CONFIG, - "Attemping to disable debugger attachment to Tor for " - "unprivileged users."); -#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL) -#ifdef PR_SET_DUMPABLE - attempted = 1; - r = prctl(PR_SET_DUMPABLE, 0); -#endif -#endif -#if defined(__APPLE__) && defined(PT_DENY_ATTACH) - if (r < 0) { - attempted = 1; - r = ptrace(PT_DENY_ATTACH, 0, 0, 0); - } -#endif - - // XXX: TODO - Mac OS X has dtrace and this may be disabled. - // XXX: TODO - Windows probably has something similar - if (r == 0 && attempted) { - log_debug(LD_CONFIG,"Debugger attachment disabled for " - "unprivileged users."); - return 1; - } else if (attempted) { - log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s", - strerror(errno)); - } - return r; -} - -#ifdef HAVE_PWD_H -/** Allocate and return a string containing the home directory for the - * user username. Only works on posix-like systems. */ -char * -get_user_homedir(const char *username) -{ - struct passwd *pw; - tor_assert(username); - - if (!(pw = getpwnam(username))) { - log_err(LD_CONFIG,"User \"%s\" not found.", username); - return NULL; - } - return tor_strdup(pw->pw_dir); -} -#endif - -/** Modify fname to contain the name of its parent directory. Doesn't - * actually examine the filesystem; does a purely syntactic modification. - * - * The parent of the root director is considered to be iteself. - * - * Path separators are the forward slash (/) everywhere and additionally - * the backslash (\) on Win32. - * - * Cuts off any number of trailing path separators but otherwise ignores - * them for purposes of finding the parent directory. - * - * Returns 0 if a parent directory was successfully found, -1 otherwise (fname - * did not have any path separators or only had them at the end). - * */ -int -get_parent_directory(char *fname) -{ - char *cp; - int at_end = 1; - tor_assert(fname); -#ifdef _WIN32 - /* If we start with, say, c:, then don't consider that the start of the path - */ - if (fname[0] && fname[1] == ':') { - fname += 2; - } -#endif - /* Now we want to remove all path-separators at the end of the string, - * and to remove the end of the string starting with the path separator - * before the last non-path-separator. In perl, this would be - * s#[/]*$##; s#/[^/]*$##; - * on a unixy platform. - */ - cp = fname + strlen(fname); - at_end = 1; - while (--cp >= fname) { - int is_sep = (*cp == '/' -#ifdef _WIN32 - || *cp == '\\' -#endif - ); - if (is_sep) { - if (cp == fname) { - /* This is the first separator in the file name; don't remove it! */ - cp[1] = '\0'; - return 0; - } - *cp = '\0'; - if (! at_end) - return 0; - } else { - at_end = 0; - } - } - return -1; -} - -#ifndef _WIN32 -/** Return a newly allocated string containing the output of getcwd(). Return - * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since - * Hurd hasn't got a PATH_MAX.) - */ -static char * -alloc_getcwd(void) -{ - int saved_errno = errno; -/* We use this as a starting path length. Not too large seems sane. */ -#define START_PATH_LENGTH 128 -/* Nobody has a maxpath longer than this, as far as I know. And if they - * do, they shouldn't. */ -#define MAX_SANE_PATH_LENGTH 4096 - size_t path_length = START_PATH_LENGTH; - char *path = tor_malloc(path_length); - - errno = 0; - while (getcwd(path, path_length) == NULL) { - if (errno == ERANGE && path_length < MAX_SANE_PATH_LENGTH) { - path_length*=2; - path = tor_realloc(path, path_length); - } else { - tor_free(path); - path = NULL; - break; - } - } - errno = saved_errno; - return path; -} -#endif - -/** Expand possibly relative path fname to an absolute path. - * Return a newly allocated string, possibly equal to fname. */ -char * -make_path_absolute(char *fname) -{ -#ifdef _WIN32 - char *absfname_malloced = _fullpath(NULL, fname, 1); - - /* We don't want to assume that tor_free can free a string allocated - * with malloc. On failure, return fname (it's better than nothing). */ - char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname); - if (absfname_malloced) free(absfname_malloced); - - return absfname; -#else - char *absfname = NULL, *path = NULL; - - tor_assert(fname); - - if (fname[0] == '/') { - absfname = tor_strdup(fname); - } else { - path = alloc_getcwd(); - if (path) { - tor_asprintf(&absfname, "%s/%s", path, fname); - tor_free(path); - } else { - /* If getcwd failed, the best we can do here is keep using the - * relative path. (Perhaps / isn't readable by this UID/GID.) */ - log_warn(LD_GENERAL, "Unable to find current working directory: %s", - strerror(errno)); - absfname = tor_strdup(fname); - } - } - return absfname; -#endif -} - -#ifndef HAVE__NSGETENVIRON -#ifndef HAVE_EXTERN_ENVIRON_DECLARED -/* Some platforms declare environ under some circumstances, others don't. */ -#ifndef RUNNING_DOXYGEN -extern char **environ; -#endif -#endif -#endif - -/** Return the current environment. This is a portable replacement for - * 'environ'. */ -char ** -get_environment(void) -{ -#ifdef HAVE__NSGETENVIRON - /* This is for compatibility between OSX versions. Otherwise (for example) - * when we do a mostly-static build on OSX 10.7, the resulting binary won't - * work on OSX 10.6. */ - return *_NSGetEnviron(); -#else - return environ; -#endif -} - -/** Set *addr to the IP address (in dotted-quad notation) stored in c. - * Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr), - * but works on Windows and Solaris.) - */ -int -tor_inet_aton(const char *str, struct in_addr* addr) -{ - unsigned a,b,c,d; - char more; - if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4) - return 0; - if (a > 255) return 0; - if (b > 255) return 0; - if (c > 255) return 0; - if (d > 255) return 0; - addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d); - return 1; -} - -/** Given af==AF_INET and src a struct in_addr, or - * af==AF_INET6 and src a struct in6_addr, try to format the - * address and store it in the len-byte buffer dst. Returns - * dst on success, NULL on failure. - * - * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it: - * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6 - * support.) */ -const char * -tor_inet_ntop(int af, const void *src, char *dst, size_t len) -{ - if (af == AF_INET) { - if (tor_inet_ntoa(src, dst, len) < 0) - return NULL; - else - return dst; - } else if (af == AF_INET6) { - const struct in6_addr *addr = src; - char buf[64], *cp; - int longestGapLen = 0, longestGapPos = -1, i, - curGapPos = -1, curGapLen = 0; - uint16_t words[8]; - for (i = 0; i < 8; ++i) { - words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1]; - } - if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && - words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) || - (words[5] == 0xffff))) { - /* This is an IPv4 address. */ - if (words[5] == 0) { - tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d", - addr->s6_addr[12], addr->s6_addr[13], - addr->s6_addr[14], addr->s6_addr[15]); - } else { - tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5], - addr->s6_addr[12], addr->s6_addr[13], - addr->s6_addr[14], addr->s6_addr[15]); - } - if ((strlen(buf) + 1) > len) /* +1 for \0 */ - return NULL; - strlcpy(dst, buf, len); - return dst; - } - i = 0; - while (i < 8) { - if (words[i] == 0) { - curGapPos = i++; - curGapLen = 1; - while (i<8 && words[i] == 0) { - ++i; ++curGapLen; - } - if (curGapLen > longestGapLen) { - longestGapPos = curGapPos; - longestGapLen = curGapLen; - } - } else { - ++i; - } - } - if (longestGapLen<=1) - longestGapPos = -1; - - cp = buf; - for (i = 0; i < 8; ++i) { - if (words[i] == 0 && longestGapPos == i) { - if (i == 0) - *cp++ = ':'; - *cp++ = ':'; - while (i < 8 && words[i] == 0) - ++i; - --i; /* to compensate for loop increment. */ - } else { - tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]); - cp += strlen(cp); - if (i != 7) - *cp++ = ':'; - } - } - *cp = '\0'; - if ((strlen(buf) + 1) > len) /* +1 for \0 */ - return NULL; - strlcpy(dst, buf, len); - return dst; - } else { - return NULL; - } -} - -/** Given af==AF_INET or af==AF_INET6, and a string src - * encoding an IPv4 address or IPv6 address correspondingly, try to parse the - * address and store the result in dst (which must have space for a - * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success, - * 0 on a bad parse, and -1 on a bad af. - * - * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor - * sometimes needs to format ipv6 addresses even on platforms without ipv6 - * support.) */ -int -tor_inet_pton(int af, const char *src, void *dst) -{ - if (af == AF_INET) { - return tor_inet_aton(src, dst); - } else if (af == AF_INET6) { - struct in6_addr *out = dst; - uint16_t words[8]; - int gapPos = -1, i, setWords=0; - const char *dot = strchr(src, '.'); - const char *eow; /* end of words. */ - if (dot == src) - return 0; - else if (!dot) - eow = src+strlen(src); - else { - unsigned byte1,byte2,byte3,byte4; - char more; - for (eow = dot-1; eow >= src && TOR_ISDIGIT(*eow); --eow) - ; - ++eow; - - /* We use "scanf" because some platform inet_aton()s are too lax - * about IPv4 addresses of the form "1.2.3" */ - if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c", - &byte1,&byte2,&byte3,&byte4,&more) != 4) - return 0; - - if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255) - return 0; - - words[6] = (byte1<<8) | byte2; - words[7] = (byte3<<8) | byte4; - setWords += 2; - } - - i = 0; - while (src < eow) { - if (i > 7) - return 0; - if (TOR_ISXDIGIT(*src)) { - char *next; - ssize_t len; - long r = strtol(src, &next, 16); - tor_assert(next != NULL); - tor_assert(next != src); - - len = *next == '\0' ? eow - src : next - src; - if (len > 4) - return 0; - if (len > 1 && !TOR_ISXDIGIT(src[1])) - return 0; /* 0x is not valid */ - - tor_assert(r >= 0); - tor_assert(r < 65536); - words[i++] = (uint16_t)r; - setWords++; - src = next; - if (*src != ':' && src != eow) - return 0; - ++src; - } else if (*src == ':' && i > 0 && gapPos == -1) { - gapPos = i; - ++src; - } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && - gapPos == -1) { - gapPos = i; - src += 2; - } else { - return 0; - } - } - - if (setWords > 8 || - (setWords == 8 && gapPos != -1) || - (setWords < 8 && gapPos == -1)) - return 0; - - if (gapPos >= 0) { - int nToMove = setWords - (dot ? 2 : 0) - gapPos; - int gapLen = 8 - setWords; - tor_assert(nToMove >= 0); - memmove(&words[gapPos+gapLen], &words[gapPos], - sizeof(uint16_t)*nToMove); - memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen); - } - for (i = 0; i < 8; ++i) { - out->s6_addr[2*i ] = words[i] >> 8; - out->s6_addr[2*i+1] = words[i] & 0xff; - } - - return 1; - } else { - return -1; - } -} - -/** Similar behavior to Unix gethostbyname: resolve name, and set - * *addr to the proper IP address, in host byte order. Returns 0 - * on success, -1 on failure; 1 on transient failure. - * - * (This function exists because standard windows gethostbyname - * doesn't treat raw IP addresses properly.) - */ -int -tor_lookup_hostname(const char *name, uint32_t *addr) -{ - tor_addr_t myaddr; - int ret; - - if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) - return ret; - - if (tor_addr_family(&myaddr) == AF_INET) { - *addr = tor_addr_to_ipv4h(&myaddr); - return ret; - } - - return -1; -} - -/** Hold the result of our call to uname. */ -static char uname_result[256]; -/** True iff uname_result is set. */ -static int uname_result_is_set = 0; - -/** Return a pointer to a description of our platform. - */ -const char * -get_uname(void) -{ -#ifdef HAVE_UNAME - struct utsname u; -#endif - if (!uname_result_is_set) { -#ifdef HAVE_UNAME - if (uname(&u) != -1) { - /* (Linux says 0 is success, Solaris says 1 is success) */ - strlcpy(uname_result, u.sysname, sizeof(uname_result)); - } else -#endif - { -#ifdef _WIN32 - OSVERSIONINFOEX info; - int i; - const char *plat = NULL; - static struct { - unsigned major; unsigned minor; const char *version; - } win_version_table[] = { - { 6, 2, "Windows 8" }, - { 6, 1, "Windows 7" }, - { 6, 0, "Windows Vista" }, - { 5, 2, "Windows Server 2003" }, - { 5, 1, "Windows XP" }, - { 5, 0, "Windows 2000" }, - /* { 4, 0, "Windows NT 4.0" }, */ - { 4, 90, "Windows Me" }, - { 4, 10, "Windows 98" }, - /* { 4, 0, "Windows 95" } */ - { 3, 51, "Windows NT 3.51" }, - { 0, 0, NULL } - }; - memset(&info, 0, sizeof(info)); - info.dwOSVersionInfoSize = sizeof(info); - if (! GetVersionEx((LPOSVERSIONINFO)&info)) { - strlcpy(uname_result, "Bizarre version of Windows where GetVersionEx" - " doesn't work.", sizeof(uname_result)); - uname_result_is_set = 1; - return uname_result; - } - if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { - if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) - plat = "Windows NT 4.0"; - else - plat = "Windows 95"; - } else { - for (i=0; win_version_table[i].major>0; ++i) { - if (win_version_table[i].major == info.dwMajorVersion && - win_version_table[i].minor == info.dwMinorVersion) { - plat = win_version_table[i].version; - break; - } - } - } - if (plat) { - strlcpy(uname_result, plat, sizeof(uname_result)); - } else { - if (info.dwMajorVersion > 6 || - (info.dwMajorVersion==6 && info.dwMinorVersion>2)) - tor_snprintf(uname_result, sizeof(uname_result), - "Very recent version of Windows [major=%d,minor=%d]", - (int)info.dwMajorVersion,(int)info.dwMinorVersion); - else - tor_snprintf(uname_result, sizeof(uname_result), - "Unrecognized version of Windows [major=%d,minor=%d]", - (int)info.dwMajorVersion,(int)info.dwMinorVersion); - } -#if !defined (WINCE) -#ifdef VER_NT_SERVER - if (info.wProductType == VER_NT_SERVER || - info.wProductType == VER_NT_DOMAIN_CONTROLLER) { - strlcat(uname_result, " [server]", sizeof(uname_result)); - } -#endif -#endif -#else - strlcpy(uname_result, "Unknown platform", sizeof(uname_result)); -#endif - } - uname_result_is_set = 1; - } - return uname_result; -} - -/* - * Process control - */ - -#if defined(USE_PTHREADS) -/** Wraps a void (*)(void*) function and its argument so we can - * invoke them in a way pthreads would expect. - */ -typedef struct tor_pthread_data_t { - void (*func)(void *); - void *data; -} tor_pthread_data_t; -/** Given a tor_pthread_data_t _data, call _data->func(d->data) - * and free _data. Used to make sure we can call functions the way pthread - * expects. */ -static void * -tor_pthread_helper_fn(void *_data) -{ - tor_pthread_data_t *data = _data; - void (*func)(void*); - void *arg; - /* mask signals to worker threads to avoid SIGPIPE, etc */ - sigset_t sigs; - /* We're in a subthread; don't handle any signals here. */ - sigfillset(&sigs); - pthread_sigmask(SIG_SETMASK, &sigs, NULL); - - func = data->func; - arg = data->data; - tor_free(_data); - func(arg); - return NULL; -} -#endif - -/** Minimalist interface to run a void function in the background. On - * Unix calls fork, on win32 calls beginthread. Returns -1 on failure. - * func should not return, but rather should call spawn_exit. - * - * NOTE: if data is used, it should not be allocated on the stack, - * since in a multithreaded environment, there is no way to be sure that - * the caller's stack will still be around when the called function is - * running. - */ -int -spawn_func(void (*func)(void *), void *data) -{ -#if defined(USE_WIN32_THREADS) - int rv; - rv = (int)_beginthread(func, 0, data); - if (rv == (int)-1) - return -1; - return 0; -#elif defined(USE_PTHREADS) - pthread_t thread; - tor_pthread_data_t *d; - d = tor_malloc(sizeof(tor_pthread_data_t)); - d->data = data; - d->func = func; - if (pthread_create(&thread,NULL,tor_pthread_helper_fn,d)) - return -1; - if (pthread_detach(thread)) - return -1; - return 0; -#else - pid_t pid; - pid = fork(); - if (pid<0) - return -1; - if (pid==0) { - /* Child */ - func(data); - tor_assert(0); /* Should never reach here. */ - return 0; /* suppress "control-reaches-end-of-non-void" warning. */ - } else { - /* Parent */ - return 0; - } -#endif -} - -/** End the current thread/process. - */ -void -spawn_exit(void) -{ -#if defined(USE_WIN32_THREADS) - _endthread(); - //we should never get here. my compiler thinks that _endthread returns, this - //is an attempt to fool it. - tor_assert(0); - _exit(0); -#elif defined(USE_PTHREADS) - pthread_exit(NULL); -#else - /* http://www.erlenstar.demon.co.uk/unix/faq_2.html says we should - * call _exit, not exit, from child processes. */ - _exit(0); -#endif -} - -/** Implementation logic for compute_num_cpus(). */ -static int -compute_num_cpus_impl(void) -{ -#ifdef _WIN32 - SYSTEM_INFO info; - memset(&info, 0, sizeof(info)); - GetSystemInfo(&info); - if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX) - return (int)info.dwNumberOfProcessors; - else - return -1; -#elif defined(HAVE_SYSCONF) -#ifdef _SC_NPROCESSORS_CONF - long cpus_conf = sysconf(_SC_NPROCESSORS_CONF); -#else - long cpus_conf = -1; -#endif -#ifdef _SC_NPROCESSORS_ONLN - long cpus_onln = sysconf(_SC_NPROCESSORS_ONLN); -#else - long cpus_onln = -1; -#endif - long cpus = -1; - - if (cpus_conf > 0 && cpus_onln < 0) { - cpus = cpus_conf; - } else if (cpus_onln > 0 && cpus_conf < 0) { - cpus = cpus_onln; - } else if (cpus_onln > 0 && cpus_conf > 0) { - if (cpus_onln < cpus_conf) { - log_notice(LD_GENERAL, "I think we have %ld CPUS, but only %ld of them " - "are available. Telling Tor to only use %ld. You can over" - "ride this with the NumCPUs option", - cpus_conf, cpus_onln, cpus_onln); - } - cpus = cpus_onln; - } - - if (cpus >= 1 && cpus < INT_MAX) - return (int)cpus; - else - return -1; -#else - return -1; -#endif -} - -#define MAX_DETECTABLE_CPUS 16 - -/** Return how many CPUs we are running with. We assume that nobody is - * using hot-swappable CPUs, so we don't recompute this after the first - * time. Return -1 if we don't know how to tell the number of CPUs on this - * system. - */ -int -compute_num_cpus(void) -{ - static int num_cpus = -2; - if (num_cpus == -2) { - num_cpus = compute_num_cpus_impl(); - tor_assert(num_cpus != -2); - if (num_cpus > MAX_DETECTABLE_CPUS) - log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I " - "will not autodetect any more than %d, though. If you " - "want to configure more, set NumCPUs in your torrc", - num_cpus, MAX_DETECTABLE_CPUS); - } - return num_cpus; -} - -/** Set *timeval to the current time of day. On error, log and terminate. - * (Same as gettimeofday(timeval,NULL), but never returns -1.) - */ -void -tor_gettimeofday(struct timeval *timeval) -{ -#ifdef _WIN32 - /* Epoch bias copied from perl: number of units between windows epoch and - * Unix epoch. */ -#define EPOCH_BIAS U64_LITERAL(116444736000000000) -#define UNITS_PER_SEC U64_LITERAL(10000000) -#define USEC_PER_SEC U64_LITERAL(1000000) -#define UNITS_PER_USEC U64_LITERAL(10) - union { - uint64_t ft_64; - FILETIME ft_ft; - } ft; -#if defined (WINCE) - /* wince do not have GetSystemTimeAsFileTime */ - SYSTEMTIME stime; - GetSystemTime(&stime); - SystemTimeToFileTime(&stime,&ft.ft_ft); -#else - /* number of 100-nsec units since Jan 1, 1601 */ - GetSystemTimeAsFileTime(&ft.ft_ft); -#endif - if (ft.ft_64 < EPOCH_BIAS) { - log_err(LD_GENERAL,"System time is before 1970; failing."); - exit(1); - } - ft.ft_64 -= EPOCH_BIAS; - timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC); - timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC); -#elif defined(HAVE_GETTIMEOFDAY) - if (gettimeofday(timeval, NULL)) { - log_err(LD_GENERAL,"gettimeofday failed."); - /* If gettimeofday dies, we have either given a bad timezone (we didn't), - or segfaulted.*/ - exit(1); - } -#elif defined(HAVE_FTIME) - struct timeb tb; - ftime(&tb); - timeval->tv_sec = tb.time; - timeval->tv_usec = tb.millitm * 1000; -#else -#error "No way to get time." -#endif - return; -} - -#if defined(TOR_IS_MULTITHREADED) && !defined(_WIN32) -/** Defined iff we need to add locks when defining fake versions of reentrant - * versions of time-related functions. */ -#define TIME_FNS_NEED_LOCKS -#endif - -/** Helper: Deal with confused or out-of-bounds values from localtime_r and - * friends. (On some platforms, they can give out-of-bounds values or can - * return NULL.) If islocal, this is a localtime result; otherwise - * it's from gmtime. The function returned r, when given timep - * as its input. If we need to store new results, store them in - * resultbuf. */ -static struct tm * -correct_tm(int islocal, const time_t *timep, struct tm *resultbuf, - struct tm *r) -{ - const char *outcome; - - if (PREDICT_LIKELY(r)) { - if (r->tm_year > 8099) { /* We can't strftime dates after 9999 CE. */ - r->tm_year = 8099; - r->tm_mon = 11; - r->tm_mday = 31; - r->tm_yday = 365; - r->tm_hour = 23; - r->tm_min = 59; - r->tm_sec = 59; - } - return r; - } - - /* If we get here, gmtime or localtime returned NULL. It might have done - * this because of overrun or underrun, or it might have done it because of - * some other weird issue. */ - if (timep) { - if (*timep < 0) { - r = resultbuf; - r->tm_year = 70; /* 1970 CE */ - r->tm_mon = 0; - r->tm_mday = 1; - r->tm_yday = 1; - r->tm_hour = 0; - r->tm_min = 0 ; - r->tm_sec = 0; - outcome = "Rounding up to 1970"; - goto done; - } else if (*timep >= INT32_MAX) { - /* Rounding down to INT32_MAX isn't so great, but keep in mind that we - * only do it if gmtime/localtime tells us NULL. */ - r = resultbuf; - r->tm_year = 137; /* 2037 CE */ - r->tm_mon = 11; - r->tm_mday = 31; - r->tm_yday = 365; - r->tm_hour = 23; - r->tm_min = 59; - r->tm_sec = 59; - outcome = "Rounding down to 2037"; - goto done; - } - } - - /* If we get here, then gmtime/localtime failed without getting an extreme - * value for *timep */ - - tor_fragile_assert(); - r = resultbuf; - memset(resultbuf, 0, sizeof(struct tm)); - outcome="can't recover"; - done: - log_warn(LD_BUG, "%s("I64_FORMAT") failed with error %s: %s", - islocal?"localtime":"gmtime", - timep?I64_PRINTF_ARG(*timep):0, - strerror(errno), - outcome); - return r; -} - -/** @{ */ -/** As localtime_r, but defined for platforms that don't have it: - * - * Convert *timep to a struct tm in local time, and store the value in - * *result. Return the result on success, or NULL on failure. - */ -#ifdef HAVE_LOCALTIME_R -struct tm * -tor_localtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - r = localtime_r(timep, result); - return correct_tm(1, timep, result, r); -} -#elif defined(TIME_FNS_NEED_LOCKS) -struct tm * -tor_localtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - static tor_mutex_t *m=NULL; - if (!m) { m=tor_mutex_new(); } - tor_assert(result); - tor_mutex_acquire(m); - r = localtime(timep); - if (r) - memcpy(result, r, sizeof(struct tm)); - tor_mutex_release(m); - return correct_tm(1, timep, result, r); -} -#else -struct tm * -tor_localtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - tor_assert(result); - r = localtime(timep); - if (r) - memcpy(result, r, sizeof(struct tm)); - return correct_tm(1, timep, result, r); -} -#endif -/** @} */ - -/** @{ */ -/** As gmtimee_r, but defined for platforms that don't have it: - * - * Convert *timep to a struct tm in UTC, and store the value in - * *result. Return the result on success, or NULL on failure. - */ -#ifdef HAVE_GMTIME_R -struct tm * -tor_gmtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - r = gmtime_r(timep, result); - return correct_tm(0, timep, result, r); -} -#elif defined(TIME_FNS_NEED_LOCKS) -struct tm * -tor_gmtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - static tor_mutex_t *m=NULL; - if (!m) { m=tor_mutex_new(); } - tor_assert(result); - tor_mutex_acquire(m); - r = gmtime(timep); - if (r) - memcpy(result, r, sizeof(struct tm)); - tor_mutex_release(m); - return correct_tm(0, timep, result, r); -} -#else -struct tm * -tor_gmtime_r(const time_t *timep, struct tm *result) -{ - struct tm *r; - tor_assert(result); - r = gmtime(timep); - if (r) - memcpy(result, r, sizeof(struct tm)); - return correct_tm(0, timep, result, r); -} -#endif - -#if defined(USE_WIN32_THREADS) -void -tor_mutex_init(tor_mutex_t *m) -{ - InitializeCriticalSection(&m->mutex); -} -void -tor_mutex_uninit(tor_mutex_t *m) -{ - DeleteCriticalSection(&m->mutex); -} -void -tor_mutex_acquire(tor_mutex_t *m) -{ - tor_assert(m); - EnterCriticalSection(&m->mutex); -} -void -tor_mutex_release(tor_mutex_t *m) -{ - LeaveCriticalSection(&m->mutex); -} -unsigned long -tor_get_thread_id(void) -{ - return (unsigned long)GetCurrentThreadId(); -} -#elif defined(USE_PTHREADS) -/** A mutex attribute that we're going to use to tell pthreads that we want - * "reentrant" mutexes (i.e., once we can re-lock if we're already holding - * them.) */ -static pthread_mutexattr_t attr_reentrant; -/** True iff we've called tor_threads_init() */ -static int threads_initialized = 0; -/** Initialize mutex so it can be locked. Every mutex must be set - * up with tor_mutex_init() or tor_mutex_new(); not both. */ -void -tor_mutex_init(tor_mutex_t *mutex) -{ - int err; - if (PREDICT_UNLIKELY(!threads_initialized)) - tor_threads_init(); - err = pthread_mutex_init(&mutex->mutex, &attr_reentrant); - if (PREDICT_UNLIKELY(err)) { - log_err(LD_GENERAL, "Error %d creating a mutex.", err); - tor_fragile_assert(); - } -} -/** Wait until m is free, then acquire it. */ -void -tor_mutex_acquire(tor_mutex_t *m) -{ - int err; - tor_assert(m); - err = pthread_mutex_lock(&m->mutex); - if (PREDICT_UNLIKELY(err)) { - log_err(LD_GENERAL, "Error %d locking a mutex.", err); - tor_fragile_assert(); - } -} -/** Release the lock m so another thread can have it. */ -void -tor_mutex_release(tor_mutex_t *m) -{ - int err; - tor_assert(m); - err = pthread_mutex_unlock(&m->mutex); - if (PREDICT_UNLIKELY(err)) { - log_err(LD_GENERAL, "Error %d unlocking a mutex.", err); - tor_fragile_assert(); - } -} -/** Clean up the mutex m so that it no longer uses any system - * resources. Does not free m. This function must only be called on - * mutexes from tor_mutex_init(). */ -void -tor_mutex_uninit(tor_mutex_t *m) -{ - int err; - tor_assert(m); - err = pthread_mutex_destroy(&m->mutex); - if (PREDICT_UNLIKELY(err)) { - log_err(LD_GENERAL, "Error %d destroying a mutex.", err); - tor_fragile_assert(); - } -} -/** Return an integer representing this thread. */ -unsigned long -tor_get_thread_id(void) -{ - union { - pthread_t thr; - unsigned long id; - } r; - r.thr = pthread_self(); - return r.id; -} -#endif - -#ifdef TOR_IS_MULTITHREADED -/** Return a newly allocated, ready-for-use mutex. */ -tor_mutex_t * -tor_mutex_new(void) -{ - tor_mutex_t *m = tor_malloc_zero(sizeof(tor_mutex_t)); - tor_mutex_init(m); - return m; -} -/** Release all storage and system resources held by m. */ -void -tor_mutex_free(tor_mutex_t *m) -{ - if (!m) - return; - tor_mutex_uninit(m); - tor_free(m); -} -#endif - -/* Conditions. */ -#ifdef USE_PTHREADS -#if 0 -/** Cross-platform condition implementation. */ -struct tor_cond_t { - pthread_cond_t cond; -}; -/** Return a newly allocated condition, with nobody waiting on it. */ -tor_cond_t * -tor_cond_new(void) -{ - tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t)); - if (pthread_cond_init(&cond->cond, NULL)) { - tor_free(cond); - return NULL; - } - return cond; -} -/** Release all resources held by cond. */ -void -tor_cond_free(tor_cond_t *cond) -{ - if (!cond) - return; - if (pthread_cond_destroy(&cond->cond)) { - log_warn(LD_GENERAL,"Error freeing condition: %s", strerror(errno)); - return; - } - tor_free(cond); -} -/** Wait until one of the tor_cond_signal functions is called on cond. - * All waiters on the condition must wait holding the same mutex. - * Returns 0 on success, negative on failure. */ -int -tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex) -{ - return pthread_cond_wait(&cond->cond, &mutex->mutex) ? -1 : 0; -} -/** Wake up one of the waiters on cond. */ -void -tor_cond_signal_one(tor_cond_t *cond) -{ - pthread_cond_signal(&cond->cond); -} -/** Wake up all of the waiters on cond. */ -void -tor_cond_signal_all(tor_cond_t *cond) -{ - pthread_cond_broadcast(&cond->cond); -} -#endif -/** Set up common structures for use by threading. */ -void -tor_threads_init(void) -{ - if (!threads_initialized) { - pthread_mutexattr_init(&attr_reentrant); - pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE); - threads_initialized = 1; - set_main_thread(); - } -} -#elif defined(USE_WIN32_THREADS) -#if 0 -static DWORD cond_event_tls_index; -struct tor_cond_t { - CRITICAL_SECTION mutex; - smartlist_t *events; -}; -tor_cond_t * -tor_cond_new(void) -{ - tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t)); - InitializeCriticalSection(&cond->mutex); - cond->events = smartlist_new(); - return cond; -} -void -tor_cond_free(tor_cond_t *cond) -{ - if (!cond) - return; - DeleteCriticalSection(&cond->mutex); - /* XXXX notify? */ - smartlist_free(cond->events); - tor_free(cond); -} -int -tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex) -{ - HANDLE event; - int r; - tor_assert(cond); - tor_assert(mutex); - event = TlsGetValue(cond_event_tls_index); - if (!event) { - event = CreateEvent(0, FALSE, FALSE, NULL); - TlsSetValue(cond_event_tls_index, event); - } - EnterCriticalSection(&cond->mutex); - - tor_assert(WaitForSingleObject(event, 0) == WAIT_TIMEOUT); - tor_assert(!smartlist_contains(cond->events, event)); - smartlist_add(cond->events, event); - - LeaveCriticalSection(&cond->mutex); - - tor_mutex_release(mutex); - r = WaitForSingleObject(event, INFINITE); - tor_mutex_acquire(mutex); - - switch (r) { - case WAIT_OBJECT_0: /* we got the mutex normally. */ - break; - case WAIT_ABANDONED: /* holding thread exited. */ - case WAIT_TIMEOUT: /* Should never happen. */ - tor_assert(0); - break; - case WAIT_FAILED: - log_warn(LD_GENERAL, "Failed to acquire mutex: %d",(int) GetLastError()); - } - return 0; -} -void -tor_cond_signal_one(tor_cond_t *cond) -{ - HANDLE event; - tor_assert(cond); - - EnterCriticalSection(&cond->mutex); - - if ((event = smartlist_pop_last(cond->events))) - SetEvent(event); - - LeaveCriticalSection(&cond->mutex); -} -void -tor_cond_signal_all(tor_cond_t *cond) -{ - tor_assert(cond); - - EnterCriticalSection(&cond->mutex); - SMARTLIST_FOREACH(cond->events, HANDLE, event, SetEvent(event)); - smartlist_clear(cond->events); - LeaveCriticalSection(&cond->mutex); -} -#endif -void -tor_threads_init(void) -{ -#if 0 - cond_event_tls_index = TlsAlloc(); -#endif - set_main_thread(); -} -#endif - -#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK) -/** Attempt to raise the current and max rlimit to infinity for our process. - * This only needs to be done once and can probably only be done when we have - * not already dropped privileges. - */ -static int -tor_set_max_memlock(void) -{ - /* Future consideration for Windows is probably SetProcessWorkingSetSize - * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK - * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx - */ - - struct rlimit limit; - - /* RLIM_INFINITY is -1 on some platforms. */ - limit.rlim_cur = RLIM_INFINITY; - limit.rlim_max = RLIM_INFINITY; - - if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) { - if (errno == EPERM) { - log_warn(LD_GENERAL, "You appear to lack permissions to change memory " - "limits. Are you root?"); - } - log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s", - strerror(errno)); - return -1; - } - - return 0; -} -#endif - -/** Attempt to lock all current and all future memory pages. - * This should only be called once and while we're privileged. - * Like mlockall() we return 0 when we're successful and -1 when we're not. - * Unlike mlockall() we return 1 if we've already attempted to lock memory. - */ -int -tor_mlockall(void) -{ - static int memory_lock_attempted = 0; - - if (memory_lock_attempted) { - return 1; - } - - memory_lock_attempted = 1; - - /* - * Future consideration for Windows may be VirtualLock - * VirtualLock appears to implement mlock() but not mlockall() - * - * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx - */ - -#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK) - if (tor_set_max_memlock() == 0) { - log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY."); - } - - if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) { - log_info(LD_GENERAL, "Insecure OS paging is effectively disabled."); - return 0; - } else { - if (errno == ENOSYS) { - /* Apple - it's 2009! I'm looking at you. Grrr. */ - log_notice(LD_GENERAL, "It appears that mlockall() is not available on " - "your platform."); - } else if (errno == EPERM) { - log_notice(LD_GENERAL, "It appears that you lack the permissions to " - "lock memory. Are you root?"); - } - log_notice(LD_GENERAL, "Unable to lock all current and future memory " - "pages: %s", strerror(errno)); - return -1; - } -#else - log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?"); - return -1; -#endif -} - -/** Identity of the "main" thread */ -static unsigned long main_thread_id = -1; - -/** Start considering the current thread to be the 'main thread'. This has - * no effect on anything besides in_main_thread(). */ -void -set_main_thread(void) -{ - main_thread_id = tor_get_thread_id(); -} -/** Return true iff called from the main thread. */ -int -in_main_thread(void) -{ - return main_thread_id == tor_get_thread_id(); -} - -/** - * On Windows, WSAEWOULDBLOCK is not always correct: when you see it, - * you need to ask the socket for its actual errno. Also, you need to - * get your errors from WSAGetLastError, not errno. (If you supply a - * socket of -1, we check WSAGetLastError, but don't correct - * WSAEWOULDBLOCKs.) - * - * The upshot of all of this is that when a socket call fails, you - * should call tor_socket_errno at most once on the failing - * socket to get the error. - */ -#if defined(_WIN32) -int -tor_socket_errno(tor_socket_t sock) -{ - int optval, optvallen=sizeof(optval); - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK && SOCKET_OK(sock)) { - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval, &optvallen)) - return err; - if (optval) - return optval; - } - return err; -} -#endif - -#if defined(_WIN32) -#define E(code, s) { code, (s " [" #code " ]") } -struct { int code; const char *msg; } windows_socket_errors[] = { - E(WSAEINTR, "Interrupted function call"), - E(WSAEACCES, "Permission denied"), - E(WSAEFAULT, "Bad address"), - E(WSAEINVAL, "Invalid argument"), - E(WSAEMFILE, "Too many open files"), - E(WSAEWOULDBLOCK, "Resource temporarily unavailable"), - E(WSAEINPROGRESS, "Operation now in progress"), - E(WSAEALREADY, "Operation already in progress"), - E(WSAENOTSOCK, "Socket operation on nonsocket"), - E(WSAEDESTADDRREQ, "Destination address required"), - E(WSAEMSGSIZE, "Message too long"), - E(WSAEPROTOTYPE, "Protocol wrong for socket"), - E(WSAENOPROTOOPT, "Bad protocol option"), - E(WSAEPROTONOSUPPORT, "Protocol not supported"), - E(WSAESOCKTNOSUPPORT, "Socket type not supported"), - /* What's the difference between NOTSUPP and NOSUPPORT? :) */ - E(WSAEOPNOTSUPP, "Operation not supported"), - E(WSAEPFNOSUPPORT, "Protocol family not supported"), - E(WSAEAFNOSUPPORT, "Address family not supported by protocol family"), - E(WSAEADDRINUSE, "Address already in use"), - E(WSAEADDRNOTAVAIL, "Cannot assign requested address"), - E(WSAENETDOWN, "Network is down"), - E(WSAENETUNREACH, "Network is unreachable"), - E(WSAENETRESET, "Network dropped connection on reset"), - E(WSAECONNABORTED, "Software caused connection abort"), - E(WSAECONNRESET, "Connection reset by peer"), - E(WSAENOBUFS, "No buffer space available"), - E(WSAEISCONN, "Socket is already connected"), - E(WSAENOTCONN, "Socket is not connected"), - E(WSAESHUTDOWN, "Cannot send after socket shutdown"), - E(WSAETIMEDOUT, "Connection timed out"), - E(WSAECONNREFUSED, "Connection refused"), - E(WSAEHOSTDOWN, "Host is down"), - E(WSAEHOSTUNREACH, "No route to host"), - E(WSAEPROCLIM, "Too many processes"), - /* Yes, some of these start with WSA, not WSAE. No, I don't know why. */ - E(WSASYSNOTREADY, "Network subsystem is unavailable"), - E(WSAVERNOTSUPPORTED, "Winsock.dll out of range"), - E(WSANOTINITIALISED, "Successful WSAStartup not yet performed"), - E(WSAEDISCON, "Graceful shutdown now in progress"), -#ifdef WSATYPE_NOT_FOUND - E(WSATYPE_NOT_FOUND, "Class type not found"), -#endif - E(WSAHOST_NOT_FOUND, "Host not found"), - E(WSATRY_AGAIN, "Nonauthoritative host not found"), - E(WSANO_RECOVERY, "This is a nonrecoverable error"), - E(WSANO_DATA, "Valid name, no data record of requested type)"), - - /* There are some more error codes whose numeric values are marked - * OS dependent. They start with WSA_, apparently for the same - * reason that practitioners of some craft traditions deliberately - * introduce imperfections into their baskets and rugs "to allow the - * evil spirits to escape." If we catch them, then our binaries - * might not report consistent results across versions of Windows. - * Thus, I'm going to let them all fall through. - */ - { -1, NULL }, -}; -/** There does not seem to be a strerror equivalent for Winsock errors. - * Naturally, we have to roll our own. - */ -const char * -tor_socket_strerror(int e) -{ - int i; - for (i=0; windows_socket_errors[i].code >= 0; ++i) { - if (e == windows_socket_errors[i].code) - return windows_socket_errors[i].msg; - } - return strerror(e); -} -#endif - -/** Called before we make any calls to network-related functions. - * (Some operating systems require their network libraries to be - * initialized.) */ -int -network_init(void) -{ -#ifdef _WIN32 - /* This silly exercise is necessary before windows will allow - * gethostbyname to work. */ - WSADATA WSAData; - int r; - r = WSAStartup(0x101,&WSAData); - if (r) { - log_warn(LD_NET,"Error initializing windows network layer: code was %d",r); - return -1; - } - if (sizeof(SOCKET) != sizeof(tor_socket_t)) { - log_warn(LD_BUG,"The tor_socket_t type does not match SOCKET in size; Tor " - "might not work. (Sizes are %d and %d respectively.)", - (int)sizeof(tor_socket_t), (int)sizeof(SOCKET)); - } - /* WSAData.iMaxSockets might show the max sockets we're allowed to use. - * We might use it to complain if we're trying to be a server but have - * too few sockets available. */ -#endif - return 0; -} - -#ifdef _WIN32 -/** Return a newly allocated string describing the windows system error code - * err. Note that error codes are different from errno. Error codes - * come from GetLastError() when a winapi call fails. errno is set only when - * ANSI functions fail. Whee. */ -char * -format_win32_error(DWORD err) -{ - TCHAR *str = NULL; - char *result; - DWORD n; - - /* Somebody once decided that this interface was better than strerror(). */ - n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPVOID)&str, - 0, NULL); - - if (str && n) { -#ifdef UNICODE - size_t len; - if (n > 128*1024) - len = (128 * 1024) * 2 + 1; /* This shouldn't be possible, but let's - * make sure. */ - else - len = n * 2 + 1; - result = tor_malloc(len); - wcstombs(result,str,len); - result[len-1] = '\0'; -#else - result = tor_strdup(str); -#endif - } else { - result = tor_strdup(""); - } - if (str) { - LocalFree(str); /* LocalFree != free() */ - } - return result; -} -#endif - diff --git a/src/tor/compat_libevent.c b/src/tor/compat_libevent.c deleted file mode 100644 index 58ec08c..0000000 --- a/src/tor/compat_libevent.c +++ /dev/null @@ -1,663 +0,0 @@ -/* Copyright (c) 2009-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file compat_libevent.c - * \brief Wrappers to handle porting between different versions of libevent. - * - * In an ideal world, we'd just use Libevent 2.0 from now on. But as of June - * 2012, Libevent 1.4 is still all over, and some poor souls are stuck on - * Libevent 1.3e. */ - -#include "orconfig.h" -#include "tor_compat.h" -#include "compat_libevent.h" - -#include "tor_util.h" -#include "torlog.h" - -#ifdef HAVE_EVENT2_EVENT_H -#include -#include -#ifdef USE_BUFFEREVENTS -#include -#endif -#else -#include -#endif - -/** A number representing a version of Libevent. - - This is a 4-byte number, with the first three bytes representing the - major, minor, and patchlevel respectively of the library. The fourth - byte is unused. - - This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent - 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the - format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a - to be equivalent to 1.0.1, and so on. -*/ -typedef uint32_t le_version_t; - -/** @{ */ -/** Macros: returns the number of a libevent version as a le_version_t */ -#define V(major, minor, patch) \ - (((major) << 24) | ((minor) << 16) | ((patch) << 8)) -#define V_OLD(major, minor, patch) \ - V((major), (minor), (patch)-'a'+1) -/** @} */ - -/** Represetns a version of libevent so old we can't figure out what version - * it is. */ -#define LE_OLD V(0,0,0) -/** Represents a version of libevent so weird we can't figure out what version - * it is. */ -#define LE_OTHER V(0,0,99) - -#if 0 -static le_version_t tor_get_libevent_version(const char **v_out); -#endif - -#if defined(HAVE_EVENT_SET_LOG_CALLBACK) || defined(RUNNING_DOXYGEN) -/** A string which, if it appears in a libevent log, should be ignored. */ -static const char *suppress_msg = NULL; -/** Callback function passed to event_set_log() so we can intercept - * log messages from libevent. */ -static void -libevent_logging_callback(int severity, const char *msg) -{ - char buf[1024]; - size_t n; - if (suppress_msg && strstr(msg, suppress_msg)) - return; - n = strlcpy(buf, msg, sizeof(buf)); - if (n && n < sizeof(buf) && buf[n-1] == '\n') { - buf[n-1] = '\0'; - } - switch (severity) { - case _EVENT_LOG_DEBUG: - log_debug(LD_NOCB|LD_NET, "Message from libevent: %s", buf); - break; - case _EVENT_LOG_MSG: - log_info(LD_NOCB|LD_NET, "Message from libevent: %s", buf); - break; - case _EVENT_LOG_WARN: - log_warn(LD_NOCB|LD_GENERAL, "Warning from libevent: %s", buf); - break; - case _EVENT_LOG_ERR: - log_err(LD_NOCB|LD_GENERAL, "Error from libevent: %s", buf); - break; - default: - log_warn(LD_NOCB|LD_GENERAL, "Message [%d] from libevent: %s", - severity, buf); - break; - } -} -/** Set hook to intercept log messages from libevent. */ -void -configure_libevent_logging(void) -{ - event_set_log_callback(libevent_logging_callback); -} -/** Ignore any libevent log message that contains msg. */ -void -suppress_libevent_log_msg(const char *msg) -{ - suppress_msg = msg; -} -#else -void -configure_libevent_logging(void) -{ -} -void -suppress_libevent_log_msg(const char *msg) -{ - (void)msg; -} -#endif - -#ifndef HAVE_EVENT2_EVENT_H -/** Work-alike replacement for event_new() on pre-Libevent-2.0 systems. */ -struct event * -tor_event_new(struct event_base *base, int sock, short what, - void (*cb)(int, short, void *), void *arg) -{ - struct event *e = tor_malloc_zero(sizeof(struct event)); - event_set(e, sock, what, cb, arg); - if (! base) - base = tor_libevent_get_base(); - event_base_set(base, e); - return e; -} -/** Work-alike replacement for evtimer_new() on pre-Libevent-2.0 systems. */ -struct event * -tor_evtimer_new(struct event_base *base, - void (*cb)(int, short, void *), void *arg) -{ - return tor_event_new(base, -1, 0, cb, arg); -} -/** Work-alike replacement for evsignal_new() on pre-Libevent-2.0 systems. */ -struct event * -tor_evsignal_new(struct event_base * base, int sig, - void (*cb)(int, short, void *), void *arg) -{ - return tor_event_new(base, sig, EV_SIGNAL|EV_PERSIST, cb, arg); -} -/** Work-alike replacement for event_free() on pre-Libevent-2.0 systems. */ -void -tor_event_free(struct event *ev) -{ - event_del(ev); - tor_free(ev); -} -#endif - -/** Global event base for use by the main thread. */ -struct event_base *the_event_base = NULL; - -/* This is what passes for version detection on OSX. We set - * MACOSX_KQUEUE_IS_BROKEN to true iff we're on a version of OSX before - * 10.4.0 (aka 1040). */ -#ifdef __APPLE__ -#ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#define MACOSX_KQUEUE_IS_BROKEN \ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1040) -#else -#define MACOSX_KQUEUE_IS_BROKEN 0 -#endif -#endif - -#ifdef USE_BUFFEREVENTS -static int using_iocp_bufferevents = 0; -static void tor_libevent_set_tick_timeout(int msec_per_tick); - -int -tor_libevent_using_iocp_bufferevents(void) -{ - return using_iocp_bufferevents; -} -#endif - -/** Initialize the Libevent library and set up the event base. */ -void -tor_libevent_initialize(tor_libevent_cfg *torcfg) -{ - tor_assert(the_event_base == NULL); - /* some paths below don't use torcfg, so avoid unused variable warnings */ - (void)torcfg; - -#ifdef HAVE_EVENT2_EVENT_H - { - int attempts = 0; - int using_threads; - struct event_config *cfg; - - retry: - ++attempts; - using_threads = 0; - cfg = event_config_new(); - tor_assert(cfg); - -#if defined(_WIN32) && defined(USE_BUFFEREVENTS) - if (! torcfg->disable_iocp) { - evthread_use_windows_threads(); - event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); - using_iocp_bufferevents = 1; - using_threads = 1; - } else { - using_iocp_bufferevents = 0; - } -#endif - - if (!using_threads) { - /* Telling Libevent not to try to turn locking on can avoid a needless - * socketpair() attempt. */ - event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK); - } - -#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7) - if (torcfg->num_cpus > 0) - event_config_set_num_cpus_hint(cfg, torcfg->num_cpus); -#endif - -#if LIBEVENT_VERSION_NUMBER >= V(2,0,9) - /* We can enable changelist support with epoll, since we don't give - * Libevent any dup'd fds. This lets us avoid some syscalls. */ - event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); -#endif - - the_event_base = event_base_new_with_config(cfg); - - event_config_free(cfg); - - if (using_threads && the_event_base == NULL && attempts < 2) { - /* This could be a socketpair() failure, which can happen sometimes on - * windows boxes with obnoxious firewall rules. Downgrade and try - * again. */ -#if defined(_WIN32) && defined(USE_BUFFEREVENTS) - if (torcfg->disable_iocp == 0) { - log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again " - "with IOCP disabled."); - } else -#endif - { - log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again."); - } - - torcfg->disable_iocp = 1; - goto retry; - } - } -#else - the_event_base = event_init(); -#endif - - if (!the_event_base) { - log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); - exit(1); - } - -#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_info(LD_GENERAL, - "Initialized libevent version %s using method %s. Good.", - event_get_version(), tor_libevent_get_method()); -#else - log_notice(LD_GENERAL, - "Initialized old libevent (version 1.0b or earlier)."); - log_warn(LD_GENERAL, - "You have a *VERY* old version of libevent. It is likely to be buggy; " - "please build Tor with a more recent version."); -#endif - -#ifdef USE_BUFFEREVENTS - tor_libevent_set_tick_timeout(torcfg->msec_per_tick); -#endif -} - -/** Return the current Libevent event base that we're set up to use. */ -struct event_base * -tor_libevent_get_base(void) -{ - return the_event_base; -} - -#ifndef HAVE_EVENT_BASE_LOOPEXIT -/** Replacement for event_base_loopexit on some very old versions of Libevent - * that we are not yet brave enough to deprecate. */ -int -tor_event_base_loopexit(struct event_base *base, struct timeval *tv) -{ - tor_assert(base == the_event_base); - return event_loopexit(tv); -} -#endif - -/** Return the name of the Libevent backend we're using. */ -const char * -tor_libevent_get_method(void) -{ -#ifdef HAVE_EVENT2_EVENT_H - return event_base_get_method(the_event_base); -#elif defined(HAVE_EVENT_GET_METHOD) - return event_get_method(); -#else - return ""; -#endif -} - -/** Return the le_version_t for the version of libevent specified in the - * string v. If the version is very new or uses an unrecognized - * version, format, return LE_OTHER. */ -static le_version_t -tor_decode_libevent_version(const char *v) -{ - unsigned major, minor, patchlevel; - char c, e, extra; - int fields; - - /* Try the new preferred "1.4.11-stable" format. - * Also accept "1.4.14b-stable". */ - fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e); - if (fields == 3 || - ((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) || - (fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) { - return V(major,minor,patchlevel); - } - - /* Try the old "1.3e" format. */ - fields = tor_sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra); - if (fields == 3 && TOR_ISALPHA(c)) { - return V_OLD(major, minor, c); - } else if (fields == 2) { - return V(major, minor, 0); - } - - return LE_OTHER; -} - -/** Return an integer representing the binary interface of a Libevent library. - * Two different versions with different numbers are sure not to be binary - * compatible. Two different versions with the same numbers have a decent - * chance of binary compatibility.*/ -static int -le_versions_compatibility(le_version_t v) -{ - if (v == LE_OTHER) - return 0; - if (v < V_OLD(1,0,'c')) - return 1; - else if (v < V(1,4,0)) - return 2; - else if (v < V(1,4,99)) - return 3; - else if (v < V(2,0,1)) - return 4; - else /* Everything 2.0 and later should be compatible. */ - return 5; -} - -#if 0 -/** Return the version number of the currently running version of Libevent. - * See le_version_t for info on the format. - */ -static le_version_t -tor_get_libevent_version(const char **v_out) -{ - const char *v; - le_version_t r; -#if defined(HAVE_EVENT_GET_VERSION_NUMBER) - v = event_get_version(); - r = event_get_version_number(); -#elif defined (HAVE_EVENT_GET_VERSION) - v = event_get_version(); - r = tor_decode_libevent_version(v); -#else - v = "pre-1.0c"; - r = LE_OLD; -#endif - if (v_out) - *v_out = v; - return r; -} -#endif - -/** Return a string representation of the version of the currently running - * version of Libevent. */ -const char * -tor_libevent_get_version_str(void) -{ -#ifdef HAVE_EVENT_GET_VERSION - return event_get_version(); -#else - return "pre-1.0c"; -#endif -} - -/** - * Compare the current Libevent method and version to a list of versions - * which are known not to work. Warn the user as appropriate. - */ -void -tor_check_libevent_version(const char *m, int server, - const char **badness_out) -{ - (void) m; - (void) server; - *badness_out = NULL; -} - -#if defined(LIBEVENT_VERSION) -#define HEADER_VERSION LIBEVENT_VERSION -#elif defined(_EVENT_VERSION) -#define HEADER_VERSION _EVENT_VERSION -#endif - -/** Return a string representation of the version of Libevent that was used -* at compilation time. */ -const char * -tor_libevent_get_header_version_str(void) -{ - return HEADER_VERSION; -} - -/** See whether the headers we were built against differ from the library we - * linked against so much that we're likely to crash. If so, warn the - * user. */ -void -tor_check_libevent_header_compatibility(void) -{ - (void) le_versions_compatibility; - (void) tor_decode_libevent_version; - - /* In libevent versions before 2.0, it's hard to keep binary compatibility - * between upgrades, and unpleasant to detect when the version we compiled - * against is unlike the version we have linked against. Here's how. */ -#if defined(HEADER_VERSION) && defined(HAVE_EVENT_GET_VERSION) - /* We have a header-file version and a function-call version. Easy. */ - if (strcmp(HEADER_VERSION, event_get_version())) { - le_version_t v1, v2; - int compat1 = -1, compat2 = -1; - int verybad; - v1 = tor_decode_libevent_version(HEADER_VERSION); - v2 = tor_decode_libevent_version(event_get_version()); - compat1 = le_versions_compatibility(v1); - compat2 = le_versions_compatibility(v2); - - verybad = compat1 != compat2; - - tor_log(verybad ? LOG_WARN : LOG_NOTICE, - LD_GENERAL, "We were compiled with headers from version %s " - "of Libevent, but we're using a Libevent library that says it's " - "version %s.", HEADER_VERSION, event_get_version()); - if (verybad) - log_warn(LD_GENERAL, "This will almost certainly make Tor crash."); - else - log_info(LD_GENERAL, "I think these versions are binary-compatible."); - } -#elif defined(HAVE_EVENT_GET_VERSION) - /* event_get_version but no _EVENT_VERSION. We might be in 1.4.0-beta or - earlier, where that's normal. To see whether we were compiled with an - earlier version, let's see whether the struct event defines MIN_HEAP_IDX. - */ -#ifdef HAVE_STRUCT_EVENT_MIN_HEAP_IDX - /* The header files are 1.4.0-beta or later. If the version is not - * 1.4.0-beta, we are incompatible. */ - { - if (strcmp(event_get_version(), "1.4.0-beta")) { - log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have " - "Libevent 1.4.0-beta header files, whereas you have linked " - "against Libevent %s. This will probably make Tor crash.", - event_get_version()); - } - } -#else - /* Our headers are 1.3e or earlier. If the library version is not 1.4.x or - later, we're probably fine. */ - { - const char *v = event_get_version(); - if ((v[0] == '1' && v[2] == '.' && v[3] > '3') || v[0] > '1') { - log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have " - "Libevent header file from 1.3e or earlier, whereas you have " - "linked against Libevent %s. This will probably make Tor " - "crash.", event_get_version()); - } - } -#endif - -#elif defined(HEADER_VERSION) -#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd." -#else - /* Your libevent is ancient. */ -#endif -} - -/* - If possible, we're going to try to use Libevent's periodic timer support, - since it does a pretty good job of making sure that periodic events get - called exactly M seconds apart, rather than starting each one exactly M - seconds after the time that the last one was run. - */ -#ifdef HAVE_EVENT2_EVENT_H -#define HAVE_PERIODIC -#define PERIODIC_FLAGS EV_PERSIST -#else -#define PERIODIC_FLAGS 0 -#endif - -/** Represents a timer that's run every N microseconds by Libevent. */ -struct periodic_timer_t { - /** Underlying event used to implement this periodic event. */ - struct event *ev; - /** The callback we'll be invoking whenever the event triggers */ - void (*cb)(struct periodic_timer_t *, void *); - /** User-supplied data for the callback */ - void *data; -#ifndef HAVE_PERIODIC - /** If Libevent doesn't know how to invoke events every N microseconds, - * we'll need to remember the timeout interval here. */ - struct timeval tv; -#endif -}; - -/** Libevent callback to implement a periodic event. */ -static void -periodic_timer_cb(evutil_socket_t fd, short what, void *arg) -{ - periodic_timer_t *timer = arg; - (void) what; - (void) fd; -#ifndef HAVE_PERIODIC - /** reschedule the event as needed. */ - event_add(timer->ev, &timer->tv); -#endif - timer->cb(timer, timer->data); -} - -/** Create and schedule a new timer that will run every tv in - * the event loop of base. When the timer fires, it will - * run the timer in cb with the user-supplied data in data. */ -periodic_timer_t * -periodic_timer_new(struct event_base *base, - const struct timeval *tv, - void (*cb)(periodic_timer_t *timer, void *data), - void *data) -{ - periodic_timer_t *timer; - tor_assert(base); - tor_assert(tv); - tor_assert(cb); - timer = tor_malloc_zero(sizeof(periodic_timer_t)); - if (!(timer->ev = tor_event_new(base, -1, PERIODIC_FLAGS, - periodic_timer_cb, timer))) { - tor_free(timer); - return NULL; - } - timer->cb = cb; - timer->data = data; -#ifndef HAVE_PERIODIC - memcpy(&timer->tv, tv, sizeof(struct timeval)); -#endif - event_add(timer->ev, (struct timeval *)tv); /*drop const for old libevent*/ - return timer; -} - -/** Stop and free a periodic timer */ -void -periodic_timer_free(periodic_timer_t *timer) -{ - if (!timer) - return; - tor_event_free(timer->ev); - tor_free(timer); -} - -#ifdef USE_BUFFEREVENTS -static const struct timeval *one_tick = NULL; -/** - * Return a special timeout to be passed whenever libevent's O(1) timeout - * implementation should be used. Only use this when the timer is supposed - * to fire after msec_per_tick ticks have elapsed. -*/ -const struct timeval * -tor_libevent_get_one_tick_timeout(void) -{ - tor_assert(one_tick); - return one_tick; -} - -/** Initialize the common timeout that we'll use to refill the buckets every - * time a tick elapses. */ -static void -tor_libevent_set_tick_timeout(int msec_per_tick) -{ - struct event_base *base = tor_libevent_get_base(); - struct timeval tv; - - tor_assert(! one_tick); - tv.tv_sec = msec_per_tick / 1000; - tv.tv_usec = (msec_per_tick % 1000) * 1000; - one_tick = event_base_init_common_timeout(base, &tv); -} - -static struct bufferevent * -tor_get_root_bufferevent(struct bufferevent *bev) -{ - struct bufferevent *u; - while ((u = bufferevent_get_underlying(bev)) != NULL) - bev = u; - return bev; -} - -int -tor_set_bufferevent_rate_limit(struct bufferevent *bev, - struct ev_token_bucket_cfg *cfg) -{ - return bufferevent_set_rate_limit(tor_get_root_bufferevent(bev), cfg); -} - -int -tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, - struct bufferevent_rate_limit_group *g) -{ - return bufferevent_add_to_rate_limit_group(tor_get_root_bufferevent(bev), g); -} -#endif - -#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1) -void -tor_gettimeofday_cached(struct timeval *tv) -{ - event_base_gettimeofday_cached(the_event_base, tv); -} -void -tor_gettimeofday_cache_clear(void) -{ - event_base_update_cache_time(the_event_base); -} -#else -/** Cache the current hi-res time; the cache gets reset when libevent - * calls us. */ -static struct timeval cached_time_hires = {0, 0}; - -/** Return a fairly recent view of the current time. */ -void -tor_gettimeofday_cached(struct timeval *tv) -{ - if (cached_time_hires.tv_sec == 0) { - tor_gettimeofday(&cached_time_hires); - } - *tv = cached_time_hires; -} - -/** Reset the cached view of the current time, so that the next time we try - * to learn it, we will get an up-to-date value. */ -void -tor_gettimeofday_cache_clear(void) -{ - cached_time_hires.tv_sec = 0; -} -#endif - diff --git a/src/tor/compat_libevent.h b/src/tor/compat_libevent.h deleted file mode 100644 index fda8733..0000000 --- a/src/tor/compat_libevent.h +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright (c) 2009-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_COMPAT_LIBEVENT_H -#define TOR_COMPAT_LIBEVENT_H - -#include "orconfig.h" - -struct event; -struct event_base; -#ifdef USE_BUFFEREVENTS -struct bufferevent; -struct ev_token_bucket_cfg; -struct bufferevent_rate_limit_group; -#endif - -#ifdef HAVE_EVENT2_EVENT_H -#include -#elif !defined(EVUTIL_SOCKET_DEFINED) -#define EVUTIL_SOCKET_DEFINED -#define evutil_socket_t int -#endif - -void configure_libevent_logging(void); -void suppress_libevent_log_msg(const char *msg); - -#ifdef HAVE_EVENT2_EVENT_H -#define tor_event_new event_new -#define tor_evtimer_new evtimer_new -#define tor_evsignal_new evsignal_new -#define tor_event_free event_free -#define tor_evdns_add_server_port(sock, tcp, cb, data) \ - evdns_add_server_port_with_base(tor_libevent_get_base(), \ - (sock),(tcp),(cb),(data)); - -#else -struct event *tor_event_new(struct event_base * base, evutil_socket_t sock, - short what, void (*cb)(evutil_socket_t, short, void *), void *arg); -struct event *tor_evtimer_new(struct event_base * base, - void (*cb)(evutil_socket_t, short, void *), void *arg); -struct event *tor_evsignal_new(struct event_base * base, int sig, - void (*cb)(evutil_socket_t, short, void *), void *arg); -void tor_event_free(struct event *ev); -#define tor_evdns_add_server_port evdns_add_server_port -#endif - -typedef struct periodic_timer_t periodic_timer_t; - -periodic_timer_t *periodic_timer_new(struct event_base *base, - const struct timeval *tv, - void (*cb)(periodic_timer_t *timer, void *data), - void *data); -void periodic_timer_free(periodic_timer_t *); - -#ifdef HAVE_EVENT_BASE_LOOPEXIT -#define tor_event_base_loopexit event_base_loopexit -#else -struct timeval; -int tor_event_base_loopexit(struct event_base *base, struct timeval *tv); -#endif - -/** Defines a configuration for using libevent with Tor: passed as an argument - * to tor_libevent_initialize() to describe how we want to set up. */ -typedef struct tor_libevent_cfg { - /** Flag: if true, disable IOCP (assuming that it could be enabled). */ - int disable_iocp; - /** How many CPUs should we use (relevant only with IOCP). */ - int num_cpus; - /** How many milliseconds should we allow between updating bandwidth limits? - * (relevant only with bufferevents). */ - int msec_per_tick; -} tor_libevent_cfg; - -void tor_libevent_initialize(tor_libevent_cfg *cfg); -struct event_base *tor_libevent_get_base(void); -const char *tor_libevent_get_method(void); -void tor_check_libevent_version(const char *m, int server, - const char **badness_out); -void tor_check_libevent_header_compatibility(void); -const char *tor_libevent_get_version_str(void); -const char *tor_libevent_get_header_version_str(void); - -#ifdef USE_BUFFEREVENTS -const struct timeval *tor_libevent_get_one_tick_timeout(void); -int tor_libevent_using_iocp_bufferevents(void); -int tor_set_bufferevent_rate_limit(struct bufferevent *bev, - struct ev_token_bucket_cfg *cfg); -int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, - struct bufferevent_rate_limit_group *g); -#endif - -void tor_gettimeofday_cached(struct timeval *tv); -void tor_gettimeofday_cache_clear(void); - -#endif - diff --git a/src/tor/config.c b/src/tor/config.c deleted file mode 100644 index d44971d..0000000 --- a/src/tor/config.c +++ /dev/null @@ -1,6704 +0,0 @@ - /* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file config.c - * \brief Code to parse and interpret configuration files. - **/ - -#define CONFIG_PRIVATE -#include "or.h" -#include "addressmap.h" -#include "channel.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitmux.h" -#include "circuitmux_ewma.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "confparse.h" -#include "cpuworker.h" -#include "dirserv.h" -#include "dirvote.h" -#include "dns.h" -#include "entrynodes.h" -#include "geoip.h" -#include "hibernate.h" -#include "onion_main.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "relay.h" -#include "rendclient.h" -#include "rendservice.h" -#include "rephist.h" -#include "router.h" -#include "sandbox.h" -#include "tor_util.h" -#include "routerlist.h" -#include "routerset.h" -#include "statefile.h" -#include "transports.h" -#include "ext_orport.h" -#include "torgzip.h" -#include "anonymize.h" -#ifdef _WIN32 -#include -#endif - -#include "procmon.h" - -/* From onion_main.c */ -extern int quiet_level; - -/** A list of abbreviations and aliases to map command-line options, obsolete - * option names, or alternative option names, to their current values. */ -static config_abbrev_t option_abbrevs_[] = { - PLURAL(AuthDirBadDirCC), - PLURAL(AuthDirBadExitCC), - PLURAL(AuthDirInvalidCC), - PLURAL(AuthDirRejectCC), - PLURAL(ExitNode), - PLURAL(EntryNode), - PLURAL(ExcludeNode), - PLURAL(FirewallPort), - PLURAL(LongLivedPort), - PLURAL(HiddenServiceNode), - PLURAL(HiddenServiceExcludeNode), - PLURAL(NumCPU), - PLURAL(RendNode), - PLURAL(RendExcludeNode), - PLURAL(StrictEntryNode), - PLURAL(StrictExitNode), - PLURAL(StrictNode), - { "l", "Log", 1, 0}, - { "AllowUnverifiedNodes", "AllowInvalidNodes", 0, 0}, - { "AutomapHostSuffixes", "AutomapHostsSuffixes", 0, 0}, - { "AutomapHostOnResolve", "AutomapHostsOnResolve", 0, 0}, - { "BandwidthRateBytes", "BandwidthRate", 0, 0}, - { "BandwidthBurstBytes", "BandwidthBurst", 0, 0}, - { "DirFetchPostPeriod", "StatusFetchPeriod", 0, 0}, - { "DirServer", "DirAuthority", 0, 0}, /* XXXX024 later, make this warn? */ - { "MaxConn", "ConnLimit", 0, 1}, - { "ORBindAddress", "ORListenAddress", 0, 0}, - { "DirBindAddress", "DirListenAddress", 0, 0}, - { "SocksBindAddress", "SocksListenAddress", 0, 0}, - { "UseHelperNodes", "UseEntryGuards", 0, 0}, - { "NumHelperNodes", "NumEntryGuards", 0, 0}, - { "UseEntryNodes", "UseEntryGuards", 0, 0}, - { "NumEntryNodes", "NumEntryGuards", 0, 0}, - { "ResolvConf", "ServerDNSResolvConfFile", 0, 1}, - { "SearchDomains", "ServerDNSSearchDomains", 0, 1}, - { "ServerDNSAllowBrokenResolvConf", "ServerDNSAllowBrokenConfig", 0, 0}, - { "PreferTunnelledDirConns", "PreferTunneledDirConns", 0, 0}, - { "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0}, - { "HashedControlPassword", "__HashedControlSessionPassword", 1, 0}, - { "StrictEntryNodes", "StrictNodes", 0, 1}, - { "StrictExitNodes", "StrictNodes", 0, 1}, - { "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0}, - { "_UseFilteringSSLBufferevents", "UseFilteringSSLBufferevents", 0, 1}, - { NULL, NULL, 0, 0}, -}; - -/** An entry for config_vars: "The option name has type - * CONFIG_TYPE_conftype, and corresponds to - * or_options_t.member" - */ -#define VAR(name,conftype,member,initvalue) \ - { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), \ - initvalue } -/** As VAR, but the option name and member name are the same. */ -#define V(member,conftype,initvalue) \ - VAR(#member, conftype, member, initvalue) -/** An entry for config_vars: "The option name 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. - */ -static config_var_t option_vars_[] = { - OBSOLETE("AccountingMaxKB"), - V(AccountingMax, MEMUNIT, "0 bytes"), - V(AccountingStart, STRING, NULL), - V(Address, STRING, NULL), - V(AllowDotExit, BOOL, "0"), - V(AllowInvalidNodes, CSV, "middle,rendezvous"), - V(AllowNonRFC953Hostnames, BOOL, "0"), - V(AllowSingleHopCircuits, BOOL, "0"), - V(AllowSingleHopExits, BOOL, "0"), - V(AlternateBridgeAuthority, LINELIST, NULL), - V(AlternateDirAuthority, LINELIST, NULL), - V(AlternateHSAuthority, LINELIST, NULL), - V(AssumeReachable, BOOL, "0"), - V(AuthDirBadDir, LINELIST, NULL), - V(AuthDirBadDirCCs, CSV, ""), - V(AuthDirBadExit, LINELIST, NULL), - V(AuthDirBadExitCCs, CSV, ""), - V(AuthDirInvalid, LINELIST, NULL), - V(AuthDirInvalidCCs, CSV, ""), - V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), - V(AuthDirGuardBWGuarantee, MEMUNIT, "250 KB"), - V(AuthDirReject, LINELIST, NULL), - V(AuthDirRejectCCs, CSV, ""), - V(AuthDirRejectUnlisted, BOOL, "0"), - V(AuthDirListBadDirs, BOOL, "0"), - 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, "1 GB"), - V(BandwidthRate, MEMUNIT, "1 GB"), - V(BridgeAuthoritativeDir, BOOL, "0"), - VAR("Bridge", LINELIST, Bridges, NULL), - V(BridgePassword, STRING, NULL), - V(BridgeRecordUsageByCountry, BOOL, "1"), - V(BridgeRelay, BOOL, "0"), - V(CellStatistics, BOOL, "0"), - V(LearnCircuitBuildTimeout, BOOL, "1"), - V(CircuitBuildTimeout, INTERVAL, "0"), - V(CircuitIdleTimeout, INTERVAL, "1 hour"), - V(CircuitStreamTimeout, INTERVAL, "0"), - 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"), - V(ConstrainedSockets, BOOL, "0"), - V(ConstrainedSockSize, MEMUNIT, "8192"), - V(ContactInfo, STRING, NULL), - V(ControlListenAddress, LINELIST, NULL), - VPORT(ControlPort, LINELIST, NULL), - V(ControlPortFileGroupReadable,BOOL, "0"), - V(ControlPortWriteToFile, FILENAME, NULL), - V(ControlSocket, LINELIST, NULL), - V(ControlSocketsGroupWritable, BOOL, "0"), - V(CookieAuthentication, BOOL, "0"), - V(CookieAuthFileGroupReadable, BOOL, "0"), - V(CookieAuthFile, STRING, NULL), - V(CountPrivateBandwidth, BOOL, "0"), - V(DataDirectory, FILENAME, NULL), - OBSOLETE("DebugLogFile"), - V(DisableNetwork, BOOL, "0"), - V(DirAllowPrivateAddresses, BOOL, "0"), - V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), - V(DirListenAddress, LINELIST, NULL), - OBSOLETE("DirFetchPeriod"), - V(DirPolicy, LINELIST, NULL), - VPORT(DirPort, LINELIST, NULL), - V(DirPortFrontPage, FILENAME, NULL), - OBSOLETE("DirPostPeriod"), - OBSOLETE("DirRecordUsageByCountry"), - OBSOLETE("DirRecordUsageGranularity"), - OBSOLETE("DirRecordUsageRetainIPs"), - OBSOLETE("DirRecordUsageSaveInterval"), - V(DirReqStatistics, BOOL, "1"), - VAR("DirAuthority", LINELIST, DirAuthorities, NULL), - V(DirAuthorityFallbackRate, DOUBLE, "1.0"), - V(DisableAllSwap, BOOL, "0"), - V(DisableDebuggerAttachment, BOOL, "1"), - V(DisableIOCP, BOOL, "1"), - OBSOLETE("DisableV2DirectoryInfo_"), - V(DynamicDHGroups, BOOL, "0"), - VPORT(DNSPort, LINELIST, NULL), - V(DNSListenAddress, LINELIST, NULL), - V(DownloadExtraInfo, BOOL, "0"), - V(TestingEnableConnBwEvent, BOOL, "0"), - V(TestingEnableCellStatsEvent, BOOL, "0"), - V(TestingEnableTbEmptyEvent, BOOL, "0"), - V(EnforceDistinctSubnets, BOOL, "1"), - V(EntryNodes, ROUTERSET, NULL), - V(EntryStatistics, BOOL, "0"), - V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "10 minutes"), - V(ExcludeNodes, ROUTERSET, NULL), - V(ExcludeExitNodes, ROUTERSET, NULL), - V(ExcludeSingleHopRelays, BOOL, "1"), - V(ExitNodes, ROUTERSET, NULL), - V(ExitPolicy, LINELIST, NULL), - V(ExitPolicyRejectPrivate, BOOL, "1"), - V(ExitPortStatistics, BOOL, "0"), - V(ExtendAllowPrivateAddresses, BOOL, "0"), - VPORT(ExtORPort, LINELIST, NULL), - V(ExtraInfoStatistics, BOOL, "1"), - V(FallbackDir, LINELIST, NULL), - - OBSOLETE("FallbackNetworkstatusFile"), - V(FascistFirewall, BOOL, "0"), - V(FirewallPorts, CSV, ""), - V(FastFirstHopPK, AUTOBOOL, "auto"), - V(FetchDirInfoEarly, BOOL, "0"), - V(FetchDirInfoExtraEarly, BOOL, "0"), - V(FetchServerDescriptors, BOOL, "1"), - V(FetchHidServDescriptors, BOOL, "1"), - V(FetchUselessDescriptors, BOOL, "0"), - OBSOLETE("FetchV2Networkstatus"), - V(GeoIPExcludeUnknown, AUTOBOOL, "auto"), -#ifdef _WIN32 - V(GeoIPFile, FILENAME, ""), - V(GeoIPv6File, FILENAME, ""), -#else - V(GeoIPFile, FILENAME, - "/usr/share" PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"), - V(GeoIPv6File, FILENAME, - "/usr/share" PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip6"), -#endif - OBSOLETE("GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays"), - OBSOLETE("Group"), - V(GuardLifetime, INTERVAL, "0 minutes"), - V(HardwareAccel, BOOL, "0"), - V(HeartbeatPeriod, INTERVAL, "6 hours"), - V(AccelName, STRING, NULL), - V(AccelDir, FILENAME, NULL), - V(HashedControlPassword, LINELIST, NULL), - V(HidServDirectoryV2, BOOL, "1"), - VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL), - OBSOLETE("HiddenServiceExcludeNodes"), - OBSOLETE("HiddenServiceNodes"), - VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL), - VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL), - VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL), - VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL), - V(HidServAuth, LINELIST, NULL), - V(HSAuthoritativeDir, BOOL, "0"), - OBSOLETE("HSAuthorityRecordStats"), - V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), - V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), - V(HTTPProxy, STRING, NULL), - V(HTTPProxyAuthenticator, STRING, NULL), - V(HTTPSProxy, STRING, NULL), - V(HTTPSProxyAuthenticator, STRING, NULL), - V(IPv6Exit, BOOL, "0"), - VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), - V(ServerTransportListenAddr, LINELIST, NULL), - V(ServerTransportOptions, LINELIST, NULL), - V(Socks4Proxy, STRING, NULL), - V(Socks5Proxy, STRING, NULL), - V(Socks5ProxyUsername, STRING, NULL), - V(Socks5ProxyPassword, STRING, NULL), - OBSOLETE("IgnoreVersion"), - V(KeepalivePeriod, INTERVAL, "5 minutes"), - VAR("Log", LINELIST, Logs, NULL), - V(LogMessageDomains, BOOL, "0"), - OBSOLETE("LinkPadding"), - OBSOLETE("LogLevel"), - OBSOLETE("LogFile"), - V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), - V(LongLivedPorts, CSV, - "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), - VAR("MapAddress", LINELIST, AddressMap, NULL), - V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"), - V(MaxCircuitDirtiness, INTERVAL, "10 minutes"), - V(MaxClientCircuitsPending, UINT, "32"), - V(MaxMemInCellQueues, MEMUNIT, "8 GB"), - OBSOLETE("MaxOnionsPending"), - V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"), - V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"), - OBSOLETE("MonthlyAccountingStart"), - V(MyFamily, STRING, NULL), - V(NewCircuitPeriod, INTERVAL, "30 seconds"), - VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"), - V(NATDListenAddress, LINELIST, NULL), - VPORT(NATDPort, LINELIST, NULL), - V(Nickname, STRING, NULL), - V(WarnUnsafeSocks, BOOL, "1"), - OBSOLETE("NoPublish"), - VAR("NodeFamily", LINELIST, NodeFamilies, NULL), - V(NumCPUs, UINT, "0"), - V(NumDirectoryGuards, UINT, "0"), - V(NumEntryGuards, UINT, "3"), - V(ORListenAddress, LINELIST, NULL), - VPORT(ORPort, LINELIST, NULL), - V(OutboundBindAddress, LINELIST, NULL), - - OBSOLETE("PathBiasDisableRate"), - V(PathBiasCircThreshold, INT, "-1"), - V(PathBiasNoticeRate, DOUBLE, "-1"), - V(PathBiasWarnRate, DOUBLE, "-1"), - V(PathBiasExtremeRate, DOUBLE, "-1"), - V(PathBiasScaleThreshold, INT, "-1"), - OBSOLETE("PathBiasScaleFactor"), - OBSOLETE("PathBiasMultFactor"), - V(PathBiasDropGuards, AUTOBOOL, "0"), - OBSOLETE("PathBiasUseCloseCounts"), - - V(PathBiasUseThreshold, INT, "-1"), - V(PathBiasNoticeUseRate, DOUBLE, "-1"), - V(PathBiasExtremeUseRate, DOUBLE, "-1"), - V(PathBiasScaleUseThreshold, INT, "-1"), - - V(PathsNeededToBuildCircuits, DOUBLE, "-1"), - OBSOLETE("PathlenCoinWeight"), - V(PerConnBWBurst, MEMUNIT, "0"), - V(PerConnBWRate, MEMUNIT, "0"), - V(PidFile, STRING, NULL), - V(TestingTorNetwork, BOOL, "0"), - V(TestingMinExitFlagThreshold, MEMUNIT, "0"), - V(TestingMinFastFlagThreshold, MEMUNIT, "0"), - V(OptimisticData, AUTOBOOL, "auto"), - V(PortForwarding, BOOL, "0"), - V(PortForwardingHelper, FILENAME, "tor-fw-helper"), - V(PreferTunneledDirConns, BOOL, "1"), - V(ProtocolWarnings, BOOL, "0"), - V(PublishServerDescriptor, CSV, "1"), - V(PublishHidServDescriptors, BOOL, "1"), - V(ReachableAddresses, LINELIST, NULL), - V(ReachableDirAddresses, LINELIST, NULL), - V(ReachableORAddresses, LINELIST, NULL), - V(RecommendedVersions, LINELIST, NULL), - V(RecommendedClientVersions, LINELIST, NULL), - V(RecommendedServerVersions, LINELIST, NULL), - OBSOLETE("RedirectExit"), - V(RefuseUnknownExits, AUTOBOOL, "auto"), - V(RejectPlaintextPorts, CSV, ""), - V(RelayBandwidthBurst, MEMUNIT, "0"), - V(RelayBandwidthRate, MEMUNIT, "0"), - OBSOLETE("RendExcludeNodes"), - OBSOLETE("RendNodes"), - V(RendPostPeriod, INTERVAL, "1 hour"), - V(RephistTrackTime, INTERVAL, "24 hours"), - OBSOLETE("RouterFile"), - V(RunAsDaemon, BOOL, "0"), -// V(RunTesting, BOOL, "0"), - OBSOLETE("RunTesting"), // currently unused - V(Sandbox, BOOL, "0"), - V(SafeLogging, STRING, "1"), - V(SafeSocks, BOOL, "0"), - V(ServerDNSAllowBrokenConfig, BOOL, "1"), - V(ServerDNSAllowNonRFC953Hostnames, BOOL,"0"), - V(ServerDNSDetectHijacking, BOOL, "1"), - V(ServerDNSRandomizeCase, BOOL, "1"), - V(ServerDNSResolvConfFile, STRING, NULL), - V(ServerDNSSearchDomains, BOOL, "0"), - V(ServerDNSTestAddresses, CSV, - "www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org"), - V(ShutdownWaitLength, INTERVAL, "30 seconds"), - V(SocksListenAddress, LINELIST, NULL), - V(SocksPolicy, LINELIST, NULL), - VPORT(SocksPort, LINELIST, NULL), - V(SocksTimeout, INTERVAL, "2 minutes"), - V(SSLKeyLifetime, INTERVAL, "0"), - OBSOLETE("StatusFetchPeriod"), - V(StrictNodes, BOOL, "0"), - V(Support022HiddenServices, AUTOBOOL, "auto"), - OBSOLETE("SysLog"), - V(TestSocks, BOOL, "0"), - OBSOLETE("TestVia"), - V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"), - V(Tor2webMode, BOOL, "0"), - V(TLSECGroup, STRING, NULL), - V(TrackHostExits, CSV, NULL), - V(TrackHostExitsExpire, INTERVAL, "30 minutes"), - OBSOLETE("TrafficShaping"), - V(TransListenAddress, LINELIST, NULL), - VPORT(TransPort, LINELIST, NULL), - V(TransProxyType, STRING, "default"), - V(TunnelDirConns, BOOL, "1"), - V(UpdateBridgesFromAuthority, BOOL, "0"), - V(UseBridges, BOOL, "0"), - V(UseEntryGuards, BOOL, "1"), - V(UseEntryGuardsAsDirGuards, BOOL, "1"), - V(UseMicrodescriptors, AUTOBOOL, "auto"), - V(UseNTorHandshake, AUTOBOOL, "1"), - V(User, STRING, NULL), - V(UserspaceIOCPBuffers, BOOL, "0"), - VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), - OBSOLETE("V2AuthoritativeDirectory"), - VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), - V(TestingV3AuthInitialVotingInterval, INTERVAL, "30 minutes"), - V(TestingV3AuthInitialVoteDelay, INTERVAL, "5 minutes"), - V(TestingV3AuthInitialDistDelay, INTERVAL, "5 minutes"), - V(TestingV3AuthVotingStartOffset, INTERVAL, "0"), - V(V3AuthVotingInterval, INTERVAL, "1 hour"), - V(V3AuthVoteDelay, INTERVAL, "5 minutes"), - V(V3AuthDistDelay, INTERVAL, "5 minutes"), - V(V3AuthNIntervalsValid, UINT, "3"), - V(V3AuthUseLegacyKey, BOOL, "0"), - V(V3BandwidthsFile, FILENAME, NULL), - VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), - V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"), - V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"), - V(WarnPlaintextPorts, CSV, "23,109,110,143"), - V(UseFilteringSSLBufferevents, BOOL, "0"), - VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), - VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), - VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), - VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), - VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, - NULL), - VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), - V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"), - V(VoteOnHidServDirectoriesV2, BOOL, "1"), - V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " - "300, 900, 2147483647"), - V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, " - "2147483647"), - V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " - "300, 600, 1800, 1800, 1800, 1800, " - "1800, 3600, 7200"), - V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " - "300, 600, 1800, 3600, 3600, 3600, " - "10800, 21600, 43200"), - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), - V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), - V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), - V(TestingConsensusMaxDownloadTries, UINT, "8"), - V(TestingDescriptorMaxDownloadTries, UINT, "8"), - V(TestingMicrodescMaxDownloadTries, UINT, "8"), - V(TestingCertMaxDownloadTries, UINT, "8"), - V(TestingDirAuthVoteGuard, ROUTERSET, NULL), - VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), - - { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } -}; - -/** 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(DirAllowPrivateAddresses, BOOL, "1"), - V(EnforceDistinctSubnets, BOOL, "0"), - V(AssumeReachable, BOOL, "1"), - V(AuthDirMaxServersPerAddr, UINT, "0"), - V(AuthDirMaxServersPerAuthAddr,UINT, "0"), - V(ClientDNSRejectInternalAddresses, BOOL,"0"), - 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"), - V(TestingV3AuthInitialVotingInterval, INTERVAL, "5 minutes"), - V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"), - V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"), - V(TestingV3AuthVotingStartOffset, INTERVAL, "0"), - V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), - V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), - V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), - V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, " - "20, 30, 60"), - V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, " - "30, 60"), - V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " - "15, 20, 30, 60"), - V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " - "15, 20, 30, 60"), - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"), - V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), - V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), - V(TestingConsensusMaxDownloadTries, UINT, "80"), - V(TestingDescriptorMaxDownloadTries, UINT, "80"), - V(TestingMicrodescMaxDownloadTries, UINT, "80"), - V(TestingCertMaxDownloadTries, UINT, "80"), - V(TestingEnableConnBwEvent, BOOL, "1"), - V(TestingEnableCellStatsEvent, BOOL, "1"), - V(TestingEnableTbEmptyEvent, BOOL, "1"), - VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), - - { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } -}; - -#undef VAR -#undef V -#undef OBSOLETE - -#ifdef _WIN32 -static char *get_windows_conf_root(void); -#endif -static int options_act_reversible(const or_options_t *old_options, char **msg); -static int options_act(const or_options_t *old_options); -static int options_transition_allowed(const or_options_t *old, - const or_options_t *new, - char **msg); -static int options_transition_affects_workers( - const or_options_t *old_options, const or_options_t *new_options); -static int options_transition_affects_descriptor( - const or_options_t *old_options, const or_options_t *new_options); -static int check_nickname_list(char **lst, const char *name, char **msg); - -static int parse_client_transport_line(const char *line, int validate_only); - -static int parse_server_transport_line(const char *line, int validate_only); -static char *get_bindaddr_from_transport_listen_line(const char *line, - const char *transport); -static int parse_dir_authority_line(const char *line, - dirinfo_type_t required_type, - int validate_only); -static int parse_dir_fallback_line(const char *line, - int validate_only); -static void port_cfg_free(port_cfg_t *port); -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); - -static int validate_data_directory(or_options_t *options); -static int write_configuration_file(const char *fname, - const or_options_t *options); -static int options_init_logs(or_options_t *options, int validate_only); - -static void init_libevent(const or_options_t *options); -static int opt_streq(const char *s1, const char *s2); -static int parse_outbound_addresses(or_options_t *options, int validate_only, - char **msg); -static void config_maybe_load_geoip_files_(const or_options_t *options, - const or_options_t *old_options); -static int options_validate_cb(void *old_options, void *options, - void *default_options, - int from_setconf, char **msg); - -/** Magic value for or_options_t. */ -#define OR_OPTIONS_MAGIC 9090909 - -/** Configuration format for or_options_t. */ -STATIC config_format_t options_format = { - sizeof(or_options_t), - OR_OPTIONS_MAGIC, - STRUCT_OFFSET(or_options_t, magic_), - option_abbrevs_, - option_vars_, - options_validate_cb, - NULL -}; - -/* - * Functions to read and write the global options pointer. - */ - -/** Command-line and config-file options. */ -static or_options_t *global_options = NULL; -/** The fallback options_t object; this is where we look for options not - * in torrc before we fall back to Tor's defaults. */ -static or_options_t *global_default_options = NULL; -/** Name of most recently read torrc file. */ -static char *torrc_fname = NULL; -/** Name of the most recently read torrc-defaults file.*/ -static char *torrc_defaults_fname; -/** Configuration options set by command line. */ -static config_line_t *global_cmdline_options = NULL; -/** Non-configuration options set by the command line */ -static config_line_t *global_cmdline_only_options = NULL; -/** Boolean: Have we parsed the command line? */ -static int have_parsed_cmdline = 0; -/** Contents of most recently read DirPortFrontPage file. */ -static char *global_dirfrontpagecontents = NULL; -/** List of port_cfg_t for all configured ports. */ -static smartlist_t *configured_ports = NULL; - -/** Return the contents of our frontpage string, or NULL if not configured. */ -const char * -get_dirportfrontpage(void) -{ - return global_dirfrontpagecontents; -} - -/** Return the currently configured options. */ -or_options_t * -get_options_mutable(void) -{ - tor_assert(global_options); - return global_options; -} - -/** Returns the currently configured options */ -const or_options_t * -get_options(void) -{ - return get_options_mutable(); -} - -/** Change the current global options to contain new_val instead of - * their current value; take action based on the new value; free the old value - * as necessary. Returns 0 on success, -1 on failure. - */ -int -set_options(or_options_t *new_val, char **msg) -{ - int i; - smartlist_t *elements; - config_line_t *line; - or_options_t *old_options = global_options; - global_options = new_val; - /* Note that we pass the *old* options below, for comparison. It - * pulls the new options directly out of global_options. */ - if (options_act_reversible(old_options, msg)<0) { - tor_assert(*msg); - global_options = old_options; - return -1; - } - if (options_act(old_options) < 0) { /* acting on the options failed. die. */ - log_err(LD_BUG, - "Acting on config options left us in a broken state. Dying."); - exit(1); - } - /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is - * just starting up then the old_options will be undefined. */ - if (old_options && old_options != global_options) { - elements = smartlist_new(); - for (i=0; options_format.vars[i].name; ++i) { - const config_var_t *var = &options_format.vars[i]; - const char *var_name = var->name; - if (var->type == CONFIG_TYPE_LINELIST_S || - var->type == CONFIG_TYPE_OBSOLETE) { - continue; - } - if (!config_is_same(&options_format, new_val, old_options, var_name)) { - line = config_get_assigned_option(&options_format, new_val, - var_name, 1); - - if (line) { - config_line_t *next; - for (; line; line = next) { - next = line->next; - smartlist_add(elements, line->key); - smartlist_add(elements, line->value); - tor_free(line); - } - } else { - smartlist_add(elements, tor_strdup(options_format.vars[i].name)); - smartlist_add(elements, NULL); - } - } - } - control_event_conf_changed(elements); - SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); - smartlist_free(elements); - } - - if (old_options != global_options) - config_free(&options_format, old_options); - - return 0; -} - -extern const char tor_git_revision[]; /* from tor_main.c */ - -/** The version of this Tor process, as parsed. */ -static char *the_tor_version = NULL; -/** A shorter version of this Tor process's version, for export in our router - * descriptor. (Does not include the git version, if any.) */ -static char *the_short_tor_version = NULL; - -/** Return the current Tor version. */ -const char * -get_version(void) -{ - if (the_tor_version == NULL) { - if (strlen(tor_git_revision)) { - tor_asprintf(&the_tor_version, "%s (git-%s)", get_short_version(), - tor_git_revision); - } else { - the_tor_version = tor_strdup(get_short_version()); - } - } - return the_tor_version; -} - -/** Return the current Tor version, without any git tag. */ -const char * -get_short_version(void) -{ - - if (the_short_tor_version == NULL) { -#ifdef TOR_BUILD_TAG - tor_asprintf(&the_short_tor_version, "%s (%s)", VERSION, TOR_BUILD_TAG); -#else - the_short_tor_version = tor_strdup(VERSION); -#endif - } - return the_short_tor_version; -} - -/** Release additional memory allocated in options - */ -STATIC void -or_options_free(or_options_t *options) -{ - if (!options) - return; - - routerset_free(options->ExcludeExitNodesUnion_); - if (options->NodeFamilySets) { - SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *, - rs, routerset_free(rs)); - smartlist_free(options->NodeFamilySets); - } - tor_free(options->BridgePassword_AuthDigest_); - tor_free(options->command_arg); - config_free(&options_format, options); -} - -/** Release all memory and resources held by global configuration structures. - */ -void -config_free_all(void) -{ - or_options_free(global_options); - global_options = NULL; - or_options_free(global_default_options); - global_default_options = NULL; - - config_free_lines(global_cmdline_options); - global_cmdline_options = NULL; - - config_free_lines(global_cmdline_only_options); - global_cmdline_only_options = NULL; - - if (configured_ports) { - SMARTLIST_FOREACH(configured_ports, - port_cfg_t *, p, port_cfg_free(p)); - smartlist_free(configured_ports); - configured_ports = NULL; - } - - tor_free(torrc_fname); - tor_free(torrc_defaults_fname); - tor_free(the_tor_version); - tor_free(global_dirfrontpagecontents); - - tor_free(the_short_tor_version); - tor_free(the_tor_version); -} - -/** Make address -- a piece of information related to our operation as - * a client -- safe to log according to the settings in options->SafeLogging, - * and return it. - * - * (We return "[scrubbed]" if SafeLogging is "1", and address otherwise.) - */ -const char * -safe_str_client(const char *address) -{ - tor_assert(address); - if (get_options()->SafeLogging_ == SAFELOG_SCRUB_ALL) - return "[scrubbed]"; - else - return address; -} - -/** Make address -- a piece of information of unspecified sensitivity - * -- safe to log according to the settings in options->SafeLogging, and - * return it. - * - * (We return "[scrubbed]" if SafeLogging is anything besides "0", and address - * otherwise.) - */ -const char * -safe_str(const char *address) -{ - tor_assert(address); - if (get_options()->SafeLogging_ != SAFELOG_SCRUB_NONE) - return "[scrubbed]"; - else - return address; -} - -/** Equivalent to escaped(safe_str_client(address)). See reentrancy note on - * escaped(): don't use this outside the main thread, or twice in the same - * log statement. */ -const char * -escaped_safe_str_client(const char *address) -{ - if (get_options()->SafeLogging_ == SAFELOG_SCRUB_ALL) - return "[scrubbed]"; - else - return escaped(address); -} - -/** Equivalent to escaped(safe_str(address)). See reentrancy note on - * escaped(): don't use this outside the main thread, or twice in the same - * log statement. */ -const char * -escaped_safe_str(const char *address) -{ - if (get_options()->SafeLogging_ != SAFELOG_SCRUB_NONE) - return "[scrubbed]"; - else - return escaped(address); -} - -/** Add the default directory authorities directly into the trusted dir list, - * but only add them insofar as they share bits with type. */ -static void -add_default_trusted_dir_authorities(dirinfo_type_t type) -{ - int i; - const char *authorities[] = { - "moria1 orport=9101 " - "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " - "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", - "tor26 v1 orport=443 v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " - "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", - "dizum orport=443 v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " - "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", - "Tonga orport=443 bridge 82.94.251.203:80 " - "4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D", - "turtles orport=9090 " - "v3ident=27B6B5996C426270A5C95488AA5BCEB6BCC86956 " - "76.73.17.194:9030 F397 038A DC51 3361 35E7 B80B D99C A384 4360 292B", - "gabelmoo orport=443 " - "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " - "212.112.245.170:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", - "dannenberg orport=443 " - "v3ident=585769C78764D58426B8B52B6651A5A71137189A " - "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", - "urras orport=80 v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C " - "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417", - "maatuska orport=80 " - "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " - "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", - "Faravahar orport=443 " - "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " - "154.35.32.5:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", - NULL - }; - for (i=0; authorities[i]; i++) { - if (parse_dir_authority_line(authorities[i], type, 0)<0) { - log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s", - authorities[i]); - } - } -} - -/** Add the default fallback directory servers into the fallback directory - * server list. */ -static void -add_default_fallback_dir_servers(void) -{ - int i; - const char *fallback[] = { - NULL - }; - for (i=0; fallback[i]; i++) { - if (parse_dir_fallback_line(fallback[i], 0)<0) { - log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s", - fallback[i]); - } - } -} - -/** Look at all the config options for using alternate directory - * authorities, and make sure none of them are broken. Also, warn the - * user if we changed any dangerous ones. - */ -static int -validate_dir_servers(or_options_t *options, or_options_t *old_options) -{ - config_line_t *cl; - - if (options->DirAuthorities && - (options->AlternateDirAuthority || options->AlternateBridgeAuthority || - options->AlternateHSAuthority)) { - log_warn(LD_CONFIG, - "You cannot set both DirAuthority and Alternate*Authority."); - return -1; - } - - /* do we want to complain to the user about being partitionable? */ - if ((options->DirAuthorities && - (!old_options || - !config_lines_eq(options->DirAuthorities, - old_options->DirAuthorities))) || - (options->AlternateDirAuthority && - (!old_options || - !config_lines_eq(options->AlternateDirAuthority, - old_options->AlternateDirAuthority)))) { - log_warn(LD_CONFIG, - "You have used DirAuthority or AlternateDirAuthority to " - "specify alternate directory authorities in " - "your configuration. This is potentially dangerous: it can " - "make you look different from all other Tor users, and hurt " - "your anonymity. Even if you've specified the same " - "authorities as Tor uses by default, the defaults could " - "change in the future. Be sure you know what you're doing."); - } - - /* Now go through the four ways you can configure an alternate - * set of directory authorities, and make sure none are broken. */ - for (cl = options->DirAuthorities; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) - return -1; - for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) - return -1; - for (cl = options->AlternateDirAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) - return -1; - for (cl = options->AlternateHSAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0) - return -1; - for (cl = options->FallbackDir; cl; cl = cl->next) - if (parse_dir_fallback_line(cl->value, 1)<0) - return -1; - return 0; -} - -/** Look at all the config options and assign new dir authorities - * as appropriate. - */ -static int -consider_adding_dir_servers(const or_options_t *options, - const or_options_t *old_options) -{ - config_line_t *cl; - int need_to_update = - !smartlist_len(router_get_trusted_dir_servers()) || - !smartlist_len(router_get_fallback_dir_servers()) || !old_options || - !config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) || - !config_lines_eq(options->FallbackDir, old_options->FallbackDir) || - !config_lines_eq(options->AlternateBridgeAuthority, - old_options->AlternateBridgeAuthority) || - !config_lines_eq(options->AlternateDirAuthority, - old_options->AlternateDirAuthority) || - !config_lines_eq(options->AlternateHSAuthority, - old_options->AlternateHSAuthority); - - if (!need_to_update) - return 0; /* all done */ - - /* Start from a clean slate. */ - clear_dir_servers(); - - if (!options->DirAuthorities) { - /* then we may want some of the defaults */ - dirinfo_type_t type = NO_DIRINFO; - if (!options->AlternateBridgeAuthority) - type |= BRIDGE_DIRINFO; - if (!options->AlternateDirAuthority) - type |= V1_DIRINFO | V3_DIRINFO | EXTRAINFO_DIRINFO | - MICRODESC_DIRINFO; - if (!options->AlternateHSAuthority) - type |= HIDSERV_DIRINFO; - add_default_trusted_dir_authorities(type); - } - if (!options->FallbackDir) - add_default_fallback_dir_servers(); - - for (cl = options->DirAuthorities; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) - return -1; - for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) - return -1; - for (cl = options->AlternateDirAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) - return -1; - for (cl = options->AlternateHSAuthority; cl; cl = cl->next) - if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) - return -1; - for (cl = options->FallbackDir; cl; cl = cl->next) - if (parse_dir_fallback_line(cl->value, 0)<0) - return -1; - return 0; -} - -/** Fetch the active option list, and take actions based on it. All of the - * things we do should survive being done repeatedly. If present, - * old_options contains the previous value of the options. - * - * Return 0 if all goes well, return -1 if things went badly. - */ -static int -options_act_reversible(const or_options_t *old_options, char **msg) -{ - smartlist_t *new_listeners = smartlist_new(); - smartlist_t *replaced_listeners = smartlist_new(); - static int libevent_initialized = 0; - or_options_t *options = get_options_mutable(); - int running_tor = options->command == CMD_RUN_TOR; - int set_conn_limit = 0; - int r = -1; - int logs_marked = 0; - int old_min_log_level = get_min_log_level(); - - /* Daemonize _first_, since we only want to open most of this stuff in - * the subprocess. Libevent bases can't be reliably inherited across - * processes. */ - if (running_tor && options->RunAsDaemon) { - /* No need to roll back, since you can't change the value. */ - start_daemon(); - } - -#ifndef HAVE_SYS_UN_H - if (options->ControlSocket || options->ControlSocketsGroupWritable) { - *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported " - "on this OS/with this build."); - goto rollback; - } -#else - if (options->ControlSocketsGroupWritable && !options->ControlSocket) { - *msg = tor_strdup("Setting ControlSocketGroupWritable without setting" - "a ControlSocket makes no sense."); - goto rollback; - } -#endif - - if (running_tor) { - int n_ports=0; - /* We need to set the connection limit before we can open the listeners. */ - if (set_max_file_descriptors((unsigned)options->ConnLimit, - &options->ConnLimit_) < 0) { - *msg = tor_strdup("Problem with ConnLimit value. See logs for details."); - goto rollback; - } - set_conn_limit = 1; - - /* Set up libevent. (We need to do this before we can register the - * listeners as listeners.) */ - if (running_tor && !libevent_initialized) { - init_libevent(options); - libevent_initialized = 1; - } - - /* Adjust the port configuration so we can launch listeners. */ - if (parse_ports(options, 0, msg, &n_ports)) { - if (!*msg) - *msg = tor_strdup("Unexpected problem parsing port config"); - goto rollback; - } - - /* Set the hibernation state appropriately.*/ - consider_hibernation(time(NULL)); - - /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. If - * networking is disabled, this will close all but the control listeners, - * but disable those. */ - if (!we_are_hibernating()) { - if (retry_all_listeners(replaced_listeners, new_listeners, - options->DisableNetwork) < 0) { - *msg = tor_strdup("Failed to bind one of the listener ports."); - goto rollback; - } - } - if (options->DisableNetwork) { - /* Aggressively close non-controller stuff, NOW */ - log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept " - "non-control network connections. Shutting down all existing " - "connections."); - connection_mark_all_noncontrol_connections(); - } - } - -#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) - /* Open /dev/pf before dropping privileges. */ - if (options->TransPort_set) { - if (get_pf_socket() < 0) { - *msg = tor_strdup("Unable to open /dev/pf for transparent proxy."); - goto rollback; - } - } -#endif - - /* Attempt to lock all current and future memory with mlockall() only once */ - if (options->DisableAllSwap) { - if (tor_mlockall() == -1) { - *msg = tor_strdup("DisableAllSwap failure. Do you have proper " - "permissions?"); - goto done; - } - } - - /* Setuid/setgid as appropriate */ - if (options->User) { - if (switch_id(options->User) != 0) { - /* No need to roll back, since you can't change the value. */ - *msg = tor_strdup("Problem with User value. See logs for details."); - goto done; - } - } - - /* Ensure data directory is private; create if possible. */ - if (check_private_dir(options->DataDirectory, - running_tor ? CPD_CREATE : CPD_CHECK, - options->User)<0) { - tor_asprintf(msg, - "Couldn't access/create private data directory \"%s\"", - options->DataDirectory); - goto done; - /* No need to roll back, since you can't change the value. */ - } - - /* Bail out at this point if we're not going to be a client or server: - * we don't run Tor itself. */ - if (!running_tor) - goto commit; - - mark_logs_temp(); /* Close current logs once new logs are open. */ - logs_marked = 1; - if (options_init_logs(options, 0)<0) { /* Configure the tor_log(s) */ - *msg = tor_strdup("Failed to init Log options. See logs for details."); - goto rollback; - } - - commit: - r = 0; - if (logs_marked) { - log_severity_list_t *severity = - tor_malloc_zero(sizeof(log_severity_list_t)); - close_temp_logs(); - add_callback_log(severity, control_event_logmsg); - control_adjust_event_log_severity(); - tor_free(severity); - tor_log_update_sigsafe_err_fds(); - } - if (get_min_log_level() >= LOG_INFO && - get_min_log_level() != old_min_log_level) { - log_warn(LD_GENERAL, "Your log may contain sensitive information: you're " - "logging more than \"notice\". Please log safely. Don't log " - "unless it serves an important reason, and overwrite the log " - "afterwards."); - } - - SMARTLIST_FOREACH(replaced_listeners, connection_t *, conn, - { - log_notice(LD_NET, "Closing old %s on %s:%d", - conn_type_to_string(conn->type), conn->address, conn->port); - connection_close_immediate(conn); - connection_mark_for_close(conn); - }); - goto done; - - rollback: - r = -1; - tor_assert(*msg); - - if (logs_marked) { - rollback_log_changes(); - control_adjust_event_log_severity(); - } - - if (set_conn_limit && old_options) - set_max_file_descriptors((unsigned)old_options->ConnLimit, - &options->ConnLimit_); - - SMARTLIST_FOREACH(new_listeners, connection_t *, conn, - { - log_notice(LD_NET, "Closing partially-constructed %s on %s:%d", - conn_type_to_string(conn->type), conn->address, conn->port); - connection_close_immediate(conn); - connection_mark_for_close(conn); - }); - - done: - smartlist_free(new_listeners); - smartlist_free(replaced_listeners); - return r; -} - -/** If we need to have a GEOIP ip-to-country map to run with our configured - * options, return 1 and set *reason_out to a description of why. */ -int -options_need_geoip_info(const or_options_t *options, const char **reason_out) -{ - int bridge_usage = - options->BridgeRelay && options->BridgeRecordUsageByCountry; - int routerset_usage = - routerset_needs_geoip(options->EntryNodes) || - routerset_needs_geoip(options->ExitNodes) || - routerset_needs_geoip(options->ExcludeExitNodes) || - routerset_needs_geoip(options->ExcludeNodes); - - if (routerset_usage && reason_out) { - *reason_out = "We've been configured to use (or avoid) nodes in certain " - "countries, and we need GEOIP information to figure out which ones they " - "are."; - } else if (bridge_usage && reason_out) { - *reason_out = "We've been configured to see which countries can access " - "us as a bridge, and we need GEOIP information to tell which countries " - "clients are in."; - } - return bridge_usage || routerset_usage; -} - -/** Return the bandwidthrate that we are going to report to the authorities - * based on the config options. */ -uint32_t -get_effective_bwrate(const or_options_t *options) -{ - uint64_t bw = options->BandwidthRate; - if (bw > options->MaxAdvertisedBandwidth) - bw = options->MaxAdvertisedBandwidth; - if (options->RelayBandwidthRate > 0 && bw > options->RelayBandwidthRate) - bw = options->RelayBandwidthRate; - /* ensure_bandwidth_cap() makes sure that this cast can't overflow. */ - return (uint32_t)bw; -} - -/** Return the bandwidthburst that we are going to report to the authorities - * based on the config options. */ -uint32_t -get_effective_bwburst(const or_options_t *options) -{ - uint64_t bw = options->BandwidthBurst; - if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst) - bw = options->RelayBandwidthBurst; - /* ensure_bandwidth_cap() makes sure that this cast can't overflow. */ - return (uint32_t)bw; -} - -/** Return True if any changes from old_options to - * new_options needs us to refresh our TLS context. */ -static int -options_transition_requires_fresh_tls_context(const or_options_t *old_options, - const or_options_t *new_options) -{ - tor_assert(new_options); - - if (!old_options) - return 0; - - if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { - return 1; - } - - if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup)) - return 1; - - return 0; -} - -/** Fetch the active option list, and take actions based on it. All of the - * things we do should survive being done repeatedly. If present, - * old_options contains the previous value of the options. - * - * Return 0 if all goes well, return -1 if it's time to die. - * - * Note: We haven't moved all the "act on new configuration" logic - * here yet. Some is still in do_hup() and other places. - */ -static int -options_act(const or_options_t *old_options) -{ - config_line_t *cl; - or_options_t *options = get_options_mutable(); - int running_tor = options->command == CMD_RUN_TOR; - char *msg=NULL; - const int transition_affects_workers = - old_options && options_transition_affects_workers(old_options, options); - int old_ewma_enabled; - - /* disable ptrace and later, other basic debugging techniques */ - { - /* Remember if we already disabled debugger attachment */ - static int disabled_debugger_attach = 0; - /* Remember if we already warned about being configured not to disable - * debugger attachment */ - static int warned_debugger_attach = 0; - /* Don't disable debugger attachment when we're running the unit tests. */ - if (options->DisableDebuggerAttachment && !disabled_debugger_attach && - running_tor) { - int ok = tor_disable_debugger_attach(); - if (warned_debugger_attach && ok == 1) { - log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged " - "users."); - } - disabled_debugger_attach = (ok == 1); - } else if (!options->DisableDebuggerAttachment && - !warned_debugger_attach) { - log_notice(LD_CONFIG, "Not disabling debugger attaching for " - "unprivileged users."); - warned_debugger_attach = 1; - } - } - - /* Write control ports to disk as appropriate */ - control_ports_write_to_file(); - - if (running_tor && !have_lockfile()) { - if (try_locking(options, 1) < 0) - return -1; - } - - if (consider_adding_dir_servers(options, old_options) < 0) - return -1; - -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif - -#ifdef ENABLE_TOR2WEB_MODE - if (!options->Tor2webMode) { - log_err(LD_CONFIG, "This copy of Tor was compiled to run in " - "'tor2web mode'. It can only be run with the Tor2webMode torrc " - "option enabled."); - return -1; - } -#else - if (options->Tor2webMode) { - log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " - "'tor2web mode'. It cannot be run with the Tor2webMode torrc " - "option enabled. To enable Tor2webMode recompile with the " - "--enable-tor2webmode option."); - return -1; - } -#endif - - if (options->SafeLogging_ != SAFELOG_SCRUB_ALL && - (!old_options || old_options->SafeLogging_ != options->SafeLogging_)) { - log_warn(LD_GENERAL, "Your log may contain sensitive information - you " - "disabled SafeLogging. Please log safely. Don't log unless it " - "serves an important reason. Overwrite the log afterwards."); - } - - if (options->Bridges) { - mark_bridge_list(); - for (cl = options->Bridges; cl; cl = cl->next) { - bridge_line_t *bridge_line = parse_bridge_line(cl->value); - if (!bridge_line) { - log_warn(LD_BUG, - "Previously validated Bridge line could not be added!"); - return -1; - } - bridge_add_from_config(bridge_line); - } - sweep_bridge_list(); - } - - if (running_tor && rend_config_services(options, 0)<0) { - log_warn(LD_BUG, - "Previously validated hidden services line could not be added!"); - return -1; - } - - if (running_tor && rend_parse_service_authorization(options, 0) < 0) { - log_warn(LD_BUG, "Previously validated client authorization for " - "hidden services could not be added!"); - return -1; - } - - /* Load state */ - if (! or_state_loaded() && running_tor) { - if (or_state_load()) - return -1; - rep_hist_load_mtbf_data(time(NULL)); - } - - mark_transport_list(); - pt_prepare_proxy_list_for_config_read(); - if (options->ClientTransportPlugin) { - for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { - if (parse_client_transport_line(cl->value, 0)<0) { - log_warn(LD_BUG, - "Previously validated ClientTransportPlugin line " - "could not be added!"); - return -1; - } - } - } - - if (options->ServerTransportPlugin && server_mode(options)) { - for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { - if (parse_server_transport_line(cl->value, 0)<0) { - log_warn(LD_BUG, - "Previously validated ServerTransportPlugin line " - "could not be added!"); - return -1; - } - } - } - sweep_transport_list(); - sweep_proxy_list(); - - /* Bail out at this point if we're not going to be a client or server: - * we want to not fork, and to log stuff to stderr. */ - if (!running_tor) - return 0; - - /* Finish backgrounding the process */ - if (options->RunAsDaemon) { - /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */ - finish_daemon(options->DataDirectory); - } - - /* If needed, generate a new TLS DH prime according to the current torrc. */ - if (server_mode(options) && options->DynamicDHGroups) { - char *keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); - return -1; - } - tor_free(keydir); - - if (!old_options || !old_options->DynamicDHGroups) { - char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); - crypto_set_tls_dh_prime(fname); - tor_free(fname); - } - } else { /* clients don't need a dynamic DH prime. */ - crypto_set_tls_dh_prime(NULL); - } - - /* We want to reinit keys as needed before we do much of anything else: - keys are important, and other things can depend on them. */ - if (transition_affects_workers || - (options->V3AuthoritativeDir && (!old_options || - !old_options->V3AuthoritativeDir))) { - if (init_keys() < 0) { - log_warn(LD_BUG,"Error initializing keys; exiting"); - return -1; - } - } else if (old_options && - options_transition_requires_fresh_tls_context(old_options, - options)) { - if (router_initialize_tls_context() < 0) { - log_warn(LD_BUG,"Error initializing TLS context."); - return -1; - } - } - - /* Write our PID to the PID file. If we do not have write permissions we - * will log a warning */ - if (options->PidFile) - write_pidfile(options->PidFile); - - /* Register addressmap directives */ - config_register_addressmaps(options); - parse_virtual_addr_network(options->VirtualAddrNetworkIPv4, AF_INET,0,NULL); - parse_virtual_addr_network(options->VirtualAddrNetworkIPv6, AF_INET6,0,NULL); - - /* Update address policies. */ - if (policies_parse_from_options(options) < 0) { - /* This should be impossible, but let's be sure. */ - log_warn(LD_BUG,"Error parsing already-validated policy options."); - return -1; - } - - if (init_control_cookie_authentication(options->CookieAuthentication) < 0) { - log_warn(LD_CONFIG,"Error creating control cookie authentication file."); - return -1; - } - - /* If we have an ExtORPort, initialize its auth cookie. */ - if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) { - log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file."); - return -1; - } - - monitor_owning_controller_process(options->OwningControllerProcess); - - /* reload keys as needed for rendezvous services. */ - if (rend_service_load_all_keys()<0) { - log_warn(LD_GENERAL,"Error loading rendezvous service keys"); - return -1; - } - - /* Set up accounting */ - if (accounting_parse_options(options, 0)<0) { - log_warn(LD_CONFIG,"Error in accounting options"); - return -1; - } - if (accounting_is_enabled(options)) - configure_accounting(time(NULL)); - -#ifdef USE_BUFFEREVENTS - /* If we're using the bufferevents implementation and our rate limits - * changed, we need to tell the rate-limiting system about it. */ - if (!old_options || - old_options->BandwidthRate != options->BandwidthRate || - old_options->BandwidthBurst != options->BandwidthBurst || - old_options->RelayBandwidthRate != options->RelayBandwidthRate || - old_options->RelayBandwidthBurst != options->RelayBandwidthBurst) - connection_bucket_init(); -#endif - - old_ewma_enabled = cell_ewma_enabled(); - /* Change the cell EWMA settings */ - cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); - /* If we just enabled ewma, set the cmux policy on all active channels */ - if (cell_ewma_enabled() && !old_ewma_enabled) { - channel_set_cmux_policy_everywhere(&ewma_policy); - } else if (!cell_ewma_enabled() && old_ewma_enabled) { - /* Turn it off everywhere */ - channel_set_cmux_policy_everywhere(NULL); - } - - /* Update the BridgePassword's hashed version as needed. We store this as a - * digest so that we can do side-channel-proof comparisons on it. - */ - if (options->BridgePassword) { - char *http_authenticator; - http_authenticator = alloc_http_authenticator(options->BridgePassword); - if (!http_authenticator) { - log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " - "BridgePassword."); - return -1; - } - options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN); - crypto_digest256(options->BridgePassword_AuthDigest_, - http_authenticator, strlen(http_authenticator), - DIGEST_SHA256); - tor_free(http_authenticator); - } - - if (parse_outbound_addresses(options, 0, &msg) < 0) { - log_warn(LD_BUG, "Failed parsing oubound bind addresses: %s", msg); - tor_free(msg); - return -1; - } - - /* Check for transitions that need action. */ - if (old_options) { - int revise_trackexithosts = 0; - int revise_automap_entries = 0; - if ((options->UseEntryGuards && !old_options->UseEntryGuards) || - options->UseBridges != old_options->UseBridges || - (options->UseBridges && - !config_lines_eq(options->Bridges, old_options->Bridges)) || - !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) || - !routerset_equal(old_options->ExcludeExitNodes, - options->ExcludeExitNodes) || - !routerset_equal(old_options->EntryNodes, options->EntryNodes) || - !routerset_equal(old_options->ExitNodes, options->ExitNodes) || - options->StrictNodes != old_options->StrictNodes) { - log_info(LD_CIRC, - "Changed to using entry guards or bridges, or changed " - "preferred or excluded node lists. " - "Abandoning previous circuits."); - circuit_mark_all_unused_circs(); - circuit_mark_all_dirty_circs_as_unusable(); - revise_trackexithosts = 1; - } - - if (!smartlist_strings_eq(old_options->TrackHostExits, - options->TrackHostExits)) - revise_trackexithosts = 1; - - if (revise_trackexithosts) - addressmap_clear_excluded_trackexithosts(options); - - if (!options->AutomapHostsOnResolve) { - if (old_options->AutomapHostsOnResolve) - revise_automap_entries = 1; - } else { - if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes, - options->AutomapHostsSuffixes)) - revise_automap_entries = 1; - else if (!opt_streq(old_options->VirtualAddrNetworkIPv4, - options->VirtualAddrNetworkIPv4) || - !opt_streq(old_options->VirtualAddrNetworkIPv6, - options->VirtualAddrNetworkIPv6)) - revise_automap_entries = 1; - } - - if (revise_automap_entries) - addressmap_clear_invalid_automaps(options); - -/* How long should we delay counting bridge stats after becoming a bridge? - * We use this so we don't count people who used our bridge thinking it is - * a relay. If you change this, don't forget to change the log message - * below. It's 4 hours (the time it takes to stop being used by clients) - * plus some extra time for clock skew. */ -#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60) - - if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) { - int was_relay = 0; - if (options->BridgeRelay) { - time_t int_start = time(NULL); - if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) { - int_start += RELAY_BRIDGE_STATS_DELAY; - was_relay = 1; - } - geoip_bridge_stats_init(int_start); - log_info(LD_CONFIG, "We are acting as a bridge now. Starting new " - "GeoIP stats interval%s.", was_relay ? " in 6 " - "hours from now" : ""); - } else { - geoip_bridge_stats_term(); - log_info(LD_GENERAL, "We are no longer acting as a bridge. " - "Forgetting GeoIP stats."); - } - } - - if (transition_affects_workers) { - log_info(LD_GENERAL, - "Worker-related options changed. Rotating workers."); - - if (server_mode(options) && !server_mode(old_options)) { - ip_address_changed(0); - if (can_complete_circuit || !any_predicted_circuits(time(NULL))) - inform_testing_reachability(); - } - cpuworkers_rotate(); - if (dns_reset()) - return -1; - } else { - if (dns_reset()) - return -1; - } - - if (options->PerConnBWRate != old_options->PerConnBWRate || - options->PerConnBWBurst != old_options->PerConnBWBurst) - connection_or_update_token_buckets(get_connection_array(), options); - } - - config_maybe_load_geoip_files_(options, old_options); - - if (geoip_is_loaded(AF_INET) && options->GeoIPExcludeUnknown) { - /* ExcludeUnknown is true or "auto" */ - const int is_auto = options->GeoIPExcludeUnknown == -1; - int changed; - - changed = routerset_add_unknown_ccs(&options->ExcludeNodes, is_auto); - changed += routerset_add_unknown_ccs(&options->ExcludeExitNodes, is_auto); - - if (changed) - routerset_add_unknown_ccs(&options->ExcludeExitNodesUnion_, is_auto); - } - - if (options->CellStatistics || options->DirReqStatistics || - options->EntryStatistics || options->ExitPortStatistics || - options->ConnDirectionStatistics || - options->BridgeAuthoritativeDir) { - time_t now = time(NULL); - int print_notice = 0; - - /* If we aren't acting as a server, we can't collect stats anyway. */ - if (!server_mode(options)) { - options->CellStatistics = 0; - options->DirReqStatistics = 0; - options->EntryStatistics = 0; - options->ExitPortStatistics = 0; - } - - if ((!old_options || !old_options->CellStatistics) && - options->CellStatistics) { - rep_hist_buffer_stats_init(now); - print_notice = 1; - } - if ((!old_options || !old_options->DirReqStatistics) && - options->DirReqStatistics) { - if (geoip_is_loaded(AF_INET)) { - geoip_dirreq_stats_init(now); - print_notice = 1; - } else { - options->DirReqStatistics = 0; - /* Don't warn Tor clients, they don't use statistics */ - 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 " - "GeoIPFile option."); - } - } - if ((!old_options || !old_options->EntryStatistics) && - options->EntryStatistics && !should_record_bridge_info(options)) { - if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) { - geoip_entry_stats_init(now); - print_notice = 1; - } else { - options->EntryStatistics = 0; - log_notice(LD_CONFIG, "Configured to measure entry node " - "statistics, but no GeoIP database found. " - "Please specify a GeoIP database using the " - "GeoIPFile option."); - } - } - if ((!old_options || !old_options->ExitPortStatistics) && - options->ExitPortStatistics) { - rep_hist_exit_stats_init(now); - print_notice = 1; - } - if ((!old_options || !old_options->ConnDirectionStatistics) && - options->ConnDirectionStatistics) { - rep_hist_conn_stats_init(now); - } - if ((!old_options || !old_options->BridgeAuthoritativeDir) && - options->BridgeAuthoritativeDir) { - rep_hist_desc_stats_init(now); - print_notice = 1; - } - if (print_notice) - log_notice(LD_CONFIG, "Configured to measure statistics. Look for " - "the *-stats files that will first be written to the " - "data directory in 24 hours from now."); - } - - if (old_options && old_options->CellStatistics && - !options->CellStatistics) - rep_hist_buffer_stats_term(); - if (old_options && old_options->DirReqStatistics && - !options->DirReqStatistics) - geoip_dirreq_stats_term(); - if (old_options && old_options->EntryStatistics && - !options->EntryStatistics) - geoip_entry_stats_term(); - if (old_options && old_options->ExitPortStatistics && - !options->ExitPortStatistics) - rep_hist_exit_stats_term(); - if (old_options && old_options->ConnDirectionStatistics && - !options->ConnDirectionStatistics) - rep_hist_conn_stats_term(); - if (old_options && old_options->BridgeAuthoritativeDir && - !options->BridgeAuthoritativeDir) - rep_hist_desc_stats_term(); - - /* Check if we need to parse and add the EntryNodes config option. */ - if (options->EntryNodes && - (!old_options || - !routerset_equal(old_options->EntryNodes,options->EntryNodes) || - !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes))) - entry_nodes_should_be_added(); - - /* Since our options changed, we might need to regenerate and upload our - * server descriptor. - */ - if (!old_options || - options_transition_affects_descriptor(old_options, options)) - mark_my_descriptor_dirty("config change"); - - /* We may need to reschedule some directory stuff if our status changed. */ - if (old_options) { - if (authdir_mode_v3(options) && !authdir_mode_v3(old_options)) - dirvote_recalculate_timing(options, time(NULL)); - if (!bool_eq(directory_fetches_dir_info_early(options), - directory_fetches_dir_info_early(old_options)) || - !bool_eq(directory_fetches_dir_info_later(options), - directory_fetches_dir_info_later(old_options))) { - /* Make sure update_router_have_min_dir_info gets called. */ - router_dir_info_changed(); - /* We might need to download a new consensus status later or sooner than - * we had expected. */ - update_consensus_networkstatus_fetch_time(time(NULL)); - } - } - - /* Load the webpage we're going to serve every time someone asks for '/' on - our DirPort. */ - tor_free(global_dirfrontpagecontents); - if (options->DirPortFrontPage) { - global_dirfrontpagecontents = - read_file_to_str(options->DirPortFrontPage, 0, NULL); - if (!global_dirfrontpagecontents) { - log_warn(LD_CONFIG, - "DirPortFrontPage file '%s' not found. Continuing anyway.", - options->DirPortFrontPage); - } - } - - return 0; -} - -static const struct { - const char *name; - int takes_argument; -} CMDLINE_ONLY_OPTIONS[] = { - { "-f", 1 }, - { "--allow-missing-torrc", 0 }, - { "--defaults-torrc", 1 }, - { "--hash-password", 1 }, - { "--dump-config", 1 }, - { "--list-fingerprint", 0 }, - { "--verify-config", 0 }, - { "--ignore-missing-torrc", 0 }, - { "--quiet", 0 }, - { "--hush", 0 }, - { "--version", 0 }, - { "--library-versions", 0 }, - { "-h", 0 }, - { "--help", 0 }, - { "--list-torrc-options", 0 }, - { "--digests", 0 }, - { "--nt-service", 0 }, - { "-nt-service", 0 }, - { NULL, 0 }, -}; - -/** Helper: Read a list of configuration options from the command line. If - * successful, or if ignore_errors is set, put them in *result, put the - * commandline-only options in *cmdline_result, and return 0; - * otherwise, return -1 and leave *result and cmdline_result - * alone. */ -int -config_parse_commandline(int argc, char **argv, int ignore_errors, - config_line_t **result, - config_line_t **cmdline_result) -{ - config_line_t *param = NULL; - - config_line_t *front = NULL; - config_line_t **new = &front; - - config_line_t *front_cmdline = NULL; - config_line_t **new_cmdline = &front_cmdline; - - char *s, *arg; - int i = 1; - - while (i < argc) { - unsigned command = CONFIG_LINE_NORMAL; - int want_arg = 1; - int is_cmdline = 0; - int j; - - for (j = 0; CMDLINE_ONLY_OPTIONS[j].name != NULL; ++j) { - if (!strcmp(argv[i], CMDLINE_ONLY_OPTIONS[j].name)) { - is_cmdline = 1; - want_arg = CMDLINE_ONLY_OPTIONS[j].takes_argument; - break; - } - } - - s = argv[i]; - - /* Each keyword may be prefixed with one or two dashes. */ - if (*s == '-') - s++; - if (*s == '-') - s++; - /* Figure out the command, if any. */ - if (*s == '+') { - s++; - command = CONFIG_LINE_APPEND; - } else if (*s == '/') { - s++; - command = CONFIG_LINE_CLEAR; - /* A 'clear' command has no argument. */ - want_arg = 0; - } - - if (want_arg && i == argc-1) { - if (ignore_errors) { - arg = strdup(""); - } else { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - config_free_lines(front_cmdline); - return -1; - } - } else { - arg = want_arg ? tor_strdup(argv[i+1]) : strdup(""); - } - - param = tor_malloc_zero(sizeof(config_line_t)); - param->key = is_cmdline ? tor_strdup(argv[i]) : tor_strdup(s); - param->value = arg; - param->command = command; - param->next = NULL; - log_debug(LD_CONFIG, "command line: parsed keyword '%s', value '%s'", - param->key, param->value); - - if (is_cmdline) { - *new_cmdline = param; - new_cmdline = &((*new_cmdline)->next); - } else { - *new = param; - new = &((*new)->next); - } - - i += want_arg ? 2 : 1; - } - *cmdline_result = front_cmdline; - *result = front; - return 0; -} - -/** Return true iff key is a valid configuration option. */ -int -option_is_recognized(const char *key) -{ - const config_var_t *var = config_find_option(&options_format, key); - return (var != NULL); -} - -/** Return the canonical name of a configuration option, or NULL - * if no such option exists. */ -const char * -option_get_canonical_name(const char *key) -{ - const config_var_t *var = config_find_option(&options_format, key); - return var ? var->name : NULL; -} - -/** Return a canonical list of the options assigned for key. - */ -config_line_t * -option_get_assignment(const or_options_t *options, const char *key) -{ - return config_get_assigned_option(&options_format, options, key, 1); -} - -/** Try assigning list to the global options. You do this by duping - * options, assigning list to the new one, then validating it. If it's - * ok, then throw out the old one and stick with the new one. Else, - * revert to old and return failure. Return SETOPT_OK on success, or - * a setopt_err_t on failure. - * - * If not success, point *msg to a newly allocated string describing - * what went wrong. - */ -setopt_err_t -options_trial_assign(config_line_t *list, int use_defaults, - int clear_first, char **msg) -{ - int r; - or_options_t *trial_options = config_dup(&options_format, get_options()); - - if ((r=config_assign(&options_format, trial_options, - list, use_defaults, clear_first, msg)) < 0) { - config_free(&options_format, trial_options); - return r; - } - - if (options_validate(get_options_mutable(), trial_options, - global_default_options, 1, msg) < 0) { - config_free(&options_format, trial_options); - return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ - } - - if (options_transition_allowed(get_options(), trial_options, msg) < 0) { - config_free(&options_format, trial_options); - return SETOPT_ERR_TRANSITION; - } - - if (set_options(trial_options, msg)<0) { - config_free(&options_format, trial_options); - return SETOPT_ERR_SETTING; - } - - /* we liked it. put it in place. */ - return SETOPT_OK; -} - -/** Print a usage message for tor. */ -static void -print_usage(void) -{ - printf( -"Copyright (c) 2001-2004, Roger Dingledine\n" -"Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n" -"Copyright (c) 2007-2013, The Tor Project, Inc.\n\n" -"tor -f [args]\n" -"See man page for options, or https://www.torproject.org/ for " -"documentation.\n"); -} - -/** Print all non-obsolete torrc options. */ -static void -list_torrc_options(void) -{ - int i; - smartlist_t *lines = smartlist_new(); - for (i = 0; option_vars_[i].name; ++i) { - const config_var_t *var = &option_vars_[i]; - if (var->type == CONFIG_TYPE_OBSOLETE || - var->type == CONFIG_TYPE_LINELIST_V) - continue; - printf("%s\n", var->name); - } - smartlist_free(lines); -} - -/** Last value actually set by resolve_my_address. */ -static uint32_t last_resolved_addr = 0; - -/** Accessor for last_resolved_addr from outside this file. */ -uint32_t -get_last_resolved_addr(void) -{ - return last_resolved_addr; -} - -/** - * Use options-\>Address to guess our public IP address. - * - * Return 0 if all is well, or -1 if we can't find a suitable - * public IP address. - * - * If we are returning 0: - * - Put our public IP address (in host order) into *addr_out. - * - If method_out is non-NULL, set *method_out to a static - * string describing how we arrived at our answer. - * - If hostname_out is non-NULL, and we resolved a hostname to - * get our address, set *hostname_out to a newly allocated string - * holding that hostname. (If we didn't get our address by resolving a - * hostname, set *hostname_out to NULL.) - * - * XXXX ipv6 - */ -int -resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out) -{ - struct in_addr in; - uint32_t addr; /* host order */ - char hostname[256]; - const char *method_used; - const char *hostname_used; - int explicit_ip=1; - int explicit_hostname=1; - int from_interface=0; - char *addr_string = NULL; - const char *address = options->Address; - int notice_severity = warn_severity <= LOG_NOTICE ? - LOG_NOTICE : warn_severity; - - tor_assert(addr_out); - - /* - * Step one: Fill in 'hostname' to be our best guess. - */ - - if (address && *address) { - strlcpy(hostname, address, sizeof(hostname)); - } else { /* then we need to guess our address */ - explicit_ip = 0; /* it's implicit */ - explicit_hostname = 0; /* it's implicit */ - - if (gethostname(hostname, sizeof(hostname)) < 0) { - log_fn(warn_severity, LD_NET,"Error obtaining local hostname"); - return -1; - } - log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname); - } - - /* - * Step two: Now that we know 'hostname', parse it or resolve it. If - * it doesn't parse or resolve, look at the interface address. Set 'addr' - * to be our (host-order) 32-bit answer. - */ - - if (tor_inet_aton(hostname, &in) == 0) { - /* then we have to resolve it */ - explicit_ip = 0; - if (tor_lookup_hostname(hostname, &addr)) { /* failed to resolve */ - uint32_t interface_ip; /* host order */ - - if (explicit_hostname) { - log_fn(warn_severity, LD_CONFIG, - "Could not resolve local Address '%s'. Failing.", hostname); - return -1; - } - log_fn(notice_severity, LD_CONFIG, - "Could not resolve guessed local hostname '%s'. " - "Trying something else.", hostname); - if (get_interface_address(warn_severity, &interface_ip)) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Failing."); - return -1; - } - from_interface = 1; - addr = interface_ip; - log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " - "local interface. Using that.", fmt_addr32(addr)); - strlcpy(hostname, "", sizeof(hostname)); - } else { /* resolved hostname into addr */ - if (!explicit_hostname && - is_internal_IP(addr, 0)) { - uint32_t interface_ip; - - log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " - "resolves to a private IP address (%s). Trying something " - "else.", hostname, fmt_addr32(addr)); - - if (get_interface_address(warn_severity, &interface_ip)) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Too bad."); - } else if (is_internal_IP(interface_ip, 0)) { - log_fn(notice_severity, LD_CONFIG, - "Interface IP address '%s' is a private address too. " - "Ignoring.", fmt_addr32(interface_ip)); - } else { - from_interface = 1; - addr = interface_ip; - log_fn(notice_severity, LD_CONFIG, - "Learned IP address '%s' for local interface." - " Using that.", fmt_addr32(addr)); - strlcpy(hostname, "", sizeof(hostname)); - } - } - } - } else { - addr = ntohl(in.s_addr); /* set addr so that addr_string is not - * illformed */ - } - - /* - * Step three: Check whether 'addr' is an internal IP address, and error - * out if it is and we don't want that. - */ - - addr_string = tor_dup_ip(addr); - if (is_internal_IP(addr, 0)) { - /* make sure we're ok with publishing an internal IP */ - if (!options->DirAuthorities && !options->AlternateDirAuthority) { - /* if they are using the default authorities, disallow internal IPs - * always. */ - log_fn(warn_severity, LD_CONFIG, - "Address '%s' resolves to private IP address '%s'. " - "Tor servers that use the default DirAuthorities must have " - "public IP addresses.", hostname, addr_string); - tor_free(addr_string); - return -1; - } - if (!explicit_ip) { - /* even if they've set their own authorities, require an explicit IP if - * they're using an internal address. */ - log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private " - "IP address '%s'. Please set the Address config option to be " - "the IP address you want to use.", hostname, addr_string); - tor_free(addr_string); - return -1; - } - } - - /* - * Step four: We have a winner! 'addr' is our answer for sure, and - * 'addr_string' is its string form. Fill out the various fields to - * say how we decided it. - */ - - log_debug(LD_CONFIG, "Resolved Address to '%s'.", addr_string); - - if (explicit_ip) { - method_used = "CONFIGURED"; - hostname_used = NULL; - } else if (explicit_hostname) { - method_used = "RESOLVED"; - hostname_used = hostname; - } else if (from_interface) { - method_used = "INTERFACE"; - hostname_used = NULL; - } else { - method_used = "GETHOSTNAME"; - hostname_used = hostname; - } - - *addr_out = addr; - if (method_out) - *method_out = method_used; - if (hostname_out) - *hostname_out = hostname_used ? tor_strdup(hostname_used) : NULL; - - /* - * Step five: Check if the answer has changed since last time (or if - * there was no last time), and if so call various functions to keep - * us up-to-date. - */ - - if (last_resolved_addr && last_resolved_addr != *addr_out) { - /* Leave this as a notice, regardless of the requested severity, - * at least until dynamic IP address support becomes bulletproof. */ - log_notice(LD_NET, - "Your IP address seems to have changed to %s " - "(METHOD=%s%s%s). Updating.", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); - ip_address_changed(0); - } - - if (last_resolved_addr != *addr_out) { - control_event_server_status(LOG_NOTICE, - "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); - } - last_resolved_addr = *addr_out; - - /* - * And finally, clean up and return success. - */ - - tor_free(addr_string); - return 0; -} - -/** Return true iff addr is judged to be on the same network as us, or - * on a private network. - */ -int -is_local_addr(const tor_addr_t *addr) -{ - if (tor_addr_is_internal(addr, 0)) - return 1; - /* Check whether ip is on the same /24 as we are. */ - if (get_options()->EnforceDistinctSubnets == 0) - return 0; - if (tor_addr_family(addr) == AF_INET) { - /*XXXX023 IP6 what corresponds to an /24? */ - uint32_t ip = tor_addr_to_ipv4h(addr); - - /* It's possible that this next check will hit before the first time - * resolve_my_address actually succeeds. (For clients, it is likely that - * resolve_my_address will never be called at all). In those cases, - * last_resolved_addr will be 0, and so checking to see whether ip is on - * the same /24 as last_resolved_addr will be the same as checking whether - * it was on net 0, which is already done by is_internal_IP. - */ - if ((last_resolved_addr & (uint32_t)0xffffff00ul) - == (ip & (uint32_t)0xffffff00ul)) - return 1; - } - return 0; -} - -/** Return a new empty or_options_t. Used for testing. */ -or_options_t * -options_new(void) -{ - return config_new(&options_format); -} - -/** Set options to hold reasonable defaults for most options. - * Each option defaults to zero. */ -void -options_init(or_options_t *options) -{ - config_init(&options_format, options); -} - -/** Return a string containing a possible configuration file that would give - * the configuration in options. If minimal is true, do not - * include options that are the same as Tor's defaults. - */ -char * -options_dump(const or_options_t *options, int how_to_dump) -{ - const or_options_t *use_defaults; - int minimal; - switch (how_to_dump) { - case OPTIONS_DUMP_MINIMAL: - use_defaults = global_default_options; - minimal = 1; - break; - case OPTIONS_DUMP_DEFAULTS: - use_defaults = NULL; - minimal = 1; - break; - case OPTIONS_DUMP_ALL: - use_defaults = NULL; - minimal = 0; - break; - default: - log_warn(LD_BUG, "Bogus value for how_to_dump==%d", how_to_dump); - return NULL; - } - - return config_dump(&options_format, use_defaults, options, minimal, 0); -} - -/** Return 0 if every element of sl is a string holding a decimal - * representation of a port number, or if sl is NULL. - * Otherwise set *msg and return -1. */ -static int -validate_ports_csv(smartlist_t *sl, const char *name, char **msg) -{ - int i; - tor_assert(name); - - if (!sl) - return 0; - - SMARTLIST_FOREACH(sl, const char *, cp, - { - i = atoi(cp); - if (i < 1 || i > 65535) { - tor_asprintf(msg, "Port '%s' out of range in %s", cp, name); - return -1; - } - }); - return 0; -} - -/** If value exceeds ROUTER_MAX_DECLARED_BANDWIDTH, write - * a complaint into *msg using string desc, and return -1. - * Else return 0. - */ -static int -ensure_bandwidth_cap(uint64_t *value, const char *desc, char **msg) -{ - if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) { - /* This handles an understandable special case where somebody says "2gb" - * whereas our actual maximum is 2gb-1 (INT_MAX) */ - --*value; - } - if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) { - tor_asprintf(msg, "%s ("U64_FORMAT") must be at most %d", - desc, U64_PRINTF_ARG(*value), - ROUTER_MAX_DECLARED_BANDWIDTH); - return -1; - } - return 0; -} - -/** Parse an authority type from options-\>PublishServerDescriptor - * and write it to options-\>PublishServerDescriptor_. Treat "1" - * as "v3" unless BridgeRelay is 1, in which case treat it as "bridge". - * Treat "0" as "". - * Return 0 on success or -1 if not a recognized authority type (in which - * case the value of PublishServerDescriptor_ is undefined). */ -static int -compute_publishserverdescriptor(or_options_t *options) -{ - smartlist_t *list = options->PublishServerDescriptor; - dirinfo_type_t *auth = &options->PublishServerDescriptor_; - *auth = NO_DIRINFO; - if (!list) /* empty list, answer is none */ - return 0; - SMARTLIST_FOREACH_BEGIN(list, const char *, string) { - if (!strcasecmp(string, "v1")) - *auth |= V1_DIRINFO; - else if (!strcmp(string, "1")) - if (options->BridgeRelay) - *auth |= BRIDGE_DIRINFO; - else - *auth |= V3_DIRINFO; - else if (!strcasecmp(string, "v2")) - /* obsolete */; - else if (!strcasecmp(string, "v3")) - *auth |= V3_DIRINFO; - else if (!strcasecmp(string, "bridge")) - *auth |= BRIDGE_DIRINFO; - else if (!strcasecmp(string, "hidserv")) - log_warn(LD_CONFIG, - "PublishServerDescriptor hidserv is invalid. See " - "PublishHidServDescriptors."); - else if (!strcasecmp(string, "") || !strcmp(string, "0")) - /* no authority */; - else - return -1; - } SMARTLIST_FOREACH_END(string); - return 0; -} - -/** Lowest allowable value for RendPostPeriod; if this is too low, hidden - * services can overload the directory system. */ -#define MIN_REND_POST_PERIOD (10*60) - -/** Highest allowable value for RendPostPeriod. */ -#define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2) - -/** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor - * will generate too many circuits and potentially overload the network. */ -#define MIN_MAX_CIRCUIT_DIRTINESS 10 - -/** Highest allowable value for MaxCircuitDirtiness: prevents time_t - * overflows. */ -#define MAX_MAX_CIRCUIT_DIRTINESS (30*24*60*60) - -/** Lowest allowable value for CircuitStreamTimeout; if this is too low, Tor - * will generate too many circuits and potentially overload the network. */ -#define MIN_CIRCUIT_STREAM_TIMEOUT 10 - -/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might - * expose more information than we're comfortable with. */ -#define MIN_HEARTBEAT_PERIOD (30*60) - -/** Lowest recommended value for CircuitBuildTimeout; if it is set too low - * and LearnCircuitBuildTimeout is off, the failure rate for circuit - * construction may be very high. In that case, if it is set below this - * threshold emit a warning. - * */ -#define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10) - -static int -options_validate_cb(void *old_options, void *options, void *default_options, - int from_setconf, char **msg) -{ - return options_validate(old_options, options, default_options, - from_setconf, msg); -} - -/** Return 0 if every setting in options is reasonable, is a - * permissible transition from old_options, and none of the - * testing-only settings differ from default_options unless in - * testing mode. Else return -1. Should have no side effects, except for - * normalizing the contents of options. - * - * On error, tor_strdup an error explanation into *msg. - * - * XXX - * If from_setconf, we were called by the controller, and our - * Log line should stay empty. If it's 0, then give us a default log - * if there are no logs defined. - */ -STATIC int -options_validate(or_options_t *old_options, or_options_t *options, - or_options_t *default_options, int from_setconf, char **msg) -{ - int i; - config_line_t *cl; - const char *uname = get_uname(); - int n_ports=0; -#define REJECT(arg) \ - STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END -#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END - - tor_assert(msg); - *msg = NULL; - - if (server_mode(options) && - (!strcmpstart(uname, "Windows 95") || - !strcmpstart(uname, "Windows 98") || - !strcmpstart(uname, "Windows Me"))) { - log_warn(LD_CONFIG, "Tor is running as a server, but you are " - "running %s; this probably won't work. See " - "https://wiki.torproject.org/TheOnionRouter/TorFAQ#ServerOS " - "for details.", uname); - } - - if (parse_ports(options, 1, msg, &n_ports) < 0) - return -1; - - if (parse_outbound_addresses(options, 1, msg) < 0) - return -1; - - if (validate_data_directory(options)<0) - REJECT("Invalid DataDirectory"); - - if (options->Nickname == NULL) { - if (server_mode(options)) { - options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); - } - } else { - if (!is_legal_nickname(options->Nickname)) { - tor_asprintf(msg, - "Nickname '%s' is wrong length or contains illegal characters.", - options->Nickname); - return -1; - } - } - - if (server_mode(options) && !options->ContactInfo) - log_notice(LD_CONFIG, "Your ContactInfo config option is not set. " - "Please consider setting it, so we can contact you if your server is " - "misconfigured or something else goes wrong."); - - /* Special case on first boot if no Log options are given. */ - if (!options->Logs && !options->RunAsDaemon && !from_setconf) { - if (quiet_level == 0) - config_line_append(&options->Logs, "Log", "notice stdout"); - else if (quiet_level == 1) - config_line_append(&options->Logs, "Log", "warn stdout"); - } - - if (options_init_logs(options, 1)<0) /* Validate the tor_log(s) */ - REJECT("Failed to validate Log options. See logs for details."); - - if (authdir_mode(options)) { - /* confirm that our address isn't broken, so we can complain now */ - uint32_t tmp; - if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) - REJECT("Failed to resolve/guess local address. See logs for details."); - } - -#ifndef _WIN32 - if (options->RunAsDaemon && torrc_fname && path_is_relative(torrc_fname)) - REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); -#endif - - /* XXXX require that the only port not be DirPort? */ - /* XXXX require that at least one port be listened-upon. */ - if (n_ports == 0 && !options->RendConfigLines) - log_warn(LD_CONFIG, - "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all " - "undefined, and there aren't any hidden services configured. " - "Tor will still run, but probably won't do anything."); - - options->TransProxyType_parsed = TPT_DEFAULT; -#ifdef USE_TRANSPARENT - if (options->TransProxyType) { - if (!strcasecmp(options->TransProxyType, "default")) { - options->TransProxyType_parsed = TPT_DEFAULT; - } else if (!strcasecmp(options->TransProxyType, "tproxy")) { -#ifndef __linux__ - REJECT("TPROXY is a Linux-specific feature."); -#else - options->TransProxyType_parsed = TPT_TPROXY; -#endif - } else { - REJECT("Unrecognized value for TransProxyType"); - } - - if (strcasecmp(options->TransProxyType, "default") && - !options->TransPort_set) { - REJECT("Cannot use TransProxyType without any valid TransPort or " - "TransListenAddress."); - } - } -#else - if (options->TransPort_set) - REJECT("TransPort and TransListenAddress are disabled " - "in this build."); -#endif - - if (options->TokenBucketRefillInterval <= 0 - || options->TokenBucketRefillInterval > 1000) { - REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); - } - - if (options->ExcludeExitNodes || options->ExcludeNodes) { - options->ExcludeExitNodesUnion_ = routerset_new(); - routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeExitNodes); - routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeNodes); - } - - if (options->NodeFamilies) { - options->NodeFamilySets = smartlist_new(); - for (cl = options->NodeFamilies; cl; cl = cl->next) { - routerset_t *rs = routerset_new(); - if (routerset_parse(rs, cl->value, cl->key) == 0) { - smartlist_add(options->NodeFamilySets, rs); - } else { - routerset_free(rs); - } - } - } - - if (options->TLSECGroup && (strcasecmp(options->TLSECGroup, "P256") && - strcasecmp(options->TLSECGroup, "P224"))) { - COMPLAIN("Unrecognized TLSECGroup: Falling back to the default."); - tor_free(options->TLSECGroup); - } - - if (options->ExcludeNodes && options->StrictNodes) { - COMPLAIN("You have asked to exclude certain relays from all positions " - "in your circuits. Expect hidden services and other Tor " - "features to be broken in unpredictable ways."); - } - - if (options->AuthoritativeDir) { - if (!options->ContactInfo && !options->TestingTorNetwork) - REJECT("Authoritative directory servers must set ContactInfo"); - if (options->V1AuthoritativeDir && !options->RecommendedVersions) - REJECT("V1 authoritative dir servers must set RecommendedVersions."); - if (!options->RecommendedClientVersions) - options->RecommendedClientVersions = - config_lines_dup(options->RecommendedVersions); - if (!options->RecommendedServerVersions) - options->RecommendedServerVersions = - config_lines_dup(options->RecommendedVersions); - if (options->VersioningAuthoritativeDir && - (!options->RecommendedClientVersions || - !options->RecommendedServerVersions)) - REJECT("Versioning authoritative dir servers must set " - "Recommended*Versions."); - if (options->UseEntryGuards) { - log_info(LD_CONFIG, "Authoritative directory servers can't set " - "UseEntryGuards. Disabling."); - options->UseEntryGuards = 0; - } - if (!options->DownloadExtraInfo && authdir_mode_any_main(options)) { - log_info(LD_CONFIG, "Authoritative directories always try to download " - "extra-info documents. Setting DownloadExtraInfo."); - options->DownloadExtraInfo = 1; - } - if (!(options->BridgeAuthoritativeDir || options->HSAuthoritativeDir || - options->V1AuthoritativeDir || - options->V3AuthoritativeDir)) - REJECT("AuthoritativeDir is set, but none of " - "(Bridge/HS/V1/V3)AuthoritativeDir is set."); - /* If we have a v3bandwidthsfile and it's broken, complain on startup */ - if (options->V3BandwidthsFile && !old_options) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); - } - } - - if (options->AuthoritativeDir && !options->DirPort_set) - REJECT("Running as authoritative directory, but no DirPort set."); - - if (options->AuthoritativeDir && !options->ORPort_set) - REJECT("Running as authoritative directory, but no ORPort set."); - - if (options->AuthoritativeDir && options->ClientOnly) - REJECT("Running as authoritative directory, but ClientOnly also set."); - - if (options->FetchDirInfoExtraEarly && !options->FetchDirInfoEarly) - REJECT("FetchDirInfoExtraEarly requires that you also set " - "FetchDirInfoEarly"); - - if (options->HSAuthoritativeDir && proxy_mode(options)) - REJECT("Running as authoritative v0 HS directory, but also configured " - "as a client."); - - if (options->ConnLimit <= 0) { - tor_asprintf(msg, - "ConnLimit must be greater than 0, but was set to %d", - options->ConnLimit); - return -1; - } - - if (options->PathsNeededToBuildCircuits >= 0.0) { - if (options->PathsNeededToBuildCircuits < 0.25) { - log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too low. Increasing " - "to 0.25"); - options->PathsNeededToBuildCircuits = 0.25; - } else if (options->PathsNeededToBuildCircuits > 0.95) { - log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too high. Decreasing " - "to 0.95"); - options->PathsNeededToBuildCircuits = 0.95; - } - } - - if (options->MaxClientCircuitsPending <= 0 || - options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) { - tor_asprintf(msg, - "MaxClientCircuitsPending must be between 1 and %d, but " - "was set to %d", MAX_MAX_CLIENT_CIRCUITS_PENDING, - options->MaxClientCircuitsPending); - return -1; - } - - if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0) - return -1; - - if (validate_ports_csv(options->LongLivedPorts, "LongLivedPorts", msg) < 0) - return -1; - - if (validate_ports_csv(options->RejectPlaintextPorts, - "RejectPlaintextPorts", msg) < 0) - return -1; - - if (validate_ports_csv(options->WarnPlaintextPorts, - "WarnPlaintextPorts", msg) < 0) - return -1; - - if (options->FascistFirewall && !options->ReachableAddresses) { - if (options->FirewallPorts && smartlist_len(options->FirewallPorts)) { - /* We already have firewall ports set, so migrate them to - * ReachableAddresses, which will set ReachableORAddresses and - * ReachableDirAddresses if they aren't set explicitly. */ - smartlist_t *instead = smartlist_new(); - config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); - new_line->key = tor_strdup("ReachableAddresses"); - /* If we're configured with the old format, we need to prepend some - * open ports. */ - SMARTLIST_FOREACH(options->FirewallPorts, const char *, portno, - { - int p = atoi(portno); - if (p<0) continue; - smartlist_add_asprintf(instead, "*:%d", p); - }); - new_line->value = smartlist_join_strings(instead,",",0,NULL); - /* These have been deprecated since 0.1.1.5-alpha-cvs */ - log_notice(LD_CONFIG, - "Converting FascistFirewall and FirewallPorts " - "config options to new format: \"ReachableAddresses %s\"", - new_line->value); - options->ReachableAddresses = new_line; - SMARTLIST_FOREACH(instead, char *, cp, tor_free(cp)); - smartlist_free(instead); - } else { - /* We do not have FirewallPorts set, so add 80 to - * ReachableDirAddresses, and 443 to ReachableORAddresses. */ - if (!options->ReachableDirAddresses) { - config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); - new_line->key = tor_strdup("ReachableDirAddresses"); - new_line->value = tor_strdup("*:80"); - options->ReachableDirAddresses = new_line; - log_notice(LD_CONFIG, "Converting FascistFirewall config option " - "to new format: \"ReachableDirAddresses *:80\""); - } - if (!options->ReachableORAddresses) { - config_line_t *new_line = tor_malloc_zero(sizeof(config_line_t)); - new_line->key = tor_strdup("ReachableORAddresses"); - new_line->value = tor_strdup("*:443"); - options->ReachableORAddresses = new_line; - log_notice(LD_CONFIG, "Converting FascistFirewall config option " - "to new format: \"ReachableORAddresses *:443\""); - } - } - } - - for (i=0; i<3; i++) { - config_line_t **linep = - (i==0) ? &options->ReachableAddresses : - (i==1) ? &options->ReachableORAddresses : - &options->ReachableDirAddresses; - if (!*linep) - continue; - /* We need to end with a reject *:*, not an implicit accept *:* */ - for (;;) { - if (!strcmp((*linep)->value, "reject *:*")) /* already there */ - break; - linep = &((*linep)->next); - if (!*linep) { - *linep = tor_malloc_zero(sizeof(config_line_t)); - (*linep)->key = tor_strdup( - (i==0) ? "ReachableAddresses" : - (i==1) ? "ReachableORAddresses" : - "ReachableDirAddresses"); - (*linep)->value = tor_strdup("reject *:*"); - break; - } - } - } - - if ((options->ReachableAddresses || - options->ReachableORAddresses || - options->ReachableDirAddresses) && - server_mode(options)) - REJECT("Servers must be able to freely connect to the rest " - "of the Internet, so they must not set Reachable*Addresses " - "or FascistFirewall."); - - if (options->UseBridges && - server_mode(options)) - REJECT("Servers must be able to freely connect to the rest " - "of the Internet, so they must not set UseBridges."); - - /* If both of these are set, we'll end up with funny behavior where we - * demand enough entrynodes be up and running else we won't build - * circuits, yet we never actually use them. */ - if (options->UseBridges && options->EntryNodes) - REJECT("You cannot set both UseBridges and EntryNodes."); - - if (options->EntryNodes && !options->UseEntryGuards) { - REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); - } - - if (options->MaxMemInCellQueues < (500 << 20)) { - log_warn(LD_CONFIG, "MaxMemInCellQueues must be at least 500 MB for now. " - "Ideally, have it as large as you can afford."); - options->MaxMemInCellQueues = (500 << 20); - } - - options->AllowInvalid_ = 0; - - if (options->AllowInvalidNodes) { - SMARTLIST_FOREACH_BEGIN(options->AllowInvalidNodes, const char *, cp) { - if (!strcasecmp(cp, "entry")) - options->AllowInvalid_ |= ALLOW_INVALID_ENTRY; - else if (!strcasecmp(cp, "exit")) - options->AllowInvalid_ |= ALLOW_INVALID_EXIT; - else if (!strcasecmp(cp, "middle")) - options->AllowInvalid_ |= ALLOW_INVALID_MIDDLE; - else if (!strcasecmp(cp, "introduction")) - options->AllowInvalid_ |= ALLOW_INVALID_INTRODUCTION; - else if (!strcasecmp(cp, "rendezvous")) - options->AllowInvalid_ |= ALLOW_INVALID_RENDEZVOUS; - else { - tor_asprintf(msg, - "Unrecognized value '%s' in AllowInvalidNodes", cp); - return -1; - } - } SMARTLIST_FOREACH_END(cp); - } - - if (!options->SafeLogging || - !strcasecmp(options->SafeLogging, "0")) { - options->SafeLogging_ = SAFELOG_SCRUB_NONE; - } else if (!strcasecmp(options->SafeLogging, "relay")) { - options->SafeLogging_ = SAFELOG_SCRUB_RELAY; - } else if (!strcasecmp(options->SafeLogging, "1")) { - options->SafeLogging_ = SAFELOG_SCRUB_ALL; - } else { - tor_asprintf(msg, - "Unrecognized value '%s' in SafeLogging", - escaped(options->SafeLogging)); - return -1; - } - - if (compute_publishserverdescriptor(options) < 0) { - tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor"); - return -1; - } - - if ((options->BridgeRelay - || options->PublishServerDescriptor_ & BRIDGE_DIRINFO) - && (options->PublishServerDescriptor_ - & (V1_DIRINFO|V3_DIRINFO))) { - REJECT("Bridges are not supposed to publish router descriptors to the " - "directory authorities. Please correct your " - "PublishServerDescriptor line."); - } - - 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_lines); - options->DirPort_lines = NULL; - options->DirPort_set = 0; - } - - if (options->MinUptimeHidServDirectoryV2 < 0) { - log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at " - "least 0 seconds. Changing to 0."); - options->MinUptimeHidServDirectoryV2 = 0; - } - - if (options->RendPostPeriod < MIN_REND_POST_PERIOD) { - log_warn(LD_CONFIG, "RendPostPeriod option is too short; " - "raising to %d seconds.", MIN_REND_POST_PERIOD); - options->RendPostPeriod = MIN_REND_POST_PERIOD; - } - - if (options->RendPostPeriod > MAX_DIR_PERIOD) { - log_warn(LD_CONFIG, "RendPostPeriod is too large; clipping to %ds.", - MAX_DIR_PERIOD); - options->RendPostPeriod = MAX_DIR_PERIOD; - } - - if (options->Tor2webMode && options->LearnCircuitBuildTimeout) { - /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in - * two ways: - * - * - LearnCircuitBuildTimeout results in a low CBT, which - * Tor2webMode's use of one-hop rendezvous circuits lowers - * much further, producing *far* too many timeouts. - * - * - The adaptive CBT code does not update its timeout estimate - * using build times for single-hop circuits. - * - * If we fix both of these issues someday, we should test - * Tor2webMode with LearnCircuitBuildTimeout on again. */ - log_notice(LD_CONFIG,"Tor2webMode is enabled; turning " - "LearnCircuitBuildTimeout off."); - options->LearnCircuitBuildTimeout = 0; - } - - if (options->Tor2webMode && options->UseEntryGuards) { - /* tor2web mode clients do not (and should not) use entry guards - * in any meaningful way. Further, tor2web mode causes the hidden - * service client code to do things which break the path bias - * detector, and it's far easier to turn off entry guards (and - * thus the path bias detector with it) than to figure out how to - * make a piece of code which cannot possibly help tor2web mode - * users compatible with tor2web mode. - */ - log_notice(LD_CONFIG, - "Tor2WebMode is enabled; disabling UseEntryGuards."); - options->UseEntryGuards = 0; - } - - if (!(options->UseEntryGuards) && - (options->RendConfigLines != NULL)) { - log_warn(LD_CONFIG, - "UseEntryGuards is disabled, but you have configured one or more " - "hidden services on this Tor instance. Your hidden services " - "will be very easy to locate using a well-known attack -- see " - "http://freehaven.net/anonbib/#hs-attack06 for details."); - } - - if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && - options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { - log_warn(LD_CONFIG, - "CircuitBuildTimeout is shorter (%d seconds) than the recommended " - "minimum (%d seconds), and LearnCircuitBuildTimeout is disabled. " - "If tor isn't working, raise this value or enable " - "LearnCircuitBuildTimeout.", - options->CircuitBuildTimeout, - RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT ); - } else if (!options->LearnCircuitBuildTimeout && - !options->CircuitBuildTimeout) { - log_notice(LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but didn't " - "a CircuitBuildTimeout. I'll pick a plausible default."); - } - - if (options->PathBiasNoticeRate > 1.0) { - tor_asprintf(msg, - "PathBiasNoticeRate is too high. " - "It must be between 0 and 1.0"); - return -1; - } - if (options->PathBiasWarnRate > 1.0) { - tor_asprintf(msg, - "PathBiasWarnRate is too high. " - "It must be between 0 and 1.0"); - return -1; - } - if (options->PathBiasExtremeRate > 1.0) { - tor_asprintf(msg, - "PathBiasExtremeRate is too high. " - "It must be between 0 and 1.0"); - return -1; - } - if (options->PathBiasNoticeUseRate > 1.0) { - tor_asprintf(msg, - "PathBiasNoticeUseRate is too high. " - "It must be between 0 and 1.0"); - return -1; - } - if (options->PathBiasExtremeUseRate > 1.0) { - tor_asprintf(msg, - "PathBiasExtremeUseRate is too high. " - "It must be between 0 and 1.0"); - return -1; - } - - if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) { - log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; " - "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS); - options->MaxCircuitDirtiness = MIN_MAX_CIRCUIT_DIRTINESS; - } - - if (options->MaxCircuitDirtiness > MAX_MAX_CIRCUIT_DIRTINESS) { - log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too high; " - "setting to %d days.", MAX_MAX_CIRCUIT_DIRTINESS/86400); - options->MaxCircuitDirtiness = MAX_MAX_CIRCUIT_DIRTINESS; - } - - if (options->CircuitStreamTimeout && - options->CircuitStreamTimeout < MIN_CIRCUIT_STREAM_TIMEOUT) { - log_warn(LD_CONFIG, "CircuitStreamTimeout option is too short; " - "raising to %d seconds.", MIN_CIRCUIT_STREAM_TIMEOUT); - options->CircuitStreamTimeout = MIN_CIRCUIT_STREAM_TIMEOUT; - } - - if (options->HeartbeatPeriod && - options->HeartbeatPeriod < MIN_HEARTBEAT_PERIOD) { - log_warn(LD_CONFIG, "HeartbeatPeriod option is too short; " - "raising to %d seconds.", MIN_HEARTBEAT_PERIOD); - options->HeartbeatPeriod = MIN_HEARTBEAT_PERIOD; - } - - if (options->KeepalivePeriod < 1) - REJECT("KeepalivePeriod option must be positive."); - - if (ensure_bandwidth_cap(&options->BandwidthRate, - "BandwidthRate", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->BandwidthBurst, - "BandwidthBurst", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->MaxAdvertisedBandwidth, - "MaxAdvertisedBandwidth", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->RelayBandwidthRate, - "RelayBandwidthRate", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->RelayBandwidthBurst, - "RelayBandwidthBurst", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->PerConnBWRate, - "PerConnBWRate", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->PerConnBWBurst, - "PerConnBWBurst", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->AuthDirFastGuarantee, - "AuthDirFastGuarantee", msg) < 0) - return -1; - if (ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee, - "AuthDirGuardBWGuarantee", msg) < 0) - return -1; - - if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) - options->RelayBandwidthBurst = options->RelayBandwidthRate; - if (options->RelayBandwidthBurst && !options->RelayBandwidthRate) - options->RelayBandwidthRate = options->RelayBandwidthBurst; - - if (server_mode(options)) { - if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) { - tor_asprintf(msg, - "BandwidthRate is set to %d bytes/second. " - "For servers, it must be at least %d.", - (int)options->BandwidthRate, - ROUTER_REQUIRED_MIN_BANDWIDTH); - return -1; - } else if (options->MaxAdvertisedBandwidth < - ROUTER_REQUIRED_MIN_BANDWIDTH/2) { - tor_asprintf(msg, - "MaxAdvertisedBandwidth is set to %d bytes/second. " - "For servers, it must be at least %d.", - (int)options->MaxAdvertisedBandwidth, - ROUTER_REQUIRED_MIN_BANDWIDTH/2); - return -1; - } - if (options->RelayBandwidthRate && - options->RelayBandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) { - tor_asprintf(msg, - "RelayBandwidthRate is set to %d bytes/second. " - "For servers, it must be at least %d.", - (int)options->RelayBandwidthRate, - ROUTER_REQUIRED_MIN_BANDWIDTH); - return -1; - } - } - - if (options->RelayBandwidthRate > options->RelayBandwidthBurst) - REJECT("RelayBandwidthBurst must be at least equal " - "to RelayBandwidthRate."); - - if (options->BandwidthRate > options->BandwidthBurst) - REJECT("BandwidthBurst must be at least equal to BandwidthRate."); - - /* if they set relaybandwidth* really high but left bandwidth* - * at the default, raise the defaults. */ - if (options->RelayBandwidthRate > options->BandwidthRate) - options->BandwidthRate = options->RelayBandwidthRate; - if (options->RelayBandwidthBurst > options->BandwidthBurst) - options->BandwidthBurst = options->RelayBandwidthBurst; - - if (accounting_parse_options(options, 1)<0) - REJECT("Failed to parse accounting options. See logs for details."); - - if (options->AccountingMax) { - if (options->RendConfigLines && server_mode(options)) { - log_warn(LD_CONFIG, "Using accounting with a hidden service and an " - "ORPort is risky: your hidden service(s) and your public " - "address will all turn off at the same time, which may alert " - "observers that they are being run by the same party."); - } else if (config_count_key(options->RendConfigLines, - "HiddenServiceDir") > 1) { - log_warn(LD_CONFIG, "Using accounting with multiple hidden services is " - "risky: they will all turn off at the same time, which may " - "alert observers that they are being run by the same party."); - } - } - - if (options->HTTPProxy) { /* parse it now */ - if (tor_addr_port_lookup(options->HTTPProxy, - &options->HTTPProxyAddr, &options->HTTPProxyPort) < 0) - REJECT("HTTPProxy failed to parse or resolve. Please fix."); - if (options->HTTPProxyPort == 0) { /* give it a default */ - options->HTTPProxyPort = 80; - } - } - - if (options->HTTPProxyAuthenticator) { - if (strlen(options->HTTPProxyAuthenticator) >= 512) - REJECT("HTTPProxyAuthenticator is too long (>= 512 chars)."); - } - - if (options->HTTPSProxy) { /* parse it now */ - if (tor_addr_port_lookup(options->HTTPSProxy, - &options->HTTPSProxyAddr, &options->HTTPSProxyPort) <0) - REJECT("HTTPSProxy failed to parse or resolve. Please fix."); - if (options->HTTPSProxyPort == 0) { /* give it a default */ - options->HTTPSProxyPort = 443; - } - } - - if (options->HTTPSProxyAuthenticator) { - if (strlen(options->HTTPSProxyAuthenticator) >= 512) - REJECT("HTTPSProxyAuthenticator is too long (>= 512 chars)."); - } - - if (options->Socks4Proxy) { /* parse it now */ - if (tor_addr_port_lookup(options->Socks4Proxy, - &options->Socks4ProxyAddr, - &options->Socks4ProxyPort) <0) - REJECT("Socks4Proxy failed to parse or resolve. Please fix."); - if (options->Socks4ProxyPort == 0) { /* give it a default */ - options->Socks4ProxyPort = 1080; - } - } - - if (options->Socks5Proxy) { /* parse it now */ - if (tor_addr_port_lookup(options->Socks5Proxy, - &options->Socks5ProxyAddr, - &options->Socks5ProxyPort) <0) - REJECT("Socks5Proxy failed to parse or resolve. Please fix."); - if (options->Socks5ProxyPort == 0) { /* give it a default */ - options->Socks5ProxyPort = 1080; - } - } - - /* Check if more than one proxy type has been enabled. */ - if (!!options->Socks4Proxy + !!options->Socks5Proxy + - !!options->HTTPSProxy + !!options->ClientTransportPlugin > 1) - REJECT("You have configured more than one proxy type. " - "(Socks4Proxy|Socks5Proxy|HTTPSProxy|ClientTransportPlugin)"); - - /* Check if the proxies will give surprising behavior. */ - if (options->HTTPProxy && !(options->Socks4Proxy || - options->Socks5Proxy || - options->HTTPSProxy)) { - log_warn(LD_CONFIG, "HTTPProxy configured, but no SOCKS proxy or " - "HTTPS proxy configured. Watch out: this configuration will " - "proxy unencrypted directory connections only."); - } - - if (options->Socks5ProxyUsername) { - size_t len; - - len = strlen(options->Socks5ProxyUsername); - if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) - REJECT("Socks5ProxyUsername must be between 1 and 255 characters."); - - if (!options->Socks5ProxyPassword) - REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); - - len = strlen(options->Socks5ProxyPassword); - if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) - REJECT("Socks5ProxyPassword must be between 1 and 255 characters."); - } else if (options->Socks5ProxyPassword) - REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); - - if (options->HashedControlPassword) { - smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword); - if (!sl) { - REJECT("Bad HashedControlPassword: wrong length or bad encoding"); - } else { - SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp)); - smartlist_free(sl); - } - } - - if (options->HashedControlSessionPassword) { - smartlist_t *sl = decode_hashed_passwords( - options->HashedControlSessionPassword); - if (!sl) { - REJECT("Bad HashedControlSessionPassword: wrong length or bad encoding"); - } else { - SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp)); - smartlist_free(sl); - } - } - - if (options->OwningControllerProcess) { - const char *validate_pspec_msg = NULL; - if (tor_validate_process_specifier(options->OwningControllerProcess, - &validate_pspec_msg)) { - tor_asprintf(msg, "Bad OwningControllerProcess: %s", - validate_pspec_msg); - return -1; - } - } - - if (options->ControlPort_set && !options->HashedControlPassword && - !options->HashedControlSessionPassword && - !options->CookieAuthentication) { - log_warn(LD_CONFIG, "ControlPort is open, but no authentication method " - "has been configured. This means that any program on your " - "computer can reconfigure your Tor. That's bad! You should " - "upgrade your Tor controller as soon as possible."); - } - - if (options->CookieAuthFileGroupReadable && !options->CookieAuthFile) { - log_warn(LD_CONFIG, "CookieAuthFileGroupReadable is set, but will have " - "no effect: you must specify an explicit CookieAuthFile to " - "have it group-readable."); - } - - if (options->UseEntryGuards && ! options->NumEntryGuards) - REJECT("Cannot enable UseEntryGuards with NumEntryGuards set to 0"); - - if (options->MyFamily && options->BridgeRelay) { - log_warn(LD_CONFIG, "Listing a family for a bridge relay is not " - "supported: it can reveal bridge fingerprints to censors. " - "You should also make sure you aren't listing this bridge's " - "fingerprint in any other MyFamily."); - } - if (check_nickname_list(&options->MyFamily, "MyFamily", msg)) - return -1; - for (cl = options->NodeFamilies; cl; cl = cl->next) { - routerset_t *rs = routerset_new(); - if (routerset_parse(rs, cl->value, cl->key)) { - routerset_free(rs); - return -1; - } - routerset_free(rs); - } - - if (validate_addr_policies(options, msg) < 0) - return -1; - - if (validate_dir_servers(options, old_options) < 0) - REJECT("Directory authority/fallback line did not parse. See logs " - "for details."); - - if (options->UseBridges && !options->Bridges) - REJECT("If you set UseBridges, you must specify at least one bridge."); - if (options->UseBridges && !options->TunnelDirConns) - REJECT("If you set UseBridges, you must set TunnelDirConns."); - - for (cl = options->Bridges; cl; cl = cl->next) { - bridge_line_t *bridge_line = parse_bridge_line(cl->value); - if (!bridge_line) - REJECT("Bridge line did not parse. See logs for details."); - bridge_line_free(bridge_line); - } - - for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { - if (parse_client_transport_line(cl->value, 1)<0) - REJECT("Transport line did not parse. See logs for details."); - } - - for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { - if (parse_server_transport_line(cl->value, 1)<0) - REJECT("Server transport line did not parse. See logs for details."); - } - - if (options->ServerTransportPlugin && !server_mode(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.", - escaped(options->ServerTransportPlugin->value)); - } - - for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { - /** If get_bindaddr_from_transport_listen_line() fails with - 'transport' being NULL, it means that something went wrong - while parsing the ServerTransportListenAddr line. */ - char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL); - if (!bindaddr) - REJECT("ServerTransportListenAddr did not parse. See logs for details."); - tor_free(bindaddr); - } - - if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) { - log_notice(LD_GENERAL, "You need at least a single managed-proxy to " - "specify a transport listen address. The " - "ServerTransportListenAddr line will be ignored."); - } - - for (cl = options->ServerTransportOptions; cl; cl = cl->next) { - /** If get_options_from_transport_options_line() fails with - 'transport' being NULL, it means that something went wrong - while parsing the ServerTransportOptions line. */ - smartlist_t *options_sl = - get_options_from_transport_options_line(cl->value, NULL); - if (!options_sl) - REJECT("ServerTransportOptions did not parse. See logs for details."); - - SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp)); - smartlist_free(options_sl); - } - - /* If we are a bridge with a pluggable transport proxy but no - Extended ORPort, inform the user that she is missing out. */ - if (server_mode(options) && options->ServerTransportPlugin && - !options->ExtORPort_lines) { - log_notice(LD_CONFIG, "We are a bridge with a pluggable transport " - "proxy but the Extended ORPort is disabled. The " - "Extended ORPort helps Tor communicate with the pluggable " - "transport proxy. Please enable it using the ExtORPort " - "torrc option."); - } - - if (options->ConstrainedSockets) { - /* If the user wants to constrain socket buffer use, make sure the desired - * limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */ - if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER || - options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER || - options->ConstrainedSockSize % 1024) { - tor_asprintf(msg, - "ConstrainedSockSize is invalid. Must be a value between %d and %d " - "in 1024 byte increments.", - MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER); - return -1; - } - 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 " - "serving directory entries via DirPort. It is strongly " - "suggested that you disable serving directory requests when " - "system TCP buffer resources are scarce."); - } - } - - if (options->V3AuthVoteDelay + options->V3AuthDistDelay >= - options->V3AuthVotingInterval/2) { - REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than half " - "V3AuthVotingInterval"); - } - if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS) - REJECT("V3AuthVoteDelay is way too low."); - if (options->V3AuthDistDelay < MIN_DIST_SECONDS) - REJECT("V3AuthDistDelay is way too low."); - - if (options->V3AuthNIntervalsValid < 2) - REJECT("V3AuthNIntervalsValid must be at least 2."); - - if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL) { - REJECT("V3AuthVotingInterval is insanely low."); - } else if (options->V3AuthVotingInterval > 24*60*60) { - REJECT("V3AuthVotingInterval is insanely high."); - } else if (((24*60*60) % options->V3AuthVotingInterval) != 0) { - COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours."); - } - - if (rend_config_services(options, 1) < 0) - REJECT("Failed to configure rendezvous options. See logs for details."); - - /* Parse client-side authorization for hidden services. */ - if (rend_parse_service_authorization(options, 1) < 0) - REJECT("Failed to configure client authorization for hidden services. " - "See logs for details."); - - if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv4, - AF_INET, 1, msg)<0) - return -1; - if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv6, - AF_INET6, 1, msg)<0) - return -1; - - if (options->PreferTunneledDirConns && !options->TunnelDirConns) - REJECT("Must set TunnelDirConns if PreferTunneledDirConns is set."); - - if ((options->Socks4Proxy || options->Socks5Proxy) && - !options->HTTPProxy && !options->PreferTunneledDirConns) - REJECT("When Socks4Proxy or Socks5Proxy is configured, " - "PreferTunneledDirConns and TunnelDirConns must both be " - "set to 1, or HTTPProxy must be configured."); - - if (options->AutomapHostsSuffixes) { - SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf, - { - size_t len = strlen(suf); - if (len && suf[len-1] == '.') - suf[len-1] = '\0'; - }); - } - - if (options->TestingTorNetwork && - !(options->DirAuthorities || - (options->AlternateDirAuthority && - options->AlternateBridgeAuthority))) { - REJECT("TestingTorNetwork may only be configured in combination with " - "a non-default set of DirAuthority or both of " - "AlternateDirAuthority and AlternateBridgeAuthority configured."); - } - - if (options->AllowSingleHopExits && !options->DirAuthorities) { - COMPLAIN("You have set AllowSingleHopExits; now your relay will allow " - "others to make one-hop exits. However, since by default most " - "clients avoid relays that set this option, most clients will " - "ignore you."); - } - -#define CHECK_DEFAULT(arg) \ - STMT_BEGIN \ - if (!options->TestingTorNetwork && \ - !options->UsingTestNetworkDefaults_ && \ - !config_is_same(&options_format,options, \ - default_options,#arg)) { \ - REJECT(#arg " may only be changed in testing Tor " \ - "networks!"); \ - } STMT_END - CHECK_DEFAULT(TestingV3AuthInitialVotingInterval); - CHECK_DEFAULT(TestingV3AuthInitialVoteDelay); - CHECK_DEFAULT(TestingV3AuthInitialDistDelay); - CHECK_DEFAULT(TestingV3AuthVotingStartOffset); - CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability); - CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime); - CHECK_DEFAULT(TestingServerDownloadSchedule); - CHECK_DEFAULT(TestingClientDownloadSchedule); - CHECK_DEFAULT(TestingServerConsensusDownloadSchedule); - CHECK_DEFAULT(TestingClientConsensusDownloadSchedule); - CHECK_DEFAULT(TestingBridgeDownloadSchedule); - CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); - CHECK_DEFAULT(TestingDirConnectionMaxStall); - CHECK_DEFAULT(TestingConsensusMaxDownloadTries); - CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); - CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); - CHECK_DEFAULT(TestingCertMaxDownloadTries); -#undef CHECK_DEFAULT - - if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) { - REJECT("TestingV3AuthInitialVotingInterval is insanely low."); - } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) { - REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into " - "30 minutes."); - } - - if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) { - REJECT("TestingV3AuthInitialVoteDelay is way too low."); - } - - if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) { - REJECT("TestingV3AuthInitialDistDelay is way too low."); - } - - if (options->TestingV3AuthInitialVoteDelay + - options->TestingV3AuthInitialDistDelay >= - options->TestingV3AuthInitialVotingInterval/2) { - REJECT("TestingV3AuthInitialVoteDelay plus TestingV3AuthInitialDistDelay " - "must be less than half TestingV3AuthInitialVotingInterval"); - } - - if (options->TestingV3AuthVotingStartOffset > - MIN(options->TestingV3AuthInitialVotingInterval, - options->V3AuthVotingInterval)) { - REJECT("TestingV3AuthVotingStartOffset is higher than the voting " - "interval."); - } - - if (options->TestingAuthDirTimeToLearnReachability < 0) { - REJECT("TestingAuthDirTimeToLearnReachability must be non-negative."); - } else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { - COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); - } - - if (options->TestingEstimatedDescriptorPropagationTime < 0) { - REJECT("TestingEstimatedDescriptorPropagationTime must be non-negative."); - } else if (options->TestingEstimatedDescriptorPropagationTime > 60*60) { - COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high."); - } - - if (options->TestingClientMaxIntervalWithoutRequest < 1) { - REJECT("TestingClientMaxIntervalWithoutRequest is way too low."); - } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) { - COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high."); - } - - if (options->TestingDirConnectionMaxStall < 5) { - REJECT("TestingDirConnectionMaxStall is way too low."); - } else if (options->TestingDirConnectionMaxStall > 3600) { - COMPLAIN("TestingDirConnectionMaxStall is insanely high."); - } - - if (options->TestingConsensusMaxDownloadTries < 2) { - REJECT("TestingConsensusMaxDownloadTries must be greater than 1."); - } else if (options->TestingConsensusMaxDownloadTries > 800) { - COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); - } - - if (options->TestingDescriptorMaxDownloadTries < 2) { - REJECT("TestingDescriptorMaxDownloadTries must be greater than 1."); - } else if (options->TestingDescriptorMaxDownloadTries > 800) { - COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high."); - } - - if (options->TestingMicrodescMaxDownloadTries < 2) { - REJECT("TestingMicrodescMaxDownloadTries must be greater than 1."); - } else if (options->TestingMicrodescMaxDownloadTries > 800) { - COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high."); - } - - if (options->TestingCertMaxDownloadTries < 2) { - REJECT("TestingCertMaxDownloadTries must be greater than 1."); - } else if (options->TestingCertMaxDownloadTries > 800) { - COMPLAIN("TestingCertMaxDownloadTries is insanely high."); - } - - if (options->TestingEnableConnBwEvent && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingEnableConnBwEvent may only be changed in testing " - "Tor networks!"); - } - - if (options->TestingEnableCellStatsEvent && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingEnableCellStatsEvent may only be changed in testing " - "Tor networks!"); - } - - if (options->TestingEnableTbEmptyEvent && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingEnableTbEmptyEvent may only be changed in testing " - "Tor networks!"); - } - - if (options->TestingTorNetwork) { - log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node " - "almost unusable in the public Tor network, and is " - "therefore only advised if you are building a " - "testing Tor network!"); - } - - if (options->AccelName && !options->HardwareAccel) - options->HardwareAccel = 1; - if (options->AccelDir && !options->AccelName) - REJECT("Can't use hardware crypto accelerator dir without engine name."); - - if (options->PublishServerDescriptor) - SMARTLIST_FOREACH(options->PublishServerDescriptor, const char *, pubdes, { - if (!strcmp(pubdes, "1") || !strcmp(pubdes, "0")) - if (smartlist_len(options->PublishServerDescriptor) > 1) { - COMPLAIN("You have passed a list of multiple arguments to the " - "PublishServerDescriptor option that includes 0 or 1. " - "0 or 1 should only be used as the sole argument. " - "This configuration will be rejected in a future release."); - break; - } - }); - - if (options->BridgeRelay == 1 && ! options->ORPort_set) - REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " - "combination."); - - return 0; -#undef REJECT -#undef COMPLAIN -} - -/** Helper: return true iff s1 and s2 are both NULL, or both non-NULL - * equal strings. */ -static int -opt_streq(const char *s1, const char *s2) -{ - return 0 == strcmp_opt(s1, s2); -} - -/** Check if any of the previous options have changed but aren't allowed to. */ -static int -options_transition_allowed(const or_options_t *old, - const or_options_t *new_val, - char **msg) -{ - if (!old) - return 0; - - if (!opt_streq(old->PidFile, new_val->PidFile)) { - *msg = tor_strdup("PidFile is not allowed to change."); - return -1; - } - - if (old->RunAsDaemon != new_val->RunAsDaemon) { - *msg = tor_strdup("While Tor is running, changing RunAsDaemon " - "is not allowed."); - return -1; - } - - if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) { - tor_asprintf(msg, - "While Tor is running, changing DataDirectory " - "(\"%s\"->\"%s\") is not allowed.", - old->DataDirectory, new_val->DataDirectory); - return -1; - } - - if (!opt_streq(old->User, new_val->User)) { - *msg = tor_strdup("While Tor is running, changing User is not allowed."); - return -1; - } - - if ((old->HardwareAccel != new_val->HardwareAccel) - || !opt_streq(old->AccelName, new_val->AccelName) - || !opt_streq(old->AccelDir, new_val->AccelDir)) { - *msg = tor_strdup("While Tor is running, changing OpenSSL hardware " - "acceleration engine is not allowed."); - return -1; - } - - if (old->TestingTorNetwork != new_val->TestingTorNetwork) { - *msg = tor_strdup("While Tor is running, changing TestingTorNetwork " - "is not allowed."); - return -1; - } - - if (old->DisableAllSwap != new_val->DisableAllSwap) { - *msg = tor_strdup("While Tor is running, changing DisableAllSwap " - "is not allowed."); - return -1; - } - - if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) { - *msg = tor_strdup("While Tor is running, changing TokenBucketRefill" - "Interval is not allowed"); - return -1; - } - - if (old->DisableIOCP != new_val->DisableIOCP) { - *msg = tor_strdup("While Tor is running, changing DisableIOCP " - "is not allowed."); - return -1; - } - - if (old->DisableDebuggerAttachment && - !new_val->DisableDebuggerAttachment) { - *msg = tor_strdup("While Tor is running, disabling " - "DisableDebuggerAttachment is not allowed."); - return -1; - } - - return 0; -} - -/** Return 1 if any change from old_options to new_options - * will require us to rotate the CPU and DNS workers; else return 0. */ -static int -options_transition_affects_workers(const or_options_t *old_options, - const or_options_t *new_options) -{ - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - old_options->NumCPUs != new_options->NumCPUs || - !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) || - old_options->ServerDNSSearchDomains != - new_options->ServerDNSSearchDomains || - old_options->SafeLogging_ != new_options->SafeLogging_ || - old_options->ClientOnly != new_options->ClientOnly || - public_server_mode(old_options) != public_server_mode(new_options) || - !config_lines_eq(old_options->Logs, new_options->Logs) || - old_options->LogMessageDomains != new_options->LogMessageDomains) - return 1; - - /* Check whether log options match. */ - - /* Nothing that changed matters. */ - return 0; -} - -/** Return 1 if any change from old_options to new_options - * will require us to generate a new descriptor; else return 0. */ -static int -options_transition_affects_descriptor(const or_options_t *old_options, - const or_options_t *new_options) -{ - /* XXX We can be smarter here. If your DirPort isn't being - * published and you just turned it off, no need to republish. Etc. */ - if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || - !opt_streq(old_options->Nickname,new_options->Nickname) || - !opt_streq(old_options->Address,new_options->Address) || - !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || - old_options->ExitPolicyRejectPrivate != - new_options->ExitPolicyRejectPrivate || - old_options->IPv6Exit != new_options->IPv6Exit || - !config_lines_eq(old_options->ORPort_lines, - new_options->ORPort_lines) || - !config_lines_eq(old_options->DirPort_lines, - new_options->DirPort_lines) || - old_options->ClientOnly != new_options->ClientOnly || - old_options->DisableNetwork != new_options->DisableNetwork || - old_options->PublishServerDescriptor_ != - new_options->PublishServerDescriptor_ || - get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || - get_effective_bwburst(old_options) != - get_effective_bwburst(new_options) || - !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || - !opt_streq(old_options->MyFamily, new_options->MyFamily) || - !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || - old_options->AccountingMax != new_options->AccountingMax || - public_server_mode(old_options) != public_server_mode(new_options)) - return 1; - - return 0; -} - -#ifdef _WIN32 -/** Return the directory on windows where we expect to find our application - * data. */ -static char * -get_windows_conf_root(void) -{ - static int is_set = 0; - static char path[MAX_PATH*2+1]; - TCHAR tpath[MAX_PATH] = {0}; - - LPITEMIDLIST idl; - IMalloc *m; - HRESULT result; - - if (is_set) - return path; - - /* Find X:\documents and settings\username\application data\ . - * We would use SHGetSpecialFolder path, but that wasn't added until IE4. - */ -#ifdef ENABLE_LOCAL_APPDATA -#define APPDATA_PATH CSIDL_LOCAL_APPDATA -#else -#define APPDATA_PATH CSIDL_APPDATA -#endif - if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, APPDATA_PATH, &idl))) { - getcwd(path,MAX_PATH); - is_set = 1; - log_warn(LD_CONFIG, - "I couldn't find your application data folder: are you " - "running an ancient version of Windows 95? Defaulting to \"%s\"", - path); - return path; - } - /* Convert the path from an "ID List" (whatever that is!) to a path. */ - result = SHGetPathFromIDList(idl, tpath); -#ifdef UNICODE - wcstombs(path,tpath,sizeof(path)); - path[sizeof(path)-1] = '\0'; -#else - strlcpy(path,tpath,sizeof(path)); -#endif - - /* Now we need to free the memory that the path-idl was stored in. In - * typical Windows fashion, we can't just call 'free()' on it. */ - SHGetMalloc(&m); - if (m) { - m->lpVtbl->Free(m, idl); - m->lpVtbl->Release(m); - } - if (!SUCCEEDED(result)) { - return NULL; - } - strlcat(path,"\\tor",MAX_PATH); - is_set = 1; - return path; -} -#endif - -/** Return the default location for our torrc file (if defaults_file is - * false), or for the torrc-defaults file (if defaults_file is true). */ -static const char * -get_default_conf_file(int defaults_file) -{ -#ifdef _WIN32 - if (defaults_file) { - static char defaults_path[MAX_PATH+1]; - tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults", - get_windows_conf_root()); - return defaults_path; - } else { - static char path[MAX_PATH+1]; - tor_snprintf(path, MAX_PATH, "%s\\torrc", - get_windows_conf_root()); - return path; - } -#else - return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; -#endif -} - -/** Verify whether lst is a string containing valid-looking comma-separated - * nicknames, or NULL. Will normalise lst to prefix '$' to any nickname - * or fingerprint that needs it. Return 0 on success. - * Warn and return -1 on failure. - */ -static int -check_nickname_list(char **lst, const char *name, char **msg) -{ - int r = 0; - smartlist_t *sl; - int changes = 0; - - if (!*lst) - return 0; - sl = smartlist_new(); - - smartlist_split_string(sl, *lst, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); - - SMARTLIST_FOREACH_BEGIN(sl, char *, s) - { - if (!is_legal_nickname_or_hexdigest(s)) { - // check if first char is dollar - if (s[0] != '$') { - // Try again but with a dollar symbol prepended - char *prepended; - tor_asprintf(&prepended, "$%s", s); - - if (is_legal_nickname_or_hexdigest(prepended)) { - // The nickname is valid when it's prepended, swap the current - // version with a prepended one - tor_free(s); - SMARTLIST_REPLACE_CURRENT(sl, s, prepended); - changes = 1; - continue; - } - - // Still not valid, free and fallback to error message - tor_free(prepended); - } - - tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); - r = -1; - break; - } - } - SMARTLIST_FOREACH_END(s); - - // Replace the caller's nickname list with a fixed one - if (changes && r == 0) { - char *newNicknames = smartlist_join_strings(sl, ", ", 0, NULL); - tor_free(*lst); - *lst = newNicknames; - } - - SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); - smartlist_free(sl); - - return r; -} - -/** Learn config file name from command line arguments, or use the default. - * - * If defaults_file is true, we're looking for torrc-defaults; - * otherwise, we're looking for the regular torrc_file. - * - * Set *using_default_fname to true if we're using the default - * configuration file name; or false if we've set it from the command line. - * - * Set *ignore_missing_torrc to true if we should ignore the resulting - * filename if it doesn't exist. - */ -static char * -find_torrc_filename(config_line_t *cmd_arg, - int defaults_file, - int *using_default_fname, int *ignore_missing_torrc) -{ - char *fname=NULL; - config_line_t *p_index; - const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; - const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; - - if (defaults_file) - *ignore_missing_torrc = 1; - - for (p_index = cmd_arg; p_index; p_index = p_index->next) { - if (!strcmp(p_index->key, fname_opt)) { - if (fname) { - log_warn(LD_CONFIG, "Duplicate %s options on command line.", - fname_opt); - tor_free(fname); - } - fname = expand_filename(p_index->value); - - { - char *absfname; - absfname = make_path_absolute(fname); - tor_free(fname); - fname = absfname; - } - - *using_default_fname = 0; - } else if (ignore_opt && !strcmp(p_index->key,ignore_opt)) { - *ignore_missing_torrc = 1; - } - } - - if (*using_default_fname) { - /* didn't find one, try CONFDIR */ - const char *dflt = get_default_conf_file(defaults_file); - if (dflt && file_status(dflt) == FN_FILE) { - fname = tor_strdup(dflt); - } else { -#ifndef _WIN32 - char *fn = NULL; - if (!defaults_file) - fn = expand_filename("~/.torrc"); - if (fn && file_status(fn) == FN_FILE) { - fname = fn; - } else { - tor_free(fn); - fname = tor_strdup(dflt); - } -#else - fname = tor_strdup(dflt); -#endif - } - } - return fname; -} - -/** Load a configuration file from disk, setting torrc_fname or - * torrc_defaults_fname if successful. - * - * If defaults_file is true, load torrc-defaults; otherwise load torrc. - * - * Return the contents of the file on success, and NULL on failure. - */ -static char * -load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) -{ - char *fname=NULL; - char *cf = NULL; - int using_default_torrc = 1; - int ignore_missing_torrc = 0; - char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; - - fname = find_torrc_filename(cmd_arg, defaults_file, - &using_default_torrc, &ignore_missing_torrc); - tor_assert(fname); - log_debug(LD_CONFIG, "Opening config file \"%s\"", fname); - - tor_free(*fname_var); - *fname_var = fname; - - /* Open config file */ - if (file_status(fname) != FN_FILE || - !(cf = read_file_to_str(fname,0,NULL))) { - if (using_default_torrc == 1 || ignore_missing_torrc) { - if (!defaults_file) - log_notice(LD_CONFIG, "Configuration file \"%s\" not present, " - "using reasonable defaults.", fname); - tor_free(fname); /* sets fname to NULL */ - *fname_var = NULL; - cf = tor_strdup(""); - } else { - log_warn(LD_CONFIG, - "Unable to open configuration file \"%s\".", fname); - goto err; - } - } else { - log_notice(LD_CONFIG, "Read configuration file \"%s\".", fname); - } - - return cf; - err: - tor_free(fname); - *fname_var = NULL; - return NULL; -} - -/** Read a configuration file into options, finding the configuration - * file location based on the command line. After loading the file - * call options_init_from_string() to load the config. - * Return 0 if success, -1 if failure. */ -int -options_init_from_torrc(int argc, char **argv) -{ - char *cf=NULL, *cf_defaults=NULL; - int command; - int retval = -1; - char *command_arg = NULL; - char *errmsg=NULL; - config_line_t *p_index = NULL; - config_line_t *cmdline_only_options = NULL; - - /* Go through command-line variables */ - if (! have_parsed_cmdline) { - /* Or we could redo the list every time we pass this place. - * It does not really matter */ - if (config_parse_commandline(argc, argv, 0, &global_cmdline_options, - &global_cmdline_only_options) < 0) { - goto err; - } - have_parsed_cmdline = 1; - } - cmdline_only_options = global_cmdline_only_options; - - if (config_line_find(cmdline_only_options, "-h") || - config_line_find(cmdline_only_options, "--help")) { - print_usage(); - exit(0); - } - if (config_line_find(cmdline_only_options, "--list-torrc-options")) { - /* For documenting validating whether we've documented everything. */ - list_torrc_options(); - exit(0); - } - - if (config_line_find(cmdline_only_options, "--version")) { - printf("Tor version %s.\n",get_version()); - exit(0); - } - - if (config_line_find(cmdline_only_options, "--digests")) { - printf("Tor version %s.\n",get_version()); - printf("%s", libor_get_digests()); - printf("%s", tor_get_digests()); - exit(0); - } - - if (config_line_find(cmdline_only_options, "--library-versions")) { - printf("Tor version %s. \n", get_version()); - printf("Library versions\tCompiled\t\tRuntime\n"); - printf("Libevent\t\t%-15s\t\t%s\n", - tor_libevent_get_header_version_str(), - tor_libevent_get_version_str()); - printf("OpenSSL \t\t%-15s\t\t%s\n", - crypto_openssl_get_header_version_str(), - crypto_openssl_get_version_str()); - printf("Zlib \t\t%-15s\t\t%s\n", - tor_zlib_get_header_version_str(), - tor_zlib_get_version_str()); - //TODO: Hex versions? - exit(0); - } - - command = CMD_RUN_TOR; - for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { - if (!strcmp(p_index->key,"--list-fingerprint")) { - command = CMD_LIST_FINGERPRINT; - } else if (!strcmp(p_index->key, "--hash-password")) { - command = CMD_HASH_PASSWORD; - command_arg = p_index->value; - } else if (!strcmp(p_index->key, "--dump-config")) { - command = CMD_DUMP_CONFIG; - command_arg = p_index->value; - } else if (!strcmp(p_index->key, "--verify-config")) { - command = CMD_VERIFY_CONFIG; - } - } - - if (command == CMD_HASH_PASSWORD) { - cf_defaults = tor_strdup(""); - cf = tor_strdup(""); - } else { - cf_defaults = load_torrc_from_disk(cmdline_only_options, 1); - cf = load_torrc_from_disk(cmdline_only_options, 0); - if (!cf) { - if (config_line_find(cmdline_only_options, "--allow-missing-torrc")) { - cf = tor_strdup(""); - } else { - goto err; - } - } - } - - retval = options_init_from_string(cf_defaults, cf, command, command_arg, - &errmsg); - - err: - - tor_free(cf); - tor_free(cf_defaults); - if (errmsg) { - log_warn(LD_CONFIG,"%s", errmsg); - tor_free(errmsg); - } - return retval < 0 ? -1 : 0; -} - -/** Load the options from the configuration in cf, validate - * them for consistency and take actions based on them. - * - * Return 0 if success, negative on error: - * * -1 for general errors. - * * -2 for failure to parse/validate, - * * -3 for transition not allowed - * * -4 for error while setting the new options - */ -setopt_err_t -options_init_from_string(const char *cf_defaults, const char *cf, - int command, const char *command_arg, - char **msg) -{ - or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; - config_line_t *cl; - int retval, i; - setopt_err_t err = SETOPT_ERR_MISC; - tor_assert(msg); - - oldoptions = global_options; /* get_options unfortunately asserts if - this is the first time we run*/ - - newoptions = tor_malloc_zero(sizeof(or_options_t)); - newoptions->magic_ = OR_OPTIONS_MAGIC; - options_init(newoptions); - newoptions->command = command; - newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; - - for (i = 0; i < 2; ++i) { - const char *body = i==0 ? cf_defaults : cf; - if (!body) - continue; - /* get config lines, assign them */ - retval = config_get_lines(body, &cl, 1); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - if (i==0) - newdefaultoptions = config_dup(&options_format, newoptions); - } - - if (newdefaultoptions == NULL) { - newdefaultoptions = config_dup(&options_format, global_default_options); - } - - /* Go through command-line variables too */ - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - - /* If this is a testing network configuration, change defaults - * for a list of dependent config options, re-initialize newoptions - * with the new defaults, and assign all options to it second time. */ - if (newoptions->TestingTorNetwork) { - /* XXXX this is a bit of a kludge. perhaps there's a better way to do - * this? We could, for example, make the parsing algorithm do two passes - * over the configuration. If it finds any "suite" options like - * TestingTorNetwork, it could change the defaults before its second pass. - * Not urgent so long as this seems to work, but at any sign of trouble, - * let's clean it up. -NM */ - - /* Change defaults. */ - int i; - for (i = 0; testing_tor_network_defaults[i].name; ++i) { - const config_var_t *new_var = &testing_tor_network_defaults[i]; - config_var_t *old_var = - config_find_option_mutable(&options_format, new_var->name); - tor_assert(new_var); - tor_assert(old_var); - old_var->initvalue = new_var->initvalue; - } - - /* Clear newoptions and re-initialize them with new defaults. */ - config_free(&options_format, newoptions); - config_free(&options_format, newdefaultoptions); - newdefaultoptions = NULL; - newoptions = tor_malloc_zero(sizeof(or_options_t)); - newoptions->magic_ = OR_OPTIONS_MAGIC; - options_init(newoptions); - newoptions->command = command; - newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; - - /* Assign all options a second time. */ - for (i = 0; i < 2; ++i) { - const char *body = i==0 ? cf_defaults : cf; - if (!body) - continue; - /* get config lines, assign them */ - retval = config_get_lines(body, &cl, 1); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - if (i==0) - newdefaultoptions = config_dup(&options_format, newoptions); - } - /* Assign command-line variables a second time too */ - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - } - - /* Validate newoptions */ - if (options_validate(oldoptions, newoptions, newdefaultoptions, - 0, msg) < 0) { - err = SETOPT_ERR_PARSE; /*XXX make this a separate return value.*/ - goto err; - } - - if (options_transition_allowed(oldoptions, newoptions, msg) < 0) { - err = SETOPT_ERR_TRANSITION; - goto err; - } - - if (set_options(newoptions, msg)) { - err = SETOPT_ERR_SETTING; - goto err; /* frees and replaces old options */ - } - config_free(&options_format, global_default_options); - global_default_options = newdefaultoptions; - - return SETOPT_OK; - - err: - config_free(&options_format, newoptions); - config_free(&options_format, newdefaultoptions); - if (*msg) { - char *old_msg = *msg; - tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); - tor_free(old_msg); - } - return err; -} - -/** Return the location for our configuration file. - */ -const char * -get_torrc_fname(int defaults_fname) -{ - const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; - - if (fname) - return fname; - else - return get_default_conf_file(defaults_fname); -} - -/** Adjust the address map based on the MapAddress elements in the - * configuration options - */ -void -config_register_addressmaps(const or_options_t *options) -{ - smartlist_t *elts; - config_line_t *opt; - const char *from, *to, *msg; - - addressmap_clear_configured(); - elts = smartlist_new(); - for (opt = options->AddressMap; opt; opt = opt->next) { - smartlist_split_string(elts, opt->value, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); - if (smartlist_len(elts) < 2) { - log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.", - opt->value); - goto cleanup; - } - - from = smartlist_get(elts,0); - to = smartlist_get(elts,1); - - if (to[0] == '.' || from[0] == '.') { - log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a" - "'.'. Ignoring.",opt->value); - goto cleanup; - } - - 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 to. */ -/* 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; - from_wildcard = 1; - } - if (!strncmp(to,"*.",2)) { - to += 2; - to_wildcard = 1; - } - - if (to_wildcard && !from_wildcard) { - *msg = "can only use wildcard (i.e. '*.') if 'from' address " - "uses wildcard also"; - return -1; - } - - if (address_is_invalid_destination(to, 1)) { - *msg = "destination is invalid"; - return -1; - } - - addressmap_register(from, tor_strdup(to), expires, addrmap_source, - from_wildcard, to_wildcard); - } - return 0; -} - -/** - * Initialize the logs based on the configuration file. - */ -static int -options_init_logs(or_options_t *options, int validate_only) -{ - config_line_t *opt; - int ok; - smartlist_t *elts; - int daemon = -#ifdef _WIN32 - 0; -#else - options->RunAsDaemon; -#endif - - if (options->LogTimeGranularity <= 0) { - log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.", - options->LogTimeGranularity); - return -1; - } else if (1000 % options->LogTimeGranularity != 0 && - options->LogTimeGranularity % 1000 != 0) { - int granularity = options->LogTimeGranularity; - if (granularity < 40) { - do granularity++; - while (1000 % granularity != 0); - } else if (granularity < 1000) { - granularity = 1000 / granularity; - while (1000 % granularity != 0) - granularity--; - granularity = 1000 / granularity; - } else { - granularity = 1000 * ((granularity / 1000) + 1); - } - log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a " - "divisor or a multiple of 1 second. Changing to " - "'%d'.", - options->LogTimeGranularity, granularity); - if (!validate_only) - set_log_time_granularity(granularity); - } else { - if (!validate_only) - set_log_time_granularity(options->LogTimeGranularity); - } - - ok = 1; - elts = smartlist_new(); - - for (opt = options->Logs; opt; opt = opt->next) { - log_severity_list_t *severity; - const char *cfg = opt->value; - severity = tor_malloc_zero(sizeof(log_severity_list_t)); - if (parse_log_severity_config(&cfg, severity) < 0) { - log_warn(LD_CONFIG, "Couldn't parse log levels in Log option 'Log %s'", - opt->value); - ok = 0; goto cleanup; - } - - smartlist_split_string(elts, cfg, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); - - if (smartlist_len(elts) == 0) - smartlist_add(elts, tor_strdup("stdout")); - - if (smartlist_len(elts) == 1 && - (!strcasecmp(smartlist_get(elts,0), "stdout") || - !strcasecmp(smartlist_get(elts,0), "stderr"))) { - int err = smartlist_len(elts) && - !strcasecmp(smartlist_get(elts,0), "stderr"); - if (!validate_only) { - if (daemon) { - log_warn(LD_CONFIG, - "Can't log to %s with RunAsDaemon set; skipping stdout", - err?"stderr":"stdout"); - } else { - add_stream_log(severity, err?"":"", - fileno(err?stderr:stdout)); - } - } - goto cleanup; - } - if (smartlist_len(elts) == 1 && - !strcasecmp(smartlist_get(elts,0), "syslog")) { -#ifdef HAVE_SYSLOG_H - if (!validate_only) { - add_syslog_log(severity); - } -#else - log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); -#endif - goto cleanup; - } - - if (smartlist_len(elts) == 2 && - !strcasecmp(smartlist_get(elts,0), "file")) { - if (!validate_only) { - char *fname = expand_filename(smartlist_get(elts, 1)); - if (add_file_log(severity, fname) < 0) { - log_warn(LD_CONFIG, "Couldn't open file for 'Log %s': %s", - opt->value, strerror(errno)); - ok = 0; - } - tor_free(fname); - } - goto cleanup; - } - - log_warn(LD_CONFIG, "Bad syntax on file Log option 'Log %s'", - opt->value); - ok = 0; goto cleanup; - - cleanup: - SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); - smartlist_clear(elts); - tor_free(severity); - } - smartlist_free(elts); - - if (ok && !validate_only) - logs_set_domain_logging(options->LogMessageDomains); - - return ok?0:-1; -} - -/** Given a smartlist of SOCKS arguments to be passed to a transport - * proxy in args, validate them and return -1 if they are - * corrupted. Return 0 if they seem OK. */ -static int -validate_transport_socks_arguments(const smartlist_t *args) -{ - char *socks_string = NULL; - size_t socks_string_len; - - tor_assert(args); - tor_assert(smartlist_len(args) > 0); - - SMARTLIST_FOREACH_BEGIN(args, const char *, s) { - if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */ - log_warn(LD_CONFIG, "'%s' is not a k=v item.", s); - return -1; - } - } SMARTLIST_FOREACH_END(s); - - socks_string = pt_stringify_socks_args(args); - if (!socks_string) - return -1; - - socks_string_len = strlen(socks_string); - tor_free(socks_string); - - if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) { - log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).", - MAX_SOCKS5_AUTH_SIZE_TOTAL, - (unsigned long) socks_string_len); - return -1; - } - - return 0; -} - -/** Deallocate a bridge_line_t structure. */ -/* private */ void -bridge_line_free(bridge_line_t *bridge_line) -{ - if (!bridge_line) - return; - - if (bridge_line->socks_args) { - SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s)); - smartlist_free(bridge_line->socks_args); - } - tor_free(bridge_line->transport_name); - tor_free(bridge_line); -} - -/** Read the contents of a Bridge line from line. Return 0 - * if the line is well-formed, and -1 if it isn't. If - * validate_only is 0, and the line is well-formed, then add - * the bridge described in the line to our internal bridge list. - * - * Bridge line format: - * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ... - */ -/* private */ bridge_line_t * -parse_bridge_line(const char *line) -{ - smartlist_t *items = NULL; - char *addrport=NULL, *fingerprint=NULL; - char *field=NULL; - bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t)); - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - if (smartlist_len(items) < 1) { - log_warn(LD_CONFIG, "Too few arguments to Bridge line."); - goto err; - } - - /* first field is either a transport name or addrport */ - field = smartlist_get(items, 0); - smartlist_del_keeporder(items, 0); - - if (string_is_C_identifier(field)) { - /* It's a transport name. */ - bridge_line->transport_name = field; - if (smartlist_len(items) < 1) { - log_warn(LD_CONFIG, "Too few items to Bridge line."); - goto err; - } - addrport = smartlist_get(items, 0); /* Next field is addrport then. */ - smartlist_del_keeporder(items, 0); - } else { - addrport = field; - } - - /* Parse addrport. */ - if (tor_addr_port_lookup(addrport, - &bridge_line->addr, &bridge_line->port)<0) { - log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport); - goto err; - } - if (!bridge_line->port) { - log_info(LD_CONFIG, - "Bridge address '%s' has no port; using default port 443.", - addrport); - bridge_line->port = 443; - } - - /* If transports are enabled, next field could be a fingerprint or a - socks argument. If transports are disabled, next field must be - a fingerprint. */ - if (smartlist_len(items)) { - if (bridge_line->transport_name) { /* transports enabled: */ - field = smartlist_get(items, 0); - smartlist_del_keeporder(items, 0); - - /* If it's a key=value pair, then it's a SOCKS argument for the - transport proxy... */ - if (string_is_key_value(LOG_DEBUG, field)) { - bridge_line->socks_args = smartlist_new(); - smartlist_add(bridge_line->socks_args, field); - } else { /* ...otherwise, it's the bridge fingerprint. */ - fingerprint = field; - } - - } else { /* transports disabled: */ - fingerprint = smartlist_join_strings(items, "", 0, NULL); - } - } - - /* Handle fingerprint, if it was provided. */ - if (fingerprint) { - if (strlen(fingerprint) != HEX_DIGEST_LEN) { - log_warn(LD_CONFIG, "Key digest for Bridge is wrong length."); - goto err; - } - if (base16_decode(bridge_line->digest, DIGEST_LEN, - fingerprint, HEX_DIGEST_LEN)<0) { - log_warn(LD_CONFIG, "Unable to decode Bridge key digest."); - goto err; - } - } - - /* If we are using transports, any remaining items in the smartlist - should be k=v values. */ - if (bridge_line->transport_name && smartlist_len(items)) { - if (!bridge_line->socks_args) - bridge_line->socks_args = smartlist_new(); - - /* append remaining items of 'items' to 'socks_args' */ - smartlist_add_all(bridge_line->socks_args, items); - smartlist_clear(items); - - tor_assert(smartlist_len(bridge_line->socks_args) > 0); - } - - if (bridge_line->socks_args) { - if (validate_transport_socks_arguments(bridge_line->socks_args) < 0) - goto err; - } - - goto done; - - err: - bridge_line_free(bridge_line); - bridge_line = NULL; - - done: - SMARTLIST_FOREACH(items, char*, s, tor_free(s)); - smartlist_free(items); - tor_free(addrport); - tor_free(fingerprint); - - return bridge_line; -} - -/** Read the contents of a ClientTransportPlugin line from - * line. Return 0 if the line is well-formed, and -1 if it - * isn't. - * - * If validate_only is 0, the line is well-formed, and the - * transport is needed by some bridge: - * - If it's an external proxy line, add the transport described in the line to - * our internal transport list. - * - If it's a managed proxy line, launch the managed proxy. */ -static int -parse_client_transport_line(const char *line, int validate_only) -{ - smartlist_t *items = NULL; - int r; - char *field2=NULL; - - const char *transports=NULL; - smartlist_t *transport_list=NULL; - char *addrport=NULL; - tor_addr_t addr; - uint16_t port = 0; - int socks_ver=PROXY_NONE; - - /* managed proxy options */ - int is_managed=0; - char **proxy_argv=NULL; - char **tmp=NULL; - int proxy_argc, i; - int is_useless_proxy=1; - - int line_length; - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - - line_length = smartlist_len(items); - if (line_length < 3) { - log_warn(LD_CONFIG, "Too few arguments on ClientTransportPlugin line."); - goto err; - } - - /* Get the first line element, split it to commas into - transport_list (in case it's multiple transports) and validate - the transport names. */ - transports = smartlist_get(items, 0); - transport_list = smartlist_new(); - smartlist_split_string(transport_list, transports, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) { - /* validate transport names */ - if (!string_is_C_identifier(transport_name)) { - log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", - transport_name); - goto err; - } - - /* see if we actually need the transports provided by this proxy */ - if (!validate_only && transport_is_needed(transport_name)) - is_useless_proxy = 0; - } SMARTLIST_FOREACH_END(transport_name); - - /* field2 is either a SOCKS version or "exec" */ - field2 = smartlist_get(items, 1); - - if (!strcmp(field2,"socks4")) { - socks_ver = PROXY_SOCKS4; - } else if (!strcmp(field2,"socks5")) { - socks_ver = PROXY_SOCKS5; - } else if (!strcmp(field2,"exec")) { - is_managed=1; - } else { - log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.", - field2); - goto err; - } - - if (is_managed) { /* managed */ - if (!validate_only && is_useless_proxy) { - log_warn(LD_GENERAL, "Pluggable transport proxy (%s) does not provide " - "any needed transports and will not be launched.", line); - } - - /* If we are not just validating, use the rest of the line as the - argv of the proxy to be launched. Also, make sure that we are - only launching proxies that contribute useful transports. */ - if (!validate_only && !is_useless_proxy) { - proxy_argc = line_length-2; - tor_assert(proxy_argc > 0); - proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1)); - tmp = proxy_argv; - for (i=0;iline, return its - * string. Return NULL if the line was not - * well-formed. - * - * If transport is set, return NULL if the line is not - * referring to transport. - * - * The returned string is allocated on the heap and it's the - * responsibility of the caller to free it. */ -static char * -get_bindaddr_from_transport_listen_line(const char *line,const char *transport) -{ - smartlist_t *items = NULL; - const char *parsed_transport = NULL; - char *addrport = NULL; - tor_addr_t addr; - uint16_t port = 0; - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - - if (smartlist_len(items) < 2) { - log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line."); - goto err; - } - - parsed_transport = smartlist_get(items, 0); - addrport = tor_strdup(smartlist_get(items, 1)); - - /* If 'transport' is given, check if it matches the one on the line */ - if (transport && strcmp(transport, parsed_transport)) - goto err; - - /* Validate addrport */ - if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port)<0) { - log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr " - "address '%s'", addrport); - goto err; - } - - goto done; - - err: - tor_free(addrport); - addrport = NULL; - - done: - SMARTLIST_FOREACH(items, char*, s, tor_free(s)); - smartlist_free(items); - - return addrport; -} - -/** Given a ServerTransportOptions line, return a smartlist - * with the options. Return NULL if the line was not well-formed. - * - * If transport is set, return NULL if the line is not - * referring to transport. - * - * The returned smartlist and its strings are allocated on the heap - * and it's the responsibility of the caller to free it. */ -smartlist_t * -get_options_from_transport_options_line(const char *line,const char *transport) -{ - smartlist_t *items = smartlist_new(); - smartlist_t *options = smartlist_new(); - const char *parsed_transport = NULL; - - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - - if (smartlist_len(items) < 2) { - log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line."); - goto err; - } - - parsed_transport = smartlist_get(items, 0); - /* If 'transport' is given, check if it matches the one on the line */ - if (transport && strcmp(transport, parsed_transport)) - goto err; - - SMARTLIST_FOREACH_BEGIN(items, const char *, option) { - if (option_sl_idx == 0) /* skip the transport field (first field)*/ - continue; - - /* validate that it's a k=v value */ - if (!string_is_key_value(LOG_WARN, option)) { - log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option)); - goto err; - } - - /* add it to the options smartlist */ - smartlist_add(options, tor_strdup(option)); - log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); - } SMARTLIST_FOREACH_END(option); - - goto done; - - err: - SMARTLIST_FOREACH(options, char*, s, tor_free(s)); - smartlist_free(options); - options = NULL; - - done: - SMARTLIST_FOREACH(items, char*, s, tor_free(s)); - smartlist_free(items); - - return options; -} - -/** Given the name of a pluggable transport in transport, check - * the configuration file to see if the user has explicitly asked for - * it to listen on a specific port. Return a string if - * so, otherwise NULL. */ -char * -get_transport_bindaddr_from_config(const char *transport) -{ - config_line_t *cl; - const or_options_t *options = get_options(); - - for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) { - char *bindaddr = - get_bindaddr_from_transport_listen_line(cl->value, transport); - if (bindaddr) - return bindaddr; - } - - return NULL; -} - -/** Given the name of a pluggable transport in transport, check - * the configuration file to see if the user has asked us to pass any - * parameters to the pluggable transport. Return a smartlist - * containing the parameters, otherwise NULL. */ -smartlist_t * -get_options_for_server_transport(const char *transport) -{ - config_line_t *cl; - const or_options_t *options = get_options(); - - for (cl = options->ServerTransportOptions; cl; cl = cl->next) { - smartlist_t *options_sl = - get_options_from_transport_options_line(cl->value, transport); - if (options_sl) - return options_sl; - } - - return NULL; -} - -/** Read the contents of a ServerTransportPlugin line from - * line. Return 0 if the line is well-formed, and -1 if it - * isn't. - * If validate_only is 0, the line is well-formed, and it's a - * managed proxy line, launch the managed proxy. */ -static int -parse_server_transport_line(const char *line, int validate_only) -{ - smartlist_t *items = NULL; - int r; - const char *transports=NULL; - smartlist_t *transport_list=NULL; - char *type=NULL; - char *addrport=NULL; - tor_addr_t addr; - uint16_t port = 0; - - /* managed proxy options */ - int is_managed=0; - char **proxy_argv=NULL; - char **tmp=NULL; - int proxy_argc,i; - - int line_length; - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - - line_length = smartlist_len(items); - if (line_length < 3) { - log_warn(LD_CONFIG, "Too few arguments on ServerTransportPlugin line."); - goto err; - } - - /* Get the first line element, split it to commas into - transport_list (in case it's multiple transports) and validate - the transport names. */ - transports = smartlist_get(items, 0); - transport_list = smartlist_new(); - smartlist_split_string(transport_list, transports, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) { - if (!string_is_C_identifier(transport_name)) { - log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).", - transport_name); - goto err; - } - } SMARTLIST_FOREACH_END(transport_name); - - type = smartlist_get(items, 1); - - if (!strcmp(type, "exec")) { - is_managed=1; - } else if (!strcmp(type, "proxy")) { - is_managed=0; - } else { - log_warn(LD_CONFIG, "Strange ServerTransportPlugin type '%s'", type); - goto err; - } - - if (is_managed) { /* managed */ - if (!validate_only) { - proxy_argc = line_length-2; - tor_assert(proxy_argc > 0); - proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1)); - tmp = proxy_argv; - - for (i=0;iline. If - * validate_only is 0, and the line is well-formed, and it - * shares any bits with required_type or required_type - * is 0, then add the dirserver described in the line (minus whatever - * bits it's missing) as a valid authority. Return 0 on success, - * or -1 if the line isn't well-formed or if we can't add it. */ -static int -parse_dir_authority_line(const char *line, dirinfo_type_t required_type, - int validate_only) -{ - smartlist_t *items = NULL; - int r; - char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL; - uint16_t dir_port = 0, or_port = 0; - char digest[DIGEST_LEN]; - char v3_digest[DIGEST_LEN]; - dirinfo_type_t type = 0; - int is_not_hidserv_authority = 0; - double weight = 1.0; - - items = smartlist_new(); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - if (smartlist_len(items) < 1) { - log_warn(LD_CONFIG, "No arguments on DirAuthority line."); - goto err; - } - - if (is_legal_nickname(smartlist_get(items, 0))) { - nickname = smartlist_get(items, 0); - smartlist_del_keeporder(items, 0); - } - - while (smartlist_len(items)) { - char *flag = smartlist_get(items, 0); - if (TOR_ISDIGIT(flag[0])) - break; - if (!strcasecmp(flag, "v1")) { - type |= (V1_DIRINFO | HIDSERV_DIRINFO); - } else if (!strcasecmp(flag, "hs")) { - type |= HIDSERV_DIRINFO; - } else if (!strcasecmp(flag, "no-hs")) { - is_not_hidserv_authority = 1; - } else if (!strcasecmp(flag, "bridge")) { - type |= BRIDGE_DIRINFO; - } else if (!strcasecmp(flag, "no-v2")) { - /* obsolete, but may still be contained in DirAuthority lines generated - by various tools */; - } else if (!strcasecmpstart(flag, "orport=")) { - int ok; - char *portstring = flag + strlen("orport="); - or_port = (uint16_t) tor_parse_long(portstring, 10, 1, 65535, &ok, NULL); - if (!ok) - log_warn(LD_CONFIG, "Invalid orport '%s' on DirAuthority line.", - portstring); - } else if (!strcmpstart(flag, "weight=")) { - int ok; - const char *wstring = flag + strlen("weight="); - weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Invalid weight '%s' on DirAuthority line.",flag); - weight=1.0; - } - } else if (!strcasecmpstart(flag, "v3ident=")) { - char *idstr = flag + strlen("v3ident="); - if (strlen(idstr) != HEX_DIGEST_LEN || - base16_decode(v3_digest, DIGEST_LEN, idstr, HEX_DIGEST_LEN)<0) { - log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirAuthority line", - flag); - } else { - type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO; - } - } else { - log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line", - flag); - } - tor_free(flag); - smartlist_del_keeporder(items, 0); - } - if (is_not_hidserv_authority) - type &= ~HIDSERV_DIRINFO; - - if (smartlist_len(items) < 2) { - log_warn(LD_CONFIG, "Too few arguments to DirAuthority line."); - goto err; - } - addrport = smartlist_get(items, 0); - smartlist_del_keeporder(items, 0); - if (addr_port_lookup(LOG_WARN, addrport, &address, NULL, &dir_port)<0) { - log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s'", addrport); - goto err; - } - if (!dir_port) { - log_warn(LD_CONFIG, "Missing port in DirAuthority address '%s'",addrport); - goto err; - } - - fingerprint = smartlist_join_strings(items, "", 0, NULL); - if (strlen(fingerprint) != HEX_DIGEST_LEN) { - log_warn(LD_CONFIG, "Key digest '%s' for DirAuthority is wrong length %d.", - fingerprint, (int)strlen(fingerprint)); - goto err; - } - if (!strcmp(fingerprint, "E623F7625FBE0C87820F11EC5F6D5377ED816294")) { - /* a known bad fingerprint. refuse to use it. We can remove this - * clause once Tor 0.1.2.17 is obsolete. */ - log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " - "torrc file (%s), or reinstall Tor and use the default torrc.", - get_torrc_fname(0)); - goto err; - } - if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { - log_warn(LD_CONFIG, "Unable to decode DirAuthority key digest."); - goto err; - } - - if (!validate_only && (!required_type || required_type & type)) { - dir_server_t *ds; - if (required_type) - type &= required_type; /* pare down what we think of them as an - * authority for. */ - log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type, - address, (int)dir_port, (char*)smartlist_get(items,0)); - if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port, - digest, v3_digest, type, weight))) - goto err; - dir_server_add(ds); - } - - r = 0; - goto done; - - err: - r = -1; - - done: - SMARTLIST_FOREACH(items, char*, s, tor_free(s)); - smartlist_free(items); - tor_free(addrport); - tor_free(address); - tor_free(nickname); - tor_free(fingerprint); - return r; -} - -/** Read the contents of a FallbackDir line from line. If - * validate_only is 0, and the line is well-formed, then add the - * dirserver described in the line as a fallback directory. Return 0 on - * success, or -1 if the line isn't well-formed or if we can't add it. */ -static int -parse_dir_fallback_line(const char *line, - int validate_only) -{ - int r = -1; - smartlist_t *items = smartlist_new(), *positional = smartlist_new(); - int orport = -1; - uint16_t dirport; - tor_addr_t addr; - int ok; - char id[DIGEST_LEN]; - char *address=NULL; - double weight=1.0; - - memset(id, 0, sizeof(id)); - smartlist_split_string(items, line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - SMARTLIST_FOREACH_BEGIN(items, const char *, cp) { - const char *eq = strchr(cp, '='); - ok = 1; - if (! eq) { - smartlist_add(positional, (char*)cp); - continue; - } - if (!strcmpstart(cp, "orport=")) { - orport = (int)tor_parse_long(cp+strlen("orport="), 10, - 1, 65535, &ok, NULL); - } else if (!strcmpstart(cp, "id=")) { - ok = !base16_decode(id, DIGEST_LEN, - cp+strlen("id="), strlen(cp)-strlen("id=")); - } else if (!strcmpstart(cp, "weight=")) { - int ok; - const char *wstring = cp + strlen("weight="); - weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Invalid weight '%s' on FallbackDir line.", cp); - weight=1.0; - } - } - - if (!ok) { - log_warn(LD_CONFIG, "Bad FallbackDir option %s", escaped(cp)); - goto end; - } - } SMARTLIST_FOREACH_END(cp); - - if (smartlist_len(positional) != 1) { - log_warn(LD_CONFIG, "Couldn't parse FallbackDir line %s", escaped(line)); - goto end; - } - - if (tor_digest_is_zero(id)) { - log_warn(LD_CONFIG, "Missing identity on FallbackDir line"); - goto end; - } - - if (orport <= 0) { - log_warn(LD_CONFIG, "Missing orport on FallbackDir line"); - goto end; - } - - if (tor_addr_port_split(LOG_INFO, smartlist_get(positional, 0), - &address, &dirport) < 0 || - tor_addr_parse(&addr, address)<0) { - log_warn(LD_CONFIG, "Couldn't parse address:port %s on FallbackDir line", - (const char*)smartlist_get(positional, 0)); - goto end; - } - - if (!validate_only) { - dir_server_t *ds; - ds = fallback_dir_server_new(&addr, dirport, orport, id, weight); - if (!ds) { - log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line)); - goto end; - } - dir_server_add(ds); - } - - r = 0; - - end: - SMARTLIST_FOREACH(items, char *, cp, tor_free(cp)); - smartlist_free(items); - smartlist_free(positional); - tor_free(address); - return r; -} - -/** Allocate and return a new port_cfg_t with reasonable defaults. */ -static port_cfg_t * -port_cfg_new(void) -{ - port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); - cfg->ipv4_traffic = 1; - cfg->cache_ipv4_answers = 1; - cfg->prefer_ipv6_virtaddr = 1; - return cfg; -} - -/** Free all storage held in port */ -static void -port_cfg_free(port_cfg_t *port) -{ - tor_free(port); -} - -/** Warn for every port in ports of type listener_type that is - * on a publicly routable address. */ -static void -warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname, - int listener_type) -{ - SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { - if (port->type != listener_type) - continue; - if (port->is_unix_addr) { - /* Unix sockets aren't accessible over a network. */ - } else if (!tor_addr_is_internal(&port->addr, 1)) { - log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. " - "Other people on the Internet might find your computer and " - "use it as an open proxy. Please don't allow this unless you " - "have a good reason.", - fmt_addrport(&port->addr, port->port), portname); - } else if (!tor_addr_is_loopback(&port->addr)) { - log_notice(LD_CONFIG, "You configured a non-loopback address '%s' " - "for %sPort. This allows everybody on your local network to " - "use your machine as a proxy. Make sure this is what you " - "wanted.", - fmt_addrport(&port->addr, port->port), portname); - } - } SMARTLIST_FOREACH_END(port); -} - -/** Warn for every Extended ORPort port in ports that is on a - * publicly routable address. */ -static void -warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) -{ - SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { - if (port->type != CONN_TYPE_EXT_OR_LISTENER) - continue; - if (port->is_unix_addr) - continue; - /* XXX maybe warn even if address is RFC1918? */ - if (!tor_addr_is_internal(&port->addr, 1)) { - log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. " - "This is not advised; this address is supposed to only be " - "exposed on localhost so that your pluggable transport " - "proxies can connect to it.", - fmt_addrport(&port->addr, port->port), portname); - } - } SMARTLIST_FOREACH_END(port); -} - -/** Given a list of port_cfg_t in ports, warn any controller port there - * is listening on any non-loopback address. If forbid is true, - * then emit a stronger warning and remove the port from the list. - */ -static void -warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) -{ - int warned = 0; - SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { - if (port->type != CONN_TYPE_CONTROL_LISTENER) - continue; - if (port->is_unix_addr) - continue; - if (!tor_addr_is_loopback(&port->addr)) { - if (forbid) { - if (!warned) - log_warn(LD_CONFIG, - "You have a ControlPort set to accept " - "unauthenticated connections from a non-local address. " - "This means that programs not running on your computer " - "can reconfigure your Tor, without even having to guess a " - "password. That's so bad that I'm closing your ControlPort " - "for you. If you need to control your Tor remotely, try " - "enabling authentication and using a tool like stunnel or " - "ssh to encrypt remote access."); - warned = 1; - port_cfg_free(port); - SMARTLIST_DEL_CURRENT(ports, port); - } else { - log_warn(LD_CONFIG, "You have a ControlPort set to accept " - "connections from a non-local address. This means that " - "programs not running on your computer can reconfigure your " - "Tor. That's pretty bad, since the controller " - "protocol isn't encrypted! Maybe you should just listen on " - "127.0.0.1 and use a tool like stunnel or ssh to encrypt " - "remote connections to your control port."); - return; /* No point in checking the rest */ - } - } - } SMARTLIST_FOREACH_END(port); -} - -#define CL_PORT_NO_OPTIONS (1u<<0) -#define CL_PORT_WARN_NONLOCAL (1u<<1) -#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) -#define CL_PORT_SERVER_OPTIONS (1u<<3) -#define CL_PORT_FORBID_NONLOCAL (1u<<4) -#define CL_PORT_TAKES_HOSTNAMES (1u<<5) - -/** - * Parse port configuration for a single port type. - * - * Read entries of the "FooPort" type from the list ports, and - * entries of the "FooListenAddress" type from the list - * listenaddrs. Two syntaxes are supported: a legacy syntax - * where FooPort is at most a single entry containing a port number and - * where FooListenAddress has any number of address:port combinations; - * and a new syntax where there are no FooListenAddress entries and - * where FooPort can have any number of entries of the format - * "[Address:][Port] IsolationOptions". - * - * In log messages, describe the port type as portname. - * - * If no address is specified, default to defaultaddr. If no - * FooPort is given, default to defaultport (if 0, there is no default). - * - * If CL_PORT_NO_OPTIONS is set in flags, do not allow stream - * isolation options in the FooPort entries. - * - * If CL_PORT_WARN_NONLOCAL is set in flags, warn if any of the - * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, - * this is a contrl port with no password set: don't even allow it. - * - * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in flags, warn - * if FooListenAddress is set but FooPort is 0. - * - * If CL_PORT_SERVER_OPTIONS is set in flags, do not allow stream - * isolation options in the FooPort entries; instead allow the - * server-port option set. - * - * If CL_PORT_TAKES_HOSTNAMES is set in flags, allow the options - * {No,}IPv{4,6}Traffic. - * - * On success, if out is given, add a new port_cfg_t entry to - * out for every port that the client should listen on. Return 0 - * on success, -1 on failure. - */ -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) -{ - smartlist_t *elts; - int retval = -1; - const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); - const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER); - const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS; - const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; - const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; - const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; - const unsigned allow_spurious_listenaddr = - flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; - const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES; - int got_zero_port=0, got_nonzero_port=0; - - /* FooListenAddress is deprecated; let's make it work like it used to work, - * though. */ - if (listenaddrs) { - int mainport = defaultport; - - if (ports && ports->next) { - log_warn(LD_CONFIG, "%sListenAddress can't be used when there are " - "multiple %sPort lines", portname, portname); - return -1; - } else if (ports) { - if (!strcmp(ports->value, "auto")) { - mainport = CFG_AUTO_PORT; - } else { - int ok; - mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "%sListenAddress can only be used with a single " - "%sPort with value \"auto\" or 1-65535 and no options set.", - portname, portname); - return -1; - } - } - } - - if (mainport == 0) { - if (allow_spurious_listenaddr) - return 1; /*DOCDOC*/ - log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used", - portname, portname); - return -1; - } - - if (use_server_options && out) { - /* Add a no_listen port. */ - port_cfg_t *cfg = port_cfg_new(); - cfg->type = listener_type; - cfg->port = mainport; - tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ - cfg->no_listen = 1; - cfg->bind_ipv4_only = 1; - cfg->ipv4_traffic = 1; - cfg->prefer_ipv6_virtaddr = 1; - smartlist_add(out, cfg); - } - - for (; listenaddrs; listenaddrs = listenaddrs->next) { - tor_addr_t addr; - uint16_t port = 0; - if (tor_addr_port_lookup(listenaddrs->value, &addr, &port) < 0) { - log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'", - portname, listenaddrs->value); - return -1; - } - if (out) { - port_cfg_t *cfg = port_cfg_new(); - cfg->type = listener_type; - cfg->port = port ? port : mainport; - tor_addr_copy(&cfg->addr, &addr); - cfg->session_group = SESSION_GROUP_UNSET; - cfg->isolation_flags = ISO_DEFAULT; - cfg->no_advertise = 1; - smartlist_add(out, cfg); - } - } - - if (warn_nonlocal && out) { - if (is_control) - warn_nonlocal_controller_ports(out, forbid_nonlocal); - else if (is_ext_orport) - warn_nonlocal_ext_orports(out, portname); - else - warn_nonlocal_client_ports(out, portname, listener_type); - } - return 0; - } /* end if (listenaddrs) */ - - /* No ListenAddress lines. If there's no FooPort, then maybe make a default - * one. */ - if (! ports) { - if (defaultport && out) { - port_cfg_t *cfg = port_cfg_new(); - cfg->type = listener_type; - cfg->port = defaultport; - tor_addr_parse(&cfg->addr, defaultaddr); - cfg->session_group = SESSION_GROUP_UNSET; - cfg->isolation_flags = ISO_DEFAULT; - smartlist_add(out, cfg); - } - return 0; - } - - /* At last we can actually parse the FooPort lines. The syntax is: - * [Addr:](Port|auto) [Options].*/ - elts = smartlist_new(); - - for (; ports; ports = ports->next) { - tor_addr_t addr; - int port; - int sessiongroup = SESSION_GROUP_UNSET; - unsigned isolation = ISO_DEFAULT; - int prefer_no_auth = 0; - - char *addrport; - uint16_t ptmp=0; - int ok; - int no_listen = 0, no_advertise = 0, all_addrs = 0, - bind_ipv4_only = 0, bind_ipv6_only = 0, - ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, - cache_ipv4 = 1, use_cached_ipv4 = 0, - cache_ipv6 = 0, use_cached_ipv6 = 0, - prefer_ipv6_automap = 1; - - smartlist_split_string(elts, ports->value, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (smartlist_len(elts) == 0) { - log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname); - goto err; - } - - if (allow_no_options && smartlist_len(elts) > 1) { - log_warn(LD_CONFIG, "Too many options on %sPort line", portname); - goto err; - } - - /* Now parse the addr/port value */ - addrport = smartlist_get(elts, 0); - if (!strcmp(addrport, "auto")) { - port = CFG_AUTO_PORT; - tor_addr_parse(&addr, defaultaddr); - } else if (!strcasecmpend(addrport, ":auto")) { - char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); - port = CFG_AUTO_PORT; - if (tor_addr_port_lookup(addrtmp, &addr, &ptmp)<0 || ptmp) { - log_warn(LD_CONFIG, "Invalid address '%s' for %sPort", - escaped(addrport), portname); - tor_free(addrtmp); - goto err; - } - } else { - /* Try parsing integer port before address, because, who knows? - "19099" might be a valid address. */ - port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); - if (ok) { - tor_addr_parse(&addr, defaultaddr); - } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { - if (ptmp == 0) { - log_warn(LD_CONFIG, "%sPort line has address but no port", portname); - goto err; - } - port = ptmp; - } else { - log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort", - escaped(addrport), portname); - goto err; - } - } - - /* Now parse the rest of the options, if any. */ - if (use_server_options) { - /* This is a server port; parse advertising options */ - SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { - if (elt_sl_idx == 0) - continue; /* Skip addr:port */ - - if (!strcasecmp(elt, "NoAdvertise")) { - no_advertise = 1; - } else if (!strcasecmp(elt, "NoListen")) { - no_listen = 1; -#if 0 - /* not implemented yet. */ - } else if (!strcasecmp(elt, "AllAddrs")) { - - all_addrs = 1; -#endif - } else if (!strcasecmp(elt, "IPv4Only")) { - bind_ipv4_only = 1; - } else if (!strcasecmp(elt, "IPv6Only")) { - bind_ipv6_only = 1; - } else { - log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", - portname, escaped(elt)); - } - } SMARTLIST_FOREACH_END(elt); - - if (no_advertise && no_listen) { - log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise " - "on %sPort line '%s'", - portname, escaped(ports->value)); - goto err; - } - if (bind_ipv4_only && bind_ipv6_only) { - log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " - "on %sPort line '%s'", - portname, escaped(ports->value)); - goto err; - } - if (bind_ipv4_only && tor_addr_family(&addr) == AF_INET6) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", - portname); - goto err; - } - if (bind_ipv6_only && tor_addr_family(&addr) == AF_INET) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", - portname); - goto err; - } - } else { - /* This is a client port; parse isolation options */ - SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { - int no = 0, isoflag = 0; - const char *elt_orig = elt; - if (elt_sl_idx == 0) - continue; /* Skip addr:port */ - if (!strcasecmpstart(elt, "SessionGroup=")) { - int group = (int)tor_parse_long(elt+strlen("SessionGroup="), - 10, 0, INT_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Invalid %sPort option '%s'", - portname, escaped(elt)); - goto err; - } - if (sessiongroup >= 0) { - log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", - portname); - goto err; - } - sessiongroup = group; - continue; - } - - if (!strcasecmpstart(elt, "No")) { - no = 1; - elt += 2; - } - - if (takes_hostnames) { - if (!strcasecmp(elt, "IPv4Traffic")) { - ipv4_traffic = ! no; - continue; - } else if (!strcasecmp(elt, "IPv6Traffic")) { - ipv6_traffic = ! no; - continue; - } else if (!strcasecmp(elt, "PreferIPv6")) { - prefer_ipv6 = ! no; - continue; - } - } - if (!strcasecmp(elt, "CacheIPv4DNS")) { - cache_ipv4 = ! no; - continue; - } else if (!strcasecmp(elt, "CacheIPv6DNS")) { - cache_ipv6 = ! no; - continue; - } else if (!strcasecmp(elt, "CacheDNS")) { - cache_ipv4 = cache_ipv6 = ! no; - continue; - } else if (!strcasecmp(elt, "UseIPv4Cache")) { - use_cached_ipv4 = ! no; - continue; - } else if (!strcasecmp(elt, "UseIPv6Cache")) { - use_cached_ipv6 = ! no; - continue; - } else if (!strcasecmp(elt, "UseDNSCache")) { - use_cached_ipv4 = use_cached_ipv6 = ! no; - continue; - } else if (!strcasecmp(elt, "PreferIPv6Automap")) { - prefer_ipv6_automap = ! no; - continue; - } else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) { - prefer_no_auth = ! no; - continue; - } - - if (!strcasecmpend(elt, "s")) - elt[strlen(elt)-1] = '\0'; /* kill plurals. */ - - if (!strcasecmp(elt, "IsolateDestPort")) { - isoflag = ISO_DESTPORT; - } else if (!strcasecmp(elt, "IsolateDestAddr")) { - isoflag = ISO_DESTADDR; - } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { - isoflag = ISO_SOCKSAUTH; - } else if (!strcasecmp(elt, "IsolateClientProtocol")) { - isoflag = ISO_CLIENTPROTO; - } else if (!strcasecmp(elt, "IsolateClientAddr")) { - isoflag = ISO_CLIENTADDR; - } else { - log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", - portname, escaped(elt_orig)); - } - - if (no) { - isolation &= ~isoflag; - } else { - isolation |= isoflag; - } - } SMARTLIST_FOREACH_END(elt); - } - - if (port) - got_nonzero_port = 1; - else - got_zero_port = 1; - - if (ipv4_traffic == 0 && ipv6_traffic == 0) { - log_warn(LD_CONFIG, "You have a %sPort entry with both IPv4 and " - "IPv6 disabled; that won't work.", portname); - goto err; - } - - if (out && port) { - port_cfg_t *cfg = port_cfg_new(); - tor_addr_copy(&cfg->addr, &addr); - cfg->port = port; - cfg->type = listener_type; - cfg->isolation_flags = isolation; - cfg->session_group = sessiongroup; - cfg->no_advertise = no_advertise; - cfg->no_listen = no_listen; - cfg->all_addrs = all_addrs; - cfg->bind_ipv4_only = bind_ipv4_only; - cfg->bind_ipv6_only = bind_ipv6_only; - cfg->ipv4_traffic = ipv4_traffic; - cfg->ipv6_traffic = ipv6_traffic; - cfg->prefer_ipv6 = prefer_ipv6; - cfg->cache_ipv4_answers = cache_ipv4; - cfg->cache_ipv6_answers = cache_ipv6; - cfg->use_cached_ipv4_answers = use_cached_ipv4; - cfg->use_cached_ipv6_answers = use_cached_ipv6; - cfg->prefer_ipv6_virtaddr = prefer_ipv6_automap; - cfg->socks_prefer_no_auth = prefer_no_auth; - if (! (isolation & ISO_SOCKSAUTH)) - cfg->socks_prefer_no_auth = 1; - - smartlist_add(out, cfg); - } - SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); - smartlist_clear(elts); - } - - if (warn_nonlocal && out) { - if (is_control) - warn_nonlocal_controller_ports(out, forbid_nonlocal); - else if (is_ext_orport) - warn_nonlocal_ext_orports(out, portname); - else - warn_nonlocal_client_ports(out, portname, listener_type); - } - - 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)); - smartlist_free(elts); - return retval; -} - -/** Parse a list of config_line_t for an AF_UNIX unix socket listener option - * from cfg and add them to out. No fancy options are - * supported: the line contains nothing but the path to the AF_UNIX socket. */ -static int -parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, - int listener_type) -{ - - if (!out) - return 0; - - for ( ; cfg; cfg = cfg->next) { - size_t len = strlen(cfg->value); - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); - port->is_unix_addr = 1; - memcpy(port->unix_addr, cfg->value, len+1); - port->type = listener_type; - smartlist_add(out, port); - } - - return 0; -} - -/** Return the number of ports which are actually going to listen with type - * listenertype. 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 - * options. On success, set *n_ports_out to the number - * of ports that are listed, update the *Port_set values in - * options, and return 0. On failure, set *msg to a - * description of the problem and return -1. - * - * If validate_only is false, set configured_client_ports to the - * new list of ports parsed from options. - **/ -static int -parse_ports(or_options_t *options, int validate_only, - char **msg, int *n_ports_out) -{ - smartlist_t *ports; - int retval = -1; - - ports = smartlist_new(); - - *n_ports_out = 0; - - if (parse_port_config(ports, - options->SocksPort_lines, options->SocksListenAddress, - "Socks", CONN_TYPE_AP_LISTENER, - "127.0.0.1", 19099, - CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR| - CL_PORT_TAKES_HOSTNAMES) < 0) { - *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); - goto err; - } - if (parse_port_config(ports, - options->DNSPort_lines, options->DNSListenAddress, - "DNS", CONN_TYPE_AP_DNS_LISTENER, - "127.0.0.1", 0, - CL_PORT_WARN_NONLOCAL|CL_PORT_TAKES_HOSTNAMES) < 0) { - *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); - goto err; - } - if (parse_port_config(ports, - 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_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; - } - { - unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL; - const int any_passwords = (options->HashedControlPassword || - options->HashedControlSessionPassword || - options->CookieAuthentication); - if (! any_passwords) - control_port_flags |= CL_PORT_FORBID_NONLOCAL; - - if (parse_port_config(ports, - options->ControlPort_lines, - options->ControlListenAddress, - "Control", CONN_TYPE_CONTROL_LISTENER, - "127.0.0.1", 0, - control_port_flags) < 0) { - *msg = tor_strdup("Invalid ControlPort/ControlListenAddress " - "configuration"); - goto err; - } - if (parse_unix_socket_config(ports, - options->ControlSocket, - CONN_TYPE_CONTROL_LISTENER) < 0) { - *msg = tor_strdup("Invalid ControlSocket configuration"); - goto err; - } - } - if (! options->ClientOnly) { - if (parse_port_config(ports, - options->ORPort_lines, options->ORListenAddress, - "OR", CONN_TYPE_OR_LISTENER, - "0.0.0.0", 0, - CL_PORT_SERVER_OPTIONS) < 0) { - *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration"); - goto err; - } - if (parse_port_config(ports, - options->ExtORPort_lines, NULL, - "ExtOR", CONN_TYPE_EXT_OR_LISTENER, - "127.0.0.1", 0, - CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) { - *msg = tor_strdup("Invalid ExtORPort configuration"); - goto err; - } - if (parse_port_config(ports, - options->DirPort_lines, options->DirListenAddress, - "Dir", CONN_TYPE_DIR_LISTENER, - "0.0.0.0", 0, - CL_PORT_SERVER_OPTIONS) < 0) { - *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration"); - goto err; - } - } - - if (check_server_ports(ports, options) < 0) { - *msg = tor_strdup("Misconfigured server ports"); - goto err; - } - - *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); - options->ExtORPort_set = - !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER); - - if (!validate_only) { - if (configured_ports) { - SMARTLIST_FOREACH(configured_ports, - port_cfg_t *, p, port_cfg_free(p)); - smartlist_free(configured_ports); - } - configured_ports = ports; - ports = NULL; /* prevent free below. */ - } - - err: - if (ports) { - SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); - smartlist_free(ports); - } - return retval; -} - -/** Given a list of port_cfg_t in ports, check them for internal - * consistency and warn as appropriate. */ -static int -check_server_ports(const smartlist_t *ports, - const or_options_t *options) -{ - int n_orport_advertised = 0; - int n_orport_advertised_ipv4 = 0; - int n_orport_listeners = 0; - int n_dirport_advertised = 0; - int n_dirport_listeners = 0; - int n_low_port = 0; - int r = 0; - - SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { - if (port->type == CONN_TYPE_DIR_LISTENER) { - if (! port->no_advertise) - ++n_dirport_advertised; - if (! port->no_listen) - ++n_dirport_listeners; - } else if (port->type == CONN_TYPE_OR_LISTENER) { - if (! port->no_advertise) { - ++n_orport_advertised; - if (tor_addr_family(&port->addr) == AF_INET || - (tor_addr_family(&port->addr) == AF_UNSPEC && - !port->bind_ipv6_only)) - ++n_orport_advertised_ipv4; - } - if (! port->no_listen) - ++n_orport_listeners; - } else { - continue; - } -#ifndef _WIN32 - if (!port->no_listen && port->port < 1024) - ++n_low_port; -#endif - } SMARTLIST_FOREACH_END(port); - - if (n_orport_advertised && !n_orport_listeners) { - log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually " - "listening on one."); - r = -1; - } - if (n_orport_listeners && !n_orport_advertised) { - log_warn(LD_CONFIG, "We are listening on an ORPort, but not advertising " - "any ORPorts. This will keep us from building a %s " - "descriptor, and make us impossible to use.", - options->BridgeRelay ? "bridge" : "router"); - r = -1; - } - if (n_dirport_advertised && !n_dirport_listeners) { - log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually " - "listening on one."); - r = -1; - } - if (n_dirport_advertised > 1) { - log_warn(LD_CONFIG, "Can't advertise more than one DirPort."); - r = -1; - } - if (n_orport_advertised && !n_orport_advertised_ipv4 && - !options->BridgeRelay) { - log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 " - "address."); - r = -1; - } - - if (n_low_port && options->AccountingMax) { - log_warn(LD_CONFIG, - "You have set AccountingMax to use hibernation. You have also " - "chosen a low DirPort or OrPort. This combination can make Tor stop " - "working when it tries to re-attach the port after a period of " - "hibernation. Please choose a different port or turn off " - "hibernation unless you know this combination will work on your " - "platform."); - } - - return r; -} - -/** Return a list of port_cfg_t for client ports parsed from the - * options. */ -const smartlist_t * -get_configured_ports(void) -{ - if (!configured_ports) - configured_ports = smartlist_new(); - return configured_ports; -} - -/** Return an address:port string representation of the address - * where the first listener_type listener waits for - * connections. Return NULL if we couldn't find a listener. The - * string is allocated on the heap and it's the responsibility of the - * caller to free it after use. - * - * This function is meant to be used by the pluggable transport proxy - * spawning code, please make sure that it fits your purposes before - * using it. */ -char * -get_first_listener_addrport_string(int listener_type) -{ - static const char *ipv4_localhost = "127.0.0.1"; - static const char *ipv6_localhost = "[::1]"; - const char *address; - uint16_t port; - char *string = NULL; - - if (!configured_ports) - return NULL; - - SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { - if (cfg->no_listen) - continue; - - if (cfg->type == listener_type && - tor_addr_family(&cfg->addr) != AF_UNSPEC) { - - /* We found the first listener of the type we are interested in! */ - - /* If a listener is listening on INADDR_ANY, assume that it's - also listening on 127.0.0.1, and point the transport proxy - there: */ - if (tor_addr_is_null(&cfg->addr)) - address = tor_addr_is_v4(&cfg->addr) ? ipv4_localhost : ipv6_localhost; - else - address = fmt_and_decorate_addr(&cfg->addr); - - /* If a listener is configured with port 'auto', we are forced - to iterate all listener connections and find out in which - port it ended up listening: */ - if (cfg->port == CFG_AUTO_PORT) { - port = router_get_active_listener_port_by_type_af(listener_type, - tor_addr_family(&cfg->addr)); - if (!port) - return NULL; - } else { - port = cfg->port; - } - - tor_asprintf(&string, "%s:%u", address, port); - - return string; - } - - } SMARTLIST_FOREACH_END(cfg); - - return NULL; -} - -/** Return the first advertised port of type listener_type in - address_family. */ -int -get_first_advertised_port_by_type_af(int listener_type, int address_family) -{ - if (!configured_ports) - return 0; - SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { - if (cfg->type == listener_type && - !cfg->no_advertise && - (tor_addr_family(&cfg->addr) == address_family || - tor_addr_family(&cfg->addr) == AF_UNSPEC)) { - if (tor_addr_family(&cfg->addr) != AF_UNSPEC || - (address_family == AF_INET && !cfg->bind_ipv6_only) || - (address_family == AF_INET6 && !cfg->bind_ipv4_only)) { - return cfg->port; - } - } - } SMARTLIST_FOREACH_END(cfg); - return 0; -} - -/** Adjust the value of options->DataDirectory, or fill it in if it's - * absent. Return 0 on success, -1 on failure. */ -static int -normalize_data_directory(or_options_t *options) -{ - char *p; - if (options->DataDirectory) - return 0; /* all set */ -#ifdef _WIN32 - p = tor_malloc(MAX_PATH); - strlcpy(p,anonymize_tor_data_directory(),MAX_PATH); -#else - p = tor_malloc(PATH_MAX); - strlcpy(p,anonymize_tor_data_directory(),PATH_MAX); -#endif - options->DataDirectory = p; - return 0; -} - -/** Check and normalize the value of options->DataDirectory; return 0 if it - * is sane, -1 otherwise. */ -static int -validate_data_directory(or_options_t *options) -{ - if (normalize_data_directory(options) < 0) - return -1; - tor_assert(options->DataDirectory); - if (strlen(options->DataDirectory) > (512-128)) { - log_warn(LD_CONFIG, "DataDirectory is too long."); - return -1; - } - return 0; -} - -/** This string must remain the same forevermore. It is how we - * recognize that the torrc file doesn't need to be backed up. */ -#define GENERATED_FILE_PREFIX "# This file was generated by Tor; " \ - "if you edit it, comments will not be preserved" -/** This string can change; it tries to give the reader an idea - * that editing this file by hand is not a good plan. */ -#define GENERATED_FILE_COMMENT "# The old torrc file was renamed " \ - "to torrc.orig.1 or similar, and Tor will ignore it" - -/** Save a configuration file for the configuration in options - * into the file fname. If the file already exists, and - * doesn't begin with GENERATED_FILE_PREFIX, rename it. Otherwise - * replace it. Return 0 on success, -1 on failure. */ -static int -write_configuration_file(const char *fname, const or_options_t *options) -{ - char *old_val=NULL, *new_val=NULL, *new_conf=NULL; - int rename_old = 0, r; - - tor_assert(fname); - - switch (file_status(fname)) { - case FN_FILE: - old_val = read_file_to_str(fname, 0, NULL); - if (!old_val || strcmpstart(old_val, GENERATED_FILE_PREFIX)) { - rename_old = 1; - } - tor_free(old_val); - break; - case FN_NOENT: - break; - case FN_ERROR: - case FN_DIR: - default: - log_warn(LD_CONFIG, - "Config file \"%s\" is not a file? Failing.", fname); - return -1; - } - - if (!(new_conf = options_dump(options, OPTIONS_DUMP_MINIMAL))) { - log_warn(LD_BUG, "Couldn't get configuration string"); - goto err; - } - - tor_asprintf(&new_val, "%s\n%s\n\n%s", - GENERATED_FILE_PREFIX, GENERATED_FILE_COMMENT, new_conf); - - if (rename_old) { - int i = 1; - char *fn_tmp = NULL; - while (1) { - tor_asprintf(&fn_tmp, "%s.orig.%d", fname, i); - if (file_status(fn_tmp) == FN_NOENT) - break; - tor_free(fn_tmp); - ++i; - } - log_notice(LD_CONFIG, "Renaming old configuration file to \"%s\"", fn_tmp); - if (rename(fname, fn_tmp) < 0) { - log_warn(LD_FS, - "Couldn't rename configuration file \"%s\" to \"%s\": %s", - fname, fn_tmp, strerror(errno)); - tor_free(fn_tmp); - goto err; - } - tor_free(fn_tmp); - } - - if (write_str_to_file(fname, new_val, 0) < 0) - goto err; - - r = 0; - goto done; - err: - r = -1; - done: - tor_free(new_val); - tor_free(new_conf); - return r; -} - -/** - * Save the current configuration file value to disk. Return 0 on - * success, -1 on failure. - **/ -int -options_save_current(void) -{ - /* This fails if we can't write to our configuration file. - * - * If we try falling back to datadirectory or something, we have a better - * chance of saving the configuration, but a better chance of doing - * something the user never expected. */ - return write_configuration_file(get_torrc_fname(0), get_options()); -} - -/** Return the number of cpus configured in options. If we are - * told to auto-detect the number of cpus, return the auto-detected number. */ -int -get_num_cpus(const or_options_t *options) -{ - if (options->NumCPUs == 0) { - int n = compute_num_cpus(); - return (n >= 1) ? n : 1; - } else { - return options->NumCPUs; - } -} - -/** - * Initialize the libevent library. - */ -static void -init_libevent(const or_options_t *options) -{ - const char *badness=NULL; - tor_libevent_cfg cfg; - - tor_assert(options); - - configure_libevent_logging(); - /* If the kernel complains that some method (say, epoll) doesn't - * exist, we don't care about it, since libevent will cope. - */ - suppress_libevent_log_msg("Function not implemented"); - - tor_check_libevent_header_compatibility(); - - memset(&cfg, 0, sizeof(cfg)); - cfg.disable_iocp = options->DisableIOCP; - cfg.num_cpus = get_num_cpus(options); - cfg.msec_per_tick = options->TokenBucketRefillInterval; - - tor_libevent_initialize(&cfg); - - suppress_libevent_log_msg(NULL); - - tor_check_libevent_version(tor_libevent_get_method(), - server_mode(get_options()), - &badness); - if (badness) { - const char *v = tor_libevent_get_version_str(); - const char *m = tor_libevent_get_method(); - control_event_general_status(LOG_WARN, - "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO", - v, m, badness); - } -} - -/** Return a newly allocated string holding a filename relative to the data - * directory. If sub1 is present, it is the first path component after - * the data directory. If sub2 is also present, it is the second path - * component after the data directory. If suffix is present, it - * is appended to the filename. - * - * Examples: - * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a - * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp - * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp - * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b - * - * Note: Consider using the get_datadir_fname* macros in or.h. - */ -char * -options_get_datadir_fname2_suffix(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix) -{ - char *fname = NULL; - size_t len; - tor_assert(options); - tor_assert(options->DataDirectory); - tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */ - len = strlen(options->DataDirectory); - if (sub1) { - len += strlen(sub1)+1; - if (sub2) - len += strlen(sub2)+1; - } - if (suffix) - len += strlen(suffix); - len++; - fname = tor_malloc(len); - if (sub1) { - if (sub2) { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1, sub2); - } else { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1); - } - } else { - strlcpy(fname, options->DataDirectory, len); - } - if (suffix) - strlcat(fname, suffix, len); - return fname; -} - -/** Check wether the data directory has a private subdirectory - * subdir. If not, try to create it. Return 0 on success, - * -1 otherwise. */ -int -check_or_create_data_subdir(const char *subdir) -{ - char *statsdir = get_datadir_fname(subdir); - int return_val = 0; - - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create %s/ directory!", subdir); - return_val = -1; - } - tor_free(statsdir); - return return_val; -} - -/** Create a file named fname with contents str in the - * subdirectory subdir of the data directory. descr - * should be a short description of the file's content and will be - * used for the warning message, if it's present and the write process - * fails. Return 0 on success, -1 otherwise.*/ -int -write_to_data_subdir(const char* subdir, const char* fname, - const char* str, const char* descr) -{ - char *filename = get_datadir_fname2(subdir, fname); - int return_val = 0; - - if (write_str_to_file(filename, str, 0) < 0) { - log_warn(LD_HIST, "Unable to write %s to disk!", descr ? descr : fname); - return_val = -1; - } - tor_free(filename); - return return_val; -} - -/** Given a file name check to see whether the file exists but has not been - * modified for a very long time. If so, remove it. */ -void -remove_file_if_very_old(const char *fname, time_t now) -{ -#define VERY_OLD_FILE_AGE (28*24*60*60) - struct stat st; - - if (stat(sandbox_intern_string(fname), &st)==0 && - st.st_mtime < now-VERY_OLD_FILE_AGE) { - char buf[ISO_TIME_LEN+1]; - format_local_iso_time(buf, st.st_mtime); - log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. " - "Removing it.", fname, buf); - unlink(fname); - } -} - -/** 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 *answer to a - * new string describing the supported configuration variables and their - * types. */ -int -getinfo_helper_config(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg) -{ - (void) conn; - (void) errmsg; - if (!strcmp(question, "config/names")) { - smartlist_t *sl = smartlist_new(); - int i; - 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; - case CONFIG_TYPE_UINT: type = "Integer"; break; - case CONFIG_TYPE_INT: type = "SignedInteger"; break; - case CONFIG_TYPE_PORT: type = "Port"; break; - case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; - case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break; - case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; - case CONFIG_TYPE_DOUBLE: type = "Float"; break; - case CONFIG_TYPE_BOOL: type = "Boolean"; break; - case CONFIG_TYPE_AUTOBOOL: type = "Boolean+Auto"; break; - case CONFIG_TYPE_ISOTIME: type = "Time"; break; - case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break; - case CONFIG_TYPE_CSV: type = "CommaList"; break; - case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break; - case CONFIG_TYPE_LINELIST: type = "LineList"; break; - case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break; - case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; - default: - case CONFIG_TYPE_OBSOLETE: - type = NULL; break; - } - if (!type) - continue; - smartlist_add_asprintf(sl, "%s %s\n",var->name,type); - } - *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; -} - -/** Parse outbound bind address option lines. If validate_only - * is not 0 update OutboundBindAddressIPv4_ and - * OutboundBindAddressIPv6_ in options. On failure, set - * msg (if provided) to a newly allocated string containing a - * description of the problem and return -1. */ -static int -parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) -{ - const config_line_t *lines = options->OutboundBindAddress; - int found_v4 = 0, found_v6 = 0; - - if (!validate_only) { - memset(&options->OutboundBindAddressIPv4_, 0, - sizeof(options->OutboundBindAddressIPv4_)); - memset(&options->OutboundBindAddressIPv6_, 0, - sizeof(options->OutboundBindAddressIPv6_)); - } - while (lines) { - tor_addr_t addr, *dst_addr = NULL; - int af = tor_addr_parse(&addr, lines->value); - switch (af) { - case AF_INET: - if (found_v4) { - if (msg) - tor_asprintf(msg, "Multiple IPv4 outbound bind addresses " - "configured: %s", lines->value); - return -1; - } - found_v4 = 1; - dst_addr = &options->OutboundBindAddressIPv4_; - break; - case AF_INET6: - if (found_v6) { - if (msg) - tor_asprintf(msg, "Multiple IPv6 outbound bind addresses " - "configured: %s", lines->value); - return -1; - } - found_v6 = 1; - dst_addr = &options->OutboundBindAddressIPv6_; - break; - default: - if (msg) - tor_asprintf(msg, "Outbound bind address '%s' didn't parse.", - lines->value); - return -1; - } - if (!validate_only) - tor_addr_copy(dst_addr, &addr); - lines = lines->next; - } - return 0; -} - -/** Load one of the geoip files, family determining which - * one. default_fname is used if on Windows and - * fname equals "". */ -static void -config_load_geoip_file_(sa_family_t family, - const char *fname, - const char *default_fname) -{ -#ifdef _WIN32 - char *free_fname = NULL; /* Used to hold any temporary-allocated value */ - /* XXXX Don't use this "" junk; make our filename options - * understand prefixes somehow. -NM */ - if (!strcmp(fname, "")) { - const char *conf_root = get_windows_conf_root(); - tor_asprintf(&free_fname, "%s\\%s", conf_root, default_fname); - fname = free_fname; - } - geoip_load_file(family, fname); - tor_free(free_fname); -#else - (void)default_fname; - geoip_load_file(family, fname); -#endif -} - -/** Load geoip files for IPv4 and IPv6 if options and - * old_options indicate we should. */ -static void -config_maybe_load_geoip_files_(const or_options_t *options, - const or_options_t *old_options) -{ - /* XXXX024 Reload GeoIPFile on SIGHUP. -NM */ - - if (options->GeoIPFile && - ((!old_options || !opt_streq(old_options->GeoIPFile, - options->GeoIPFile)) - || !geoip_is_loaded(AF_INET))) - config_load_geoip_file_(AF_INET, options->GeoIPFile, "geoip"); - if (options->GeoIPv6File && - ((!old_options || !opt_streq(old_options->GeoIPv6File, - options->GeoIPv6File)) - || !geoip_is_loaded(AF_INET6))) - config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6"); -} - -/** Initialize cookie authentication (used so far by the ControlPort - * and Extended ORPort). - * - * Allocate memory and create a cookie (of length cookie_len) - * in cookie_out. - * Then write it down to fname and prepend it with header. - * - * If the whole procedure was successful, set - * cookie_is_set_out to True. */ -int -init_cookie_authentication(const char *fname, const char *header, - int cookie_len, - uint8_t **cookie_out, int *cookie_is_set_out) -{ - char cookie_file_str_len = strlen(header) + cookie_len; - char *cookie_file_str = tor_malloc(cookie_file_str_len); - int retval = -1; - - /* We don't want to generate a new cookie every time we call - * options_act(). One should be enough. */ - if (*cookie_is_set_out) { - retval = 0; /* we are all set */ - goto done; - } - - /* If we've already set the cookie, free it before re-setting - it. This can happen if we previously generated a cookie, but - couldn't write it to a disk. */ - if (*cookie_out) - tor_free(*cookie_out); - - /* Generate the cookie */ - *cookie_out = tor_malloc(cookie_len); - if (crypto_rand((char *)*cookie_out, cookie_len) < 0) - goto done; - - /* Create the string that should be written on the file. */ - memcpy(cookie_file_str, header, strlen(header)); - memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len); - if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) { - log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname)); - goto done; - } - - /* Success! */ - log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname)); - *cookie_is_set_out = 1; - retval = 0; - - done: - memwipe(cookie_file_str, 0, cookie_file_str_len); - tor_free(cookie_file_str); - return retval; -} - diff --git a/src/tor/config.h b/src/tor/config.h deleted file mode 100644 index 8ee2a45..0000000 --- a/src/tor/config.h +++ /dev/null @@ -1,144 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file config.h - * \brief Header file for config.c. - **/ - -#ifndef TOR_CONFIG_H -#define TOR_CONFIG_H - -const char *get_dirportfrontpage(void); -const or_options_t *get_options(void); -or_options_t *get_options_mutable(void); -int set_options(or_options_t *new_val, char **msg); -void config_free_all(void); -const char *safe_str_client(const char *address); -const char *safe_str(const char *address); -const char *escaped_safe_str_client(const char *address); -const char *escaped_safe_str(const char *address); -const char *get_version(void); -const char *get_short_version(void); -setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, - int clear_first, char **msg); - -uint32_t get_last_resolved_addr(void); -int resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out); -int is_local_addr(const tor_addr_t *addr); -void options_init(or_options_t *options); - -#define OPTIONS_DUMP_MINIMAL 1 -#define OPTIONS_DUMP_DEFAULTS 2 -#define OPTIONS_DUMP_ALL 3 -char *options_dump(const or_options_t *options, int how_to_dump); -int options_init_from_torrc(int argc, char **argv); -setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, - int command, const char *command_arg, char **msg); -int option_is_recognized(const char *key); -const char *option_get_canonical_name(const char *key); -config_line_t *option_get_assignment(const or_options_t *options, - const char *key); -int options_save_current(void); -const char *get_torrc_fname(int defaults_fname); -char *options_get_datadir_fname2_suffix(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix); -#define get_datadir_fname2_suffix(sub1, sub2, suffix) \ - options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) -/** Return a newly allocated string containing datadir/sub1. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL) -/** Return a newly allocated string containing datadir/sub1/sub2. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname2(sub1,sub2) \ - get_datadir_fname2_suffix((sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1suffix. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname_suffix(sub1, suffix) \ - get_datadir_fname2_suffix((sub1), NULL, (suffix)) - -int check_or_create_data_subdir(const char *subdir); -int write_to_data_subdir(const char* subdir, const char* fname, - const char* str, const char* descr); - -int get_num_cpus(const or_options_t *options); - -const smartlist_t *get_configured_ports(void); -int get_first_advertised_port_by_type_af(int listener_type, - int address_family); -#define get_primary_or_port() \ - (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) -#define get_primary_dir_port() \ - (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET)) - -char *get_first_listener_addrport_string(int listener_type); - -int options_need_geoip_info(const or_options_t *options, - const char **reason_out); - -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); - -const char *tor_get_digests(void); -uint32_t get_effective_bwrate(const or_options_t *options); -uint32_t get_effective_bwburst(const or_options_t *options); - -char *get_transport_bindaddr_from_config(const char *transport); - -int init_cookie_authentication(const char *fname, const char *header, - int cookie_len, - uint8_t **cookie_out, int *cookie_is_set_out); - -or_options_t *options_new(void); - -int config_parse_commandline(int argc, char **argv, int ignore_errors, - config_line_t **result, - config_line_t **cmdline_result); - -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); - -/** Represents the information stored in a torrc Bridge line. */ -typedef struct bridge_line_t { - tor_addr_t addr; /* The IP address of the bridge. */ - uint16_t port; /* The TCP port of the bridge. */ - char *transport_name; /* The name of the pluggable transport that - should be used to connect to the bridge. */ - char digest[DIGEST_LEN]; /* The bridge's identity key digest. */ - smartlist_t *socks_args; /* SOCKS arguments for the pluggable - transport proxy. */ -} bridge_line_t; - -void bridge_line_free(bridge_line_t *bridge_line); -bridge_line_t *parse_bridge_line(const char *line); -smartlist_t *get_options_from_transport_options_line(const char *line, - const char *transport); -smartlist_t *get_options_for_server_transport(const char *transport); - -#ifdef CONFIG_PRIVATE -#ifdef TOR_UNIT_TESTS -extern struct config_format_t options_format; -#endif - -STATIC void or_options_free(or_options_t *options); -STATIC int options_validate(or_options_t *old_options, - or_options_t *options, - or_options_t *default_options, - int from_setconf, char **msg); -#endif - -#endif - diff --git a/src/tor/config_codedigest.c b/src/tor/config_codedigest.c deleted file mode 100644 index 86d14ba..0000000 --- a/src/tor/config_codedigest.c +++ /dev/null @@ -1,13 +0,0 @@ - -const char *tor_get_digests(void); - -/** Return a string describing the digest of the source files in src/or/ - */ -const char * -tor_get_digests(void) -{ - return "" -#include "or_sha1.i" - ; -} - diff --git a/src/tor/confparse.c b/src/tor/confparse.c deleted file mode 100644 index c5400a6..0000000 --- a/src/tor/confparse.c +++ /dev/null @@ -1,1316 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "confparse.h" -#include "routerset.h" - -static uint64_t config_parse_memunit(const char *s, int *ok); -static int config_parse_msec_interval(const char *s, int *ok); -static int config_parse_interval(const char *s, int *ok); -static void config_reset(const config_format_t *fmt, void *options, - const config_var_t *var, int use_defaults); - -/** Allocate an empty configuration object of a given format type. */ -void * -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; - CONFIG_CHECK(fmt, opts); - return opts; -} - -/* - * Functions to parse config options - */ - -/** If option is an official abbreviation for a longer option, - * return the longer option. Otherwise return option. - * If command_line is set, apply all abbreviations. Otherwise, only - * apply abbreviations that work for the config file and the command line. - * If warn_obsolete is set, warn about deprecated names. */ -const char * -config_expand_abbrev(const config_format_t *fmt, const char *option, - int command_line, int warn_obsolete) -{ - int i; - if (! fmt->abbrevs) - return option; - for (i=0; fmt->abbrevs[i].abbreviated; ++i) { - /* Abbreviations are case insensitive. */ - if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) && - (command_line || !fmt->abbrevs[i].commandline_only)) { - if (warn_obsolete && fmt->abbrevs[i].warn) { - log_warn(LD_CONFIG, - "The configuration option '%s' is deprecated; " - "use '%s' instead.", - fmt->abbrevs[i].abbreviated, - fmt->abbrevs[i].full); - } - /* Keep going through the list in case we want to rewrite it more. - * (We could imagine recursing here, but I don't want to get the - * user into an infinite loop if we craft our list wrong.) */ - option = fmt->abbrevs[i].full; - } - } - return option; -} - -/** Helper: allocate a new configuration option mapping 'key' to 'val', - * append it to *lst. */ -void -config_line_append(config_line_t **lst, - const char *key, - const char *val) -{ - config_line_t *newline; - - newline = tor_malloc_zero(sizeof(config_line_t)); - newline->key = tor_strdup(key); - newline->value = tor_strdup(val); - newline->next = NULL; - while (*lst) - lst = &((*lst)->next); - - (*lst) = newline; -} - -/** Return the line in lines whose key is exactly key, or NULL - * if no such key exists. For handling commandline-only options only; other - * options should be looked up in the appropriate data structure. */ -const config_line_t * -config_line_find(const config_line_t *lines, - const char *key) -{ - const config_line_t *cl; - for (cl = lines; cl; cl = cl->next) { - if (!strcmp(cl->key, key)) - return cl; - } - return NULL; -} - -/** Helper: parse the config string and strdup into key/value - * strings. Set *result to the list, or NULL if parsing the string - * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. - * - * If extended is set, then treat keys beginning with / and with + as - * indicating "clear" and "append" respectively. */ -int -config_get_lines(const char *string, config_line_t **result, int extended) -{ - config_line_t *list = NULL, **next; - char *k, *v; - const char *parse_err; - - next = &list; - do { - k = v = NULL; - string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err); - if (!string) { - log_warn(LD_CONFIG, "Error while parsing configuration: %s", - parse_err?parse_err:""); - config_free_lines(list); - tor_free(k); - tor_free(v); - return -1; - } - if (k && v) { - unsigned command = CONFIG_LINE_NORMAL; - if (extended) { - if (k[0] == '+') { - char *k_new = tor_strdup(k+1); - tor_free(k); - k = k_new; - command = CONFIG_LINE_APPEND; - } else if (k[0] == '/') { - char *k_new = tor_strdup(k+1); - tor_free(k); - k = k_new; - tor_free(v); - v = tor_strdup(""); - command = CONFIG_LINE_CLEAR; - } - } - /* This list can get long, so we keep a pointer to the end of it - * rather than using config_line_append over and over and getting - * n^2 performance. */ - *next = tor_malloc_zero(sizeof(config_line_t)); - (*next)->key = k; - (*next)->value = v; - (*next)->next = NULL; - (*next)->command = command; - next = &((*next)->next); - } else { - tor_free(k); - tor_free(v); - } - } while (*string); - - *result = list; - return 0; -} - -/** - * Free all the configuration lines on the linked list front. - */ -void -config_free_lines(config_line_t *front) -{ - config_line_t *tmp; - - while (front) { - tmp = front; - front = tmp->next; - - tor_free(tmp->key); - tor_free(tmp->value); - tor_free(tmp); - } -} - -/** As config_find_option, but return a non-const pointer. */ -config_var_t * -config_find_option_mutable(config_format_t *fmt, const char *key) -{ - int i; - size_t keylen = strlen(key); - if (!keylen) - return NULL; /* if they say "--" on the command line, it's not an option */ - /* First, check for an exact (case-insensitive) match */ - for (i=0; fmt->vars[i].name; ++i) { - if (!strcasecmp(key, fmt->vars[i].name)) { - return &fmt->vars[i]; - } - } - /* If none, check for an abbreviated match */ - for (i=0; fmt->vars[i].name; ++i) { - if (!strncasecmp(key, fmt->vars[i].name, keylen)) { - log_warn(LD_CONFIG, "The abbreviation '%s' is deprecated. " - "Please use '%s' instead", - key, fmt->vars[i].name); - return &fmt->vars[i]; - } - } - /* Okay, unrecognized option */ - return NULL; -} - -/** If key is a configuration option, return the corresponding const - * config_var_t. Otherwise, if key is a non-standard abbreviation, - * warn, and return the corresponding const config_var_t. Otherwise return - * NULL. - */ -const config_var_t * -config_find_option(const config_format_t *fmt, const char *key) -{ - return config_find_option_mutable((config_format_t*)fmt, key); -} - -/** Return the number of option entries in fmt. */ -static int -config_count_options(const config_format_t *fmt) -{ - int i; - for (i=0; fmt->vars[i].name; ++i) - ; - return i; -} - -/* - * Functions to assign config options. - */ - -/** c-\>key is known to be a real key. Update options - * with c-\>value and return 0, or return -1 if bad value. - * - * Called from config_assign_line() and option_reset(). - */ -static int -config_assign_value(const config_format_t *fmt, void *options, - config_line_t *c, char **msg) -{ - int i, ok; - const config_var_t *var; - void *lvalue; - int *csv_int; - smartlist_t *csv_str; - - CONFIG_CHECK(fmt, options); - - var = config_find_option(fmt, c->key); - tor_assert(var); - - lvalue = STRUCT_VAR_P(options, var->var_offset); - - switch (var->type) { - - case CONFIG_TYPE_PORT: - if (!strcasecmp(c->value, "auto")) { - *(int *)lvalue = CFG_AUTO_PORT; - break; - } - /* fall through */ - case CONFIG_TYPE_INT: - case CONFIG_TYPE_UINT: - i = (int)tor_parse_long(c->value, 10, - var->type==CONFIG_TYPE_INT ? INT_MIN : 0, - var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, - &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Int keyword '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_INTERVAL: { - i = config_parse_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MSEC_INTERVAL: { - i = config_parse_msec_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Msec interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MEMUNIT: { - uint64_t u64 = config_parse_memunit(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Value '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(uint64_t *)lvalue = u64; - break; - } - - case CONFIG_TYPE_BOOL: - i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Boolean '%s %s' expects 0 or 1.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_AUTOBOOL: - if (!strcmp(c->value, "auto")) - *(int *)lvalue = -1; - else if (!strcmp(c->value, "0")) - *(int *)lvalue = 0; - else if (!strcmp(c->value, "1")) - *(int *)lvalue = 1; - else { - tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.", - c->key, c->value); - return -1; - } - break; - - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char **)lvalue); - *(char **)lvalue = tor_strdup(c->value); - break; - - case CONFIG_TYPE_DOUBLE: - *(double *)lvalue = atof(c->value); - break; - - case CONFIG_TYPE_ISOTIME: - if (parse_iso_time(c->value, (time_t *)lvalue)) { - tor_asprintf(msg, - "Invalid time '%s' for keyword '%s'", c->value, c->key); - return -1; - } - break; - - case CONFIG_TYPE_ROUTERSET: - if (*(routerset_t**)lvalue) { - routerset_free(*(routerset_t**)lvalue); - } - *(routerset_t**)lvalue = routerset_new(); - if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) { - tor_asprintf(msg, "Invalid exit list '%s' for option '%s'", - c->value, c->key); - return -1; - } - break; - - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); - smartlist_clear(*(smartlist_t**)lvalue); - } else { - *(smartlist_t**)lvalue = smartlist_new(); - } - - smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - break; - - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t**)lvalue, int *, cp, tor_free(cp)); - smartlist_clear(*(smartlist_t**)lvalue); - } else { - *(smartlist_t**)lvalue = smartlist_new(); - } - csv_str = smartlist_new(); - smartlist_split_string(csv_str, c->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(csv_str, char *, str) - { - i = config_parse_interval(str, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval in '%s %s' is malformed or out of bounds.", - c->key, c->value); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - return -1; - } - csv_int = tor_malloc_zero(sizeof(int)); - *csv_int = i; - smartlist_add(*(smartlist_t**)lvalue, csv_int); - } - SMARTLIST_FOREACH_END(str); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - break; - - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - { - config_line_t *lastval = *(config_line_t**)lvalue; - if (lastval && lastval->fragile) { - if (c->command != CONFIG_LINE_APPEND) { - config_free_lines(lastval); - *(config_line_t**)lvalue = NULL; - } else { - lastval->fragile = 0; - } - } - - config_line_append((config_line_t**)lvalue, c->key, c->value); - } - break; - case CONFIG_TYPE_OBSOLETE: - log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); - break; - case CONFIG_TYPE_LINELIST_V: - tor_asprintf(msg, - "You may not provide a value for virtual option '%s'", c->key); - return -1; - default: - tor_assert(0); - break; - } - return 0; -} - -/** Mark every linelist in options "fragile", so that fresh assignments - * to it will replace old ones. */ -static void -config_mark_lists_fragile(const config_format_t *fmt, void *options) -{ - int i; - tor_assert(fmt); - tor_assert(options); - - for (i = 0; fmt->vars[i].name; ++i) { - const config_var_t *var = &fmt->vars[i]; - config_line_t *list; - if (var->type != CONFIG_TYPE_LINELIST && - var->type != CONFIG_TYPE_LINELIST_V) - continue; - - list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset); - if (list) - list->fragile = 1; - } -} - -/** If c is a syntactically valid configuration line, update - * options with its value and return 0. Otherwise return -1 for bad - * key, -2 for bad value. - * - * If clear_first is set, clear the value first. Then if - * use_defaults is set, set the value to the default. - * - * Called from config_assign(). - */ -static int -config_assign_line(const config_format_t *fmt, void *options, - config_line_t *c, int use_defaults, - int clear_first, bitarray_t *options_seen, char **msg) -{ - const config_var_t *var; - - CONFIG_CHECK(fmt, options); - - var = config_find_option(fmt, c->key); - if (!var) { - if (fmt->extra) { - void *lvalue = STRUCT_VAR_P(options, fmt->extra->var_offset); - log_info(LD_CONFIG, - "Found unrecognized option '%s'; saving it.", c->key); - config_line_append((config_line_t**)lvalue, c->key, c->value); - return 0; - } else { - tor_asprintf(msg, - "Unknown option '%s'. Failing.", c->key); - return -1; - } - } - - /* Put keyword into canonical case. */ - if (strcmp(var->name, c->key)) { - tor_free(c->key); - c->key = tor_strdup(var->name); - } - - if (!strlen(c->value)) { - /* reset or clear it, then return */ - if (!clear_first) { - if ((var->type == CONFIG_TYPE_LINELIST || - var->type == CONFIG_TYPE_LINELIST_S) && - c->command != CONFIG_LINE_CLEAR) { - /* We got an empty linelist from the torrc or command line. - As a special case, call this an error. Warn and ignore. */ - log_warn(LD_CONFIG, - "Linelist option '%s' has no value. Skipping.", c->key); - } else { /* not already cleared */ - config_reset(fmt, options, var, use_defaults); - } - } - return 0; - } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { - config_reset(fmt, options, var, use_defaults); - } - - if (options_seen && (var->type != CONFIG_TYPE_LINELIST && - var->type != CONFIG_TYPE_LINELIST_S)) { - /* We're tracking which options we've seen, and this option is not - * supposed to occur more than once. */ - int var_index = (int)(var - fmt->vars); - if (bitarray_is_set(options_seen, var_index)) { - log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last " - "value will be ignored.", var->name); - } - bitarray_set(options_seen, var_index); - } - - if (config_assign_value(fmt, options, c, msg) < 0) - return -2; - return 0; -} - -/** Restore the option named key in options to its default value. - * Called from config_assign(). */ -static void -config_reset_line(const config_format_t *fmt, void *options, - const char *key, int use_defaults) -{ - const config_var_t *var; - - CONFIG_CHECK(fmt, options); - - var = config_find_option(fmt, key); - if (!var) - return; /* give error on next pass. */ - - config_reset(fmt, options, var, use_defaults); -} - -/** Return true iff value needs to be quoted and escaped to be used in - * a configuration file. */ -static int -config_value_needs_escape(const char *value) -{ - if (*value == '\"') - return 1; - while (*value) { - switch (*value) - { - case '\r': - case '\n': - case '#': - /* Note: quotes and backspaces need special handling when we are using - * quotes, not otherwise, so they don't trigger escaping on their - * own. */ - return 1; - default: - if (!TOR_ISPRINT(*value)) - return 1; - } - ++value; - } - return 0; -} - -/** Return a newly allocated deep copy of the lines in inp. */ -config_line_t * -config_lines_dup(const config_line_t *inp) -{ - config_line_t *result = NULL; - config_line_t **next_out = &result; - while (inp) { - *next_out = tor_malloc_zero(sizeof(config_line_t)); - (*next_out)->key = tor_strdup(inp->key); - (*next_out)->value = tor_strdup(inp->value); - inp = inp->next; - next_out = &((*next_out)->next); - } - (*next_out) = NULL; - return result; -} - -/** Return newly allocated line or lines corresponding to key in the - * configuration options. If escape_val is true and a - * value needs to be quoted before it's put in a config file, quote and - * escape that value. Return NULL if no such key exists. */ -config_line_t * -config_get_assigned_option(const config_format_t *fmt, const void *options, - const char *key, int escape_val) -{ - const config_var_t *var; - const void *value; - config_line_t *result; - smartlist_t *csv_str; - tor_assert(options && key); - - CONFIG_CHECK(fmt, options); - - var = config_find_option(fmt, key); - if (!var) { - log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key); - return NULL; - } - value = STRUCT_VAR_P(options, var->var_offset); - - result = tor_malloc_zero(sizeof(config_line_t)); - result->key = tor_strdup(var->name); - switch (var->type) - { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - if (*(char**)value) { - result->value = tor_strdup(*(char**)value); - } else { - tor_free(result->key); - tor_free(result); - return NULL; - } - break; - case CONFIG_TYPE_ISOTIME: - if (*(time_t*)value) { - result->value = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(result->value, *(time_t*)value); - } else { - tor_free(result->key); - tor_free(result); - } - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_PORT: - if (*(int*)value == CFG_AUTO_PORT) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - /* This means every or_options_t uint or bool element - * needs to be an int. Not, say, a uint16_t or char. */ - tor_asprintf(&result->value, "%d", *(int*)value); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_MEMUNIT: - tor_asprintf(&result->value, U64_FORMAT, - U64_PRINTF_ARG(*(uint64_t*)value)); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_DOUBLE: - tor_asprintf(&result->value, "%f", *(double*)value); - escape_val = 0; /* Can't need escape. */ - break; - - case CONFIG_TYPE_AUTOBOOL: - if (*(int*)value == -1) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_BOOL: - result->value = tor_strdup(*(int*)value ? "1" : "0"); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_ROUTERSET: - result->value = routerset_to_string(*(routerset_t**)value); - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)value) - result->value = - smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); - else - result->value = tor_strdup(""); - break; - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)value) { - csv_str = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(*(smartlist_t**)value, int *, i) - { - smartlist_add_asprintf(csv_str, "%d", *i); - } - SMARTLIST_FOREACH_END(i); - result->value = smartlist_join_strings(csv_str, ",", 0, NULL); - SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); - smartlist_free(csv_str); - } else - result->value = tor_strdup(""); - break; - case CONFIG_TYPE_OBSOLETE: - log_fn(LOG_INFO, LD_CONFIG, - "You asked me for the value of an obsolete config option '%s'.", - key); - tor_free(result->key); - tor_free(result); - return NULL; - case CONFIG_TYPE_LINELIST_S: - log_warn(LD_CONFIG, - "Can't return context-sensitive '%s' on its own", key); - tor_free(result->key); - tor_free(result); - return NULL; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_V: - tor_free(result->key); - tor_free(result); - result = config_lines_dup(*(const config_line_t**)value); - break; - default: - tor_free(result->key); - tor_free(result); - log_warn(LD_BUG,"Unknown type %d for known key '%s'", - var->type, key); - return NULL; - } - - if (escape_val) { - config_line_t *line; - for (line = result; line; line = line->next) { - if (line->value && config_value_needs_escape(line->value)) { - char *newval = esc_for_log(line->value); - tor_free(line->value); - line->value = newval; - } - } - } - - return result; -} -/** Iterate through the linked list of requested options list. - * For each item, convert as appropriate and assign to options. - * If an item is unrecognized, set *msg and return -1 immediately, - * else return 0 for success. - * - * If clear_first, interpret config options as replacing (not - * extending) their previous values. If clear_first is set, - * then use_defaults to decide if you set to defaults after - * clearing, or make the value 0 or NULL. - * - * Here are the use cases: - * 1. A non-empty AllowInvalid line in your torrc. Appends to current - * if linelist, replaces current if csv. - * 2. An empty AllowInvalid line in your torrc. Should clear it. - * 3. "RESETCONF AllowInvalid" sets it to default. - * 4. "SETCONF AllowInvalid" makes it NULL. - * 5. "SETCONF AllowInvalid=foo" clears it and sets it to "foo". - * - * Use_defaults Clear_first - * 0 0 "append" - * 1 0 undefined, don't use - * 0 1 "set to null first" - * 1 1 "set to defaults first" - * Return 0 on success, -1 on bad key, -2 on bad value. - * - * As an additional special case, if a LINELIST config option has - * no value and clear_first is 0, then warn and ignore it. - */ - -/* -There are three call cases for config_assign() currently. - -Case one: Torrc entry -options_init_from_torrc() calls config_assign(0, 0) - calls config_assign_line(0, 0). - if value is empty, calls config_reset(0) and returns. - calls config_assign_value(), appends. - -Case two: setconf -options_trial_assign() calls config_assign(0, 1) - calls config_reset_line(0) - calls config_reset(0) - calls option_clear(). - calls config_assign_line(0, 1). - if value is empty, returns. - calls config_assign_value(), appends. - -Case three: resetconf -options_trial_assign() calls config_assign(1, 1) - calls config_reset_line(1) - calls config_reset(1) - calls option_clear(). - calls config_assign_value(default) - calls config_assign_line(1, 1). - returns. -*/ -int -config_assign(const config_format_t *fmt, void *options, config_line_t *list, - int use_defaults, int clear_first, char **msg) -{ - config_line_t *p; - bitarray_t *options_seen; - const int n_options = config_count_options(fmt); - - CONFIG_CHECK(fmt, options); - - /* pass 1: normalize keys */ - for (p = list; p; p = p->next) { - const char *full = config_expand_abbrev(fmt, p->key, 0, 1); - if (strcmp(full,p->key)) { - tor_free(p->key); - p->key = tor_strdup(full); - } - } - - /* pass 2: if we're reading from a resetting source, clear all - * mentioned config options, and maybe set to their defaults. */ - if (clear_first) { - for (p = list; p; p = p->next) - config_reset_line(fmt, options, p->key, use_defaults); - } - - options_seen = bitarray_init_zero(n_options); - /* pass 3: assign. */ - while (list) { - int r; - if ((r=config_assign_line(fmt, options, list, use_defaults, - clear_first, options_seen, msg))) { - bitarray_free(options_seen); - return r; - } - list = list->next; - } - bitarray_free(options_seen); - - /** Now we're done assigning a group of options to the configuration. - * Subsequent group assignments should _replace_ linelists, not extend - * them. */ - config_mark_lists_fragile(fmt, options); - - return 0; -} - -/** Reset config option var to 0, 0.0, NULL, or the equivalent. - * Called from config_reset() and config_free(). */ -static void -config_clear(const config_format_t *fmt, void *options, - const config_var_t *var) -{ - void *lvalue = STRUCT_VAR_P(options, var->var_offset); - (void)fmt; /* unused */ - switch (var->type) { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char**)lvalue); - break; - case CONFIG_TYPE_DOUBLE: - *(double*)lvalue = 0.0; - break; - case CONFIG_TYPE_ISOTIME: - *(time_t*)lvalue = 0; - break; - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - case CONFIG_TYPE_PORT: - case CONFIG_TYPE_BOOL: - *(int*)lvalue = 0; - break; - case CONFIG_TYPE_AUTOBOOL: - *(int*)lvalue = -1; - break; - case CONFIG_TYPE_MEMUNIT: - *(uint64_t*)lvalue = 0; - break; - case CONFIG_TYPE_ROUTERSET: - if (*(routerset_t**)lvalue) { - routerset_free(*(routerset_t**)lvalue); - *(routerset_t**)lvalue = NULL; - } - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; - } - break; - case CONFIG_TYPE_CSV_INTERVAL: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, int *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; - } - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - config_free_lines(*(config_line_t **)lvalue); - *(config_line_t **)lvalue = NULL; - break; - case CONFIG_TYPE_LINELIST_V: - /* handled by linelist_s. */ - break; - case CONFIG_TYPE_OBSOLETE: - break; - } -} - -/** Clear the option indexed by var in options. Then if - * use_defaults, set it to its default value. - * Called by config_init() and option_reset_line() and option_assign_line(). */ -static void -config_reset(const config_format_t *fmt, void *options, - const config_var_t *var, int use_defaults) -{ - config_line_t *c; - char *msg = NULL; - CONFIG_CHECK(fmt, options); - config_clear(fmt, options, var); /* clear it first */ - if (!use_defaults) - return; /* all done */ - if (var->initvalue) { - c = tor_malloc_zero(sizeof(config_line_t)); - c->key = tor_strdup(var->name); - c->value = tor_strdup(var->initvalue); - if (config_assign_value(fmt, options, c, &msg) < 0) { - log_warn(LD_BUG, "Failed to assign default: %s", msg); - tor_free(msg); /* if this happens it's a bug */ - } - config_free_lines(c); - } -} - -/** Release storage held by options. */ -void -config_free(const config_format_t *fmt, void *options) -{ - int i; - - if (!options) - return; - - tor_assert(fmt); - - for (i=0; fmt->vars[i].name; ++i) - config_clear(fmt, options, &(fmt->vars[i])); - if (fmt->extra) { - config_line_t **linep = STRUCT_VAR_P(options, fmt->extra->var_offset); - config_free_lines(*linep); - *linep = NULL; - } - tor_free(options); -} - -/** Return true iff a and b contain identical keys and values in identical - * order. */ -int -config_lines_eq(config_line_t *a, config_line_t *b) -{ - while (a && b) { - if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value)) - return 0; - a = a->next; - b = b->next; - } - if (a || b) - return 0; - return 1; -} - -/** Return the number of lines in a whose key is key. */ -int -config_count_key(const config_line_t *a, const char *key) -{ - int n = 0; - while (a) { - if (!strcasecmp(a->key, key)) { - ++n; - } - a = a->next; - } - return n; -} - -/** Return true iff the option name has the same value in o1 - * and o2. Must not be called for LINELIST_S or OBSOLETE options. - */ -int -config_is_same(const config_format_t *fmt, - const void *o1, const void *o2, - const char *name) -{ - config_line_t *c1, *c2; - int r = 1; - CONFIG_CHECK(fmt, o1); - CONFIG_CHECK(fmt, o2); - - c1 = config_get_assigned_option(fmt, o1, name, 0); - c2 = config_get_assigned_option(fmt, o2, name, 0); - r = config_lines_eq(c1, c2); - config_free_lines(c1); - config_free_lines(c2); - return r; -} - -/** Copy storage held by old into a new or_options_t and return it. */ -void * -config_dup(const config_format_t *fmt, const void *old) -{ - void *newopts; - int i; - config_line_t *line; - - newopts = config_new(fmt); - for (i=0; fmt->vars[i].name; ++i) { - if (fmt->vars[i].type == CONFIG_TYPE_LINELIST_S) - continue; - if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE) - continue; - line = config_get_assigned_option(fmt, old, fmt->vars[i].name, 0); - if (line) { - char *msg = NULL; - if (config_assign(fmt, newopts, line, 0, 0, &msg) < 0) { - log_err(LD_BUG, "config_get_assigned_option() generated " - "something we couldn't config_assign(): %s", msg); - tor_free(msg); - tor_assert(0); - } - } - config_free_lines(line); - } - return newopts; -} -/** Set all vars in the configuration object options to their default - * values. */ -void -config_init(const config_format_t *fmt, void *options) -{ - int i; - const config_var_t *var; - CONFIG_CHECK(fmt, options); - - for (i=0; fmt->vars[i].name; ++i) { - var = &fmt->vars[i]; - if (!var->initvalue) - continue; /* defaults to NULL or 0 */ - config_reset(fmt, options, var, 1); - } -} - -/** Allocate and return a new string holding the written-out values of the vars - * in 'options'. If 'minimal', do not write out any default-valued vars. - * Else, if comment_defaults, write default values as comments. - */ -char * -config_dump(const config_format_t *fmt, const void *default_options, - const void *options, int minimal, - int comment_defaults) -{ - smartlist_t *elements; - const void *defaults = default_options; - void *defaults_tmp = NULL; - config_line_t *line, *assigned; - char *result; - int i; - char *msg = NULL; - - if (defaults == NULL) { - defaults = defaults_tmp = config_new(fmt); - config_init(fmt, defaults_tmp); - } - - /* XXX use a 1 here so we don't add a new log line while dumping */ - if (default_options == NULL) { - if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config: %s", msg); - tor_free(msg); - tor_assert(0); - } - } - - elements = smartlist_new(); - for (i=0; fmt->vars[i].name; ++i) { - int comment_option = 0; - if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE || - fmt->vars[i].type == CONFIG_TYPE_LINELIST_S) - continue; - /* Don't save 'hidden' control variables. */ - if (!strcmpstart(fmt->vars[i].name, "__")) - continue; - if (minimal && config_is_same(fmt, options, defaults, fmt->vars[i].name)) - continue; - else if (comment_defaults && - config_is_same(fmt, options, defaults, fmt->vars[i].name)) - comment_option = 1; - - line = assigned = - config_get_assigned_option(fmt, options, fmt->vars[i].name, 1); - - for (; line; line = line->next) { - smartlist_add_asprintf(elements, "%s%s %s\n", - comment_option ? "# " : "", - line->key, line->value); - } - config_free_lines(assigned); - } - - if (fmt->extra) { - line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset); - for (; line; line = line->next) { - smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value); - } - } - - result = smartlist_join_strings(elements, "", 0, NULL); - SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); - smartlist_free(elements); - if (defaults_tmp) - config_free(fmt, defaults_tmp); - return result; -} - -/** Mapping from a unit name to a multiplier for converting that unit into a - * base unit. Used by config_parse_unit. */ -struct unit_table_t { - const char *unit; /**< The name of the unit */ - uint64_t multiplier; /**< How many of the base unit appear in this unit */ -}; - -/** Table to map the names of memory units to the number of bytes they - * contain. */ -static struct unit_table_t memory_units[] = { - { "", 1 }, - { "b", 1<< 0 }, - { "byte", 1<< 0 }, - { "bytes", 1<< 0 }, - { "kb", 1<<10 }, - { "kbyte", 1<<10 }, - { "kbytes", 1<<10 }, - { "kilobyte", 1<<10 }, - { "kilobytes", 1<<10 }, - { "kilobits", 1<<7 }, - { "kilobit", 1<<7 }, - { "kbits", 1<<7 }, - { "kbit", 1<<7 }, - { "m", 1<<20 }, - { "mb", 1<<20 }, - { "mbyte", 1<<20 }, - { "mbytes", 1<<20 }, - { "megabyte", 1<<20 }, - { "megabytes", 1<<20 }, - { "megabits", 1<<17 }, - { "megabit", 1<<17 }, - { "mbits", 1<<17 }, - { "mbit", 1<<17 }, - { "gb", 1<<30 }, - { "gbyte", 1<<30 }, - { "gbytes", 1<<30 }, - { "gigabyte", 1<<30 }, - { "gigabytes", 1<<30 }, - { "gigabits", 1<<27 }, - { "gigabit", 1<<27 }, - { "gbits", 1<<27 }, - { "gbit", 1<<27 }, - { "tb", U64_LITERAL(1)<<40 }, - { "terabyte", U64_LITERAL(1)<<40 }, - { "terabytes", U64_LITERAL(1)<<40 }, - { "terabits", U64_LITERAL(1)<<37 }, - { "terabit", U64_LITERAL(1)<<37 }, - { "tbits", U64_LITERAL(1)<<37 }, - { "tbit", U64_LITERAL(1)<<37 }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of seconds they - * contain. */ -static struct unit_table_t time_units[] = { - { "", 1 }, - { "second", 1 }, - { "seconds", 1 }, - { "minute", 60 }, - { "minutes", 60 }, - { "hour", 60*60 }, - { "hours", 60*60 }, - { "day", 24*60*60 }, - { "days", 24*60*60 }, - { "week", 7*24*60*60 }, - { "weeks", 7*24*60*60 }, - { "month", 2629728, }, /* about 30.437 days */ - { "months", 2629728, }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of milliseconds - * they contain. */ -static struct unit_table_t time_msec_units[] = { - { "", 1 }, - { "msec", 1 }, - { "millisecond", 1 }, - { "milliseconds", 1 }, - { "second", 1000 }, - { "seconds", 1000 }, - { "minute", 60*1000 }, - { "minutes", 60*1000 }, - { "hour", 60*60*1000 }, - { "hours", 60*60*1000 }, - { "day", 24*60*60*1000 }, - { "days", 24*60*60*1000 }, - { "week", 7*24*60*60*1000 }, - { "weeks", 7*24*60*60*1000 }, - { NULL, 0 }, -}; - -/** Parse a string val containing a number, zero or more - * spaces, and an optional unit string. If the unit appears in the - * table u, then multiply the number by the unit multiplier. - * On success, set *ok to 1 and return this product. - * Otherwise, set *ok to 0. - */ -static uint64_t -config_parse_units(const char *val, struct unit_table_t *u, int *ok) -{ - uint64_t v = 0; - double d = 0; - int use_float = 0; - char *cp; - - tor_assert(ok); - - v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); - if (!*ok || (cp && *cp == '.')) { - d = tor_parse_double(val, 0, UINT64_MAX, ok, &cp); - if (!*ok) - goto done; - use_float = 1; - } - - if (!cp) { - *ok = 1; - v = use_float ? DBL_TO_U64(d) : v; - goto done; - } - - cp = (char*) eat_whitespace(cp); - - for ( ;u->unit;++u) { - if (!strcasecmp(u->unit, cp)) { - if (use_float) - v = u->multiplier * d; - else - v *= u->multiplier; - *ok = 1; - goto done; - } - } - log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); - *ok = 0; - done: - - if (*ok) - return v; - else - return 0; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * information (byte, KB, M, etc). On success, set *ok to true - * and return the number of bytes specified. Otherwise, set - * *ok to false and return 0. */ -static uint64_t -config_parse_memunit(const char *s, int *ok) -{ - uint64_t u = config_parse_units(s, memory_units, ok); - return u; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * time in milliseconds. On success, set *ok to true and return - * the number of milliseconds in the provided interval. Otherwise, set - * *ok to 0 and return -1. */ -static int -config_parse_msec_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_msec_units, ok); - if (!ok) - return -1; - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} - -/** Parse a string in the format "number unit", where unit is a unit of time. - * On success, set *ok to true and return the number of seconds in - * the provided interval. Otherwise, set *ok to 0 and return -1. - */ -static int -config_parse_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_units, ok); - if (!ok) - return -1; - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} - diff --git a/src/tor/confparse.h b/src/tor/confparse.h deleted file mode 100644 index 2cd6c49..0000000 --- a/src/tor/confparse.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_CONFPARSE_H -#define TOR_CONFPARSE_H - -/** Enumeration of types which option values can take */ -typedef enum config_type_t { - CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ - CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ - CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ - CONFIG_TYPE_INT, /**< Any integer. */ - CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or - * "auto". */ - CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ - CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional - * units */ - CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ - CONFIG_TYPE_DOUBLE, /**< A floating-point value */ - CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ - CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, - * 1 for true, and -1 for auto */ - CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ - CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and - * optional whitespace. */ - CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and - * optional whitespace, representing intervals in - * seconds, with optional units */ - CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ - CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, - * mixed with other keywords. */ - CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize - * context-sensitive config lines when fetching. - */ - CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, - * parsed into a routerset_t. */ - CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ -} config_type_t; - -/** An abbreviation for a configuration option allowed on the command line. */ -typedef struct config_abbrev_t { - const char *abbreviated; - const char *full; - int commandline_only; - int warn; -} config_abbrev_t; - -/* Handy macro for declaring "In the config file or on the command line, - * you can abbreviate toks as tok". */ -#define PLURAL(tok) { #tok, #tok "s", 0, 0 } - -/** A variable allowed in the configuration file or on the command line. */ -typedef struct config_var_t { - const char *name; /**< The full keyword (case insensitive). */ - config_type_t type; /**< How to interpret the type and turn it into a - * value. */ - off_t var_offset; /**< Offset of the corresponding member of or_options_t. */ - const char *initvalue; /**< String (or null) describing initial value. */ -} config_var_t; - -/** Represents an English description of a configuration variable; used when - * generating configuration file comments. */ -typedef struct config_var_description_t { - const char *name; - const char *description; -} config_var_description_t; - -/** Type of a callback to validate whether a given configuration is - * well-formed and consistent. See options_trial_assign() for documentation - * of arguments. */ -typedef int (*validate_fn_t)(void*,void*,void*,int,char**); - -/** Information on the keys, value types, key-to-struct-member mappings, - * variable descriptions, validation functions, and abbreviations for a - * configuration or storage format. */ -typedef struct config_format_t { - size_t size; /**< Size of the struct that everything gets parsed into. */ - uint32_t magic; /**< Required 'magic value' to make sure we have a struct - * of the right type. */ - off_t magic_offset; /**< Offset of the magic value within the struct. */ - config_abbrev_t *abbrevs; /**< List of abbreviations that we expand when - * parsing this format. */ - config_var_t *vars; /**< List of variables we recognize, their default - * values, and where we stick them in the structure. */ - validate_fn_t validate_fn; /**< Function to validate config. */ - /** If present, extra is a LINELIST variable for unrecognized - * lines. Otherwise, unrecognized lines are an error. */ - config_var_t *extra; -} config_format_t; - -/** Macro: assert that cfg has the right magic field for format - * fmt. */ -#define CONFIG_CHECK(fmt, cfg) STMT_BEGIN \ - tor_assert(fmt && cfg); \ - tor_assert((fmt)->magic == \ - *(uint32_t*)STRUCT_VAR_P(cfg,fmt->magic_offset)); \ - STMT_END - -void *config_new(const config_format_t *fmt); -void config_line_append(config_line_t **lst, - const char *key, const char *val); -config_line_t *config_lines_dup(const config_line_t *inp); -const config_line_t *config_line_find(const config_line_t *lines, - const char *key); -void config_free(const config_format_t *fmt, void *options); -int config_lines_eq(config_line_t *a, config_line_t *b); -int config_count_key(const config_line_t *a, const char *key); -config_line_t *config_get_assigned_option(const config_format_t *fmt, - const void *options, const char *key, - int escape_val); -int config_is_same(const config_format_t *fmt, - const void *o1, const void *o2, - const char *name); -void config_init(const config_format_t *fmt, void *options); -void *config_dup(const config_format_t *fmt, const void *old); -char *config_dump(const config_format_t *fmt, const void *default_options, - const void *options, int minimal, - int comment_defaults); -int config_assign(const config_format_t *fmt, void *options, - config_line_t *list, - int use_defaults, int clear_first, char **msg); -config_var_t *config_find_option_mutable(config_format_t *fmt, - const char *key); -const config_var_t *config_find_option(const config_format_t *fmt, - const char *key); - -int config_get_lines(const char *string, config_line_t **result, int extended); -void config_free_lines(config_line_t *front); -const char *config_expand_abbrev(const config_format_t *fmt, - const char *option, - int command_line, int warn_obsolete); - -#endif - diff --git a/src/tor/connection.c b/src/tor/connection.c deleted file mode 100644 index ebf679e..0000000 --- a/src/tor/connection.c +++ /dev/null @@ -1,4850 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection.c - * \brief General high-level functions to handle reading and writing - * on connections. - **/ - -#define CONNECTION_PRIVATE -#include "or.h" -#include "buffers.h" -/* - * Define this so we get channel internal functions, since we're implementing - * part of a subclass (channel_tls_t). - */ -#define TOR_CHANNEL_INTERNAL_ -#define CONNECTION_PRIVATE -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "cpuworker.h" -#include "directory.h" -#include "dirserv.h" -#include "dns.h" -#include "dnsserv.h" -#include "entrynodes.h" -#include "ext_orport.h" -#include "geoip.h" -#include "onion_main.h" -#include "policies.h" -#include "reasons.h" -#include "relay.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rephist.h" -#include "router.h" -#include "transports.h" -#include "routerparse.h" -#include "transports.h" - -#ifdef USE_BUFFEREVENTS -#include -#endif - -#ifdef HAVE_PWD_H -#include -#endif - -static connection_t *connection_listener_new( - const struct sockaddr *listensockaddr, - socklen_t listensocklen, int type, - const char *address, - const port_cfg_t *portcfg); -static void connection_init(time_t now, connection_t *conn, int type, - int socket_family); -static int connection_init_accepted_conn(connection_t *conn, - const listener_connection_t *listener); -static int connection_handle_listener_read(connection_t *conn, int new_type); -#ifndef USE_BUFFEREVENTS -static int connection_bucket_should_increase(int bucket, - or_connection_t *conn); -#endif -static int connection_finished_flushing(connection_t *conn); -static int connection_flushed_some(connection_t *conn); -static int connection_finished_connecting(connection_t *conn); -static int connection_reached_eof(connection_t *conn); -static int connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, - int *socket_error); -static int connection_process_inbuf(connection_t *conn, int package_partial); -static void client_check_address_changed(tor_socket_t sock); -static void set_constrained_socket_buffers(tor_socket_t sock, int size); - -static const char *connection_proxy_state_to_string(int state); -static int connection_read_https_proxy_response(connection_t *conn); -static void connection_send_socks5_connect(connection_t *conn); -static const char *proxy_type_to_string(int proxy_type); -static int get_proxy_type(void); - -/** The last addresses that our network interface seemed to have been - * binding to. We use this as one way to detect when our IP changes. - * - * XXX024 We should really use the entire list of interfaces here. - **/ -static tor_addr_t *last_interface_ipv4 = NULL; -/* DOCDOC last_interface_ipv6 */ -static tor_addr_t *last_interface_ipv6 = NULL; -/** A list of tor_addr_t for addresses we've used in outgoing connections. - * Used to detect IP address changes. */ -static smartlist_t *outgoing_addrs = NULL; - -#define CASE_ANY_LISTENER_TYPE \ - case CONN_TYPE_OR_LISTENER: \ - case CONN_TYPE_EXT_OR_LISTENER: \ - case CONN_TYPE_AP_LISTENER: \ - case CONN_TYPE_DIR_LISTENER: \ - case CONN_TYPE_CONTROL_LISTENER: \ - case CONN_TYPE_AP_TRANS_LISTENER: \ - case CONN_TYPE_AP_NATD_LISTENER: \ - case CONN_TYPE_AP_DNS_LISTENER - -/**************************************************************/ - -/** - * Return the human-readable name for the connection type type - */ -const char * -conn_type_to_string(int type) -{ - static char buf[64]; - switch (type) { - case CONN_TYPE_OR_LISTENER: return "OR listener"; - case CONN_TYPE_OR: return "OR"; - case CONN_TYPE_EXIT: return "Exit"; - case CONN_TYPE_AP_LISTENER: return "Socks listener"; - case CONN_TYPE_AP_TRANS_LISTENER: - return "Transparent pf/netfilter listener"; - case CONN_TYPE_AP_NATD_LISTENER: return "Transparent natd listener"; - case CONN_TYPE_AP_DNS_LISTENER: return "DNS listener"; - case CONN_TYPE_AP: return "Socks"; - case CONN_TYPE_DIR_LISTENER: return "Directory listener"; - case CONN_TYPE_DIR: return "Directory"; - case CONN_TYPE_CPUWORKER: return "CPU worker"; - case CONN_TYPE_CONTROL_LISTENER: return "Control listener"; - case CONN_TYPE_CONTROL: return "Control"; - case CONN_TYPE_EXT_OR: return "Extended OR"; - case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener"; - default: - log_warn(LD_BUG, "unknown connection type %d", type); - tor_snprintf(buf, sizeof(buf), "unknown [%d]", type); - return buf; - } -} - -/** - * Return the human-readable name for the connection state state - * for the connection type type - */ -const char * -conn_state_to_string(int type, int state) -{ - static char buf[96]; - switch (type) { - CASE_ANY_LISTENER_TYPE: - if (state == LISTENER_STATE_READY) - return "ready"; - break; - case CONN_TYPE_OR: - switch (state) { - case OR_CONN_STATE_CONNECTING: return "connect()ing"; - case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)"; - case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)"; - case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING: - return "renegotiating (TLS, v2 handshake)"; - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - return "waiting for renegotiation or V3 handshake"; - case OR_CONN_STATE_OR_HANDSHAKING_V2: - return "handshaking (Tor, v2 handshake)"; - case OR_CONN_STATE_OR_HANDSHAKING_V3: - return "handshaking (Tor, v3 handshake)"; - case OR_CONN_STATE_OPEN: return "open"; - } - break; - case CONN_TYPE_EXT_OR: - switch (state) { - case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: - return "waiting for authentication type"; - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: - return "waiting for client nonce"; - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: - return "waiting for client hash"; - case EXT_OR_CONN_STATE_OPEN: return "open"; - case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY"; - } - break; - case CONN_TYPE_EXIT: - switch (state) { - case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info"; - case EXIT_CONN_STATE_CONNECTING: return "connecting"; - case EXIT_CONN_STATE_OPEN: return "open"; - case EXIT_CONN_STATE_RESOLVEFAILED: return "resolve failed"; - } - break; - case CONN_TYPE_AP: - switch (state) { - case AP_CONN_STATE_SOCKS_WAIT: return "waiting for socks info"; - case AP_CONN_STATE_NATD_WAIT: return "waiting for natd dest info"; - case AP_CONN_STATE_RENDDESC_WAIT: return "waiting for rendezvous desc"; - case AP_CONN_STATE_CONTROLLER_WAIT: return "waiting for controller"; - case AP_CONN_STATE_CIRCUIT_WAIT: return "waiting for circuit"; - case AP_CONN_STATE_CONNECT_WAIT: return "waiting for connect response"; - case AP_CONN_STATE_RESOLVE_WAIT: return "waiting for resolve response"; - case AP_CONN_STATE_OPEN: return "open"; - } - break; - case CONN_TYPE_DIR: - switch (state) { - case DIR_CONN_STATE_CONNECTING: return "connecting"; - case DIR_CONN_STATE_CLIENT_SENDING: return "client sending"; - case DIR_CONN_STATE_CLIENT_READING: return "client reading"; - case DIR_CONN_STATE_CLIENT_FINISHED: return "client finished"; - case DIR_CONN_STATE_SERVER_COMMAND_WAIT: return "waiting for command"; - case DIR_CONN_STATE_SERVER_WRITING: return "writing"; - } - break; - case CONN_TYPE_CPUWORKER: - switch (state) { - case CPUWORKER_STATE_IDLE: return "idle"; - case CPUWORKER_STATE_BUSY_ONION: return "busy with onion"; - } - break; - case CONN_TYPE_CONTROL: - switch (state) { - case CONTROL_CONN_STATE_OPEN: return "open (protocol v1)"; - case CONTROL_CONN_STATE_NEEDAUTH: - return "waiting for authentication (protocol v1)"; - } - break; - } - - log_warn(LD_BUG, "unknown connection state %d (type %d)", state, type); - tor_snprintf(buf, sizeof(buf), - "unknown state [%d] on unknown [%s] connection", - state, conn_type_to_string(type)); - return buf; -} - -#ifdef USE_BUFFEREVENTS -/** Return true iff the connection's type is one that can use a - bufferevent-based implementation. */ -int -connection_type_uses_bufferevent(connection_t *conn) -{ - switch (conn->type) { - case CONN_TYPE_AP: - case CONN_TYPE_EXIT: - case CONN_TYPE_DIR: - case CONN_TYPE_CONTROL: - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - case CONN_TYPE_CPUWORKER: - return 1; - default: - return 0; - } -} -#endif - -/** Allocate and return a new dir_connection_t, initialized as by - * connection_init(). */ -dir_connection_t * -dir_connection_new(int socket_family) -{ - dir_connection_t *dir_conn = tor_malloc_zero(sizeof(dir_connection_t)); - connection_init(time(NULL), TO_CONN(dir_conn), CONN_TYPE_DIR, socket_family); - return dir_conn; -} - -/** Allocate and return a new or_connection_t, initialized as by - * 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 type, int socket_family) -{ - or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t)); - time_t now = time(NULL); - tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR); - connection_init(now, TO_CONN(or_conn), type, socket_family); - - or_conn->timestamp_last_added_nonpadding = time(NULL); - - if (type == CONN_TYPE_EXT_OR) - connection_or_set_ext_or_identifier(or_conn); - - return or_conn; -} - -/** Allocate and return a new entry_connection_t, initialized as by - * connection_init(). - * - * Allocate space to store the socks_request. - */ -entry_connection_t * -entry_connection_new(int type, int socket_family) -{ - entry_connection_t *entry_conn = tor_malloc_zero(sizeof(entry_connection_t)); - tor_assert(type == CONN_TYPE_AP); - connection_init(time(NULL), ENTRY_TO_CONN(entry_conn), type, socket_family); - entry_conn->socks_request = socks_request_new(); - /* If this is coming from a listener, we'll set it up based on the listener - * in a little while. Otherwise, we're doing this as a linked connection - * of some kind, and we should set it up here based on the socket family */ - if (socket_family == AF_INET) - entry_conn->ipv4_traffic_ok = 1; - else if (socket_family == AF_INET6) - entry_conn->ipv6_traffic_ok = 1; - return entry_conn; -} - -/** Allocate and return a new edge_connection_t, initialized as by - * connection_init(). */ -edge_connection_t * -edge_connection_new(int type, int socket_family) -{ - edge_connection_t *edge_conn = tor_malloc_zero(sizeof(edge_connection_t)); - tor_assert(type == CONN_TYPE_EXIT); - connection_init(time(NULL), TO_CONN(edge_conn), type, socket_family); - return edge_conn; -} - -/** Allocate and return a new control_connection_t, initialized as by - * connection_init(). */ -control_connection_t * -control_connection_new(int socket_family) -{ - control_connection_t *control_conn = - tor_malloc_zero(sizeof(control_connection_t)); - connection_init(time(NULL), - TO_CONN(control_conn), CONN_TYPE_CONTROL, socket_family); - return control_conn; -} - -/** Allocate and return a new listener_connection_t, initialized as by - * connection_init(). */ -listener_connection_t * -listener_connection_new(int type, int socket_family) -{ - listener_connection_t *listener_conn = - tor_malloc_zero(sizeof(listener_connection_t)); - connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family); - return listener_conn; -} - -/** Allocate, initialize, and return a new connection_t subtype of type - * to make or receive connections of address family socket_family. The - * type should be one of the CONN_TYPE_* constants. */ -connection_t * -connection_new(int type, int socket_family) -{ - switch (type) { - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - return TO_CONN(or_connection_new(type, socket_family)); - - case CONN_TYPE_EXIT: - return TO_CONN(edge_connection_new(type, socket_family)); - - case CONN_TYPE_AP: - return ENTRY_TO_CONN(entry_connection_new(type, socket_family)); - - case CONN_TYPE_DIR: - return TO_CONN(dir_connection_new(socket_family)); - - case CONN_TYPE_CONTROL: - return TO_CONN(control_connection_new(socket_family)); - - CASE_ANY_LISTENER_TYPE: - return TO_CONN(listener_connection_new(type, socket_family)); - - default: { - connection_t *conn = tor_malloc_zero(sizeof(connection_t)); - connection_init(time(NULL), conn, type, socket_family); - return conn; - } - } -} - -/** 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 type. Set conn-\>s and conn-\>conn_array_index to - * -1 to signify they are not yet assigned. - * - * Initialize conn's timestamps to now. - */ -static void -connection_init(time_t now, connection_t *conn, int type, int socket_family) -{ - static uint64_t n_connections_allocated = 1; - - switch (type) { - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - conn->magic = OR_CONNECTION_MAGIC; - break; - case CONN_TYPE_EXIT: - conn->magic = EDGE_CONNECTION_MAGIC; - break; - case CONN_TYPE_AP: - conn->magic = ENTRY_CONNECTION_MAGIC; - break; - case CONN_TYPE_DIR: - conn->magic = DIR_CONNECTION_MAGIC; - break; - case CONN_TYPE_CONTROL: - conn->magic = CONTROL_CONNECTION_MAGIC; - break; - CASE_ANY_LISTENER_TYPE: - conn->magic = LISTENER_CONNECTION_MAGIC; - break; - default: - conn->magic = BASE_CONNECTION_MAGIC; - break; - } - - conn->s = TOR_INVALID_SOCKET; /* give it a default of 'not used' */ - conn->conn_array_index = -1; /* also default to 'not used' */ - conn->global_identifier = n_connections_allocated++; - - conn->type = type; - conn->socket_family = socket_family; -#ifndef USE_BUFFEREVENTS - if (!connection_is_listener(conn)) { - /* listeners never use their buf */ - conn->inbuf = buf_new(); - conn->outbuf = buf_new(); - } -#endif - - conn->timestamp_created = now; - conn->timestamp_lastread = now; - conn->timestamp_lastwritten = now; -} - -/** Create a link between conn_a and conn_b. */ -void -connection_link_connections(connection_t *conn_a, connection_t *conn_b) -{ - tor_assert(! SOCKET_OK(conn_a->s)); - tor_assert(! SOCKET_OK(conn_b->s)); - - conn_a->linked = 1; - conn_b->linked = 1; - conn_a->linked_conn = conn_b; - conn_b->linked_conn = conn_a; -} - -/** Deallocate memory used by conn. Deallocate its buffers if - * necessary, close its socket if necessary, and mark the directory as dirty - * if conn is an OR or OP connection. - */ -STATIC void -connection_free_(connection_t *conn) -{ - void *mem; - size_t memlen; - if (!conn) - return; - - switch (conn->type) { - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - tor_assert(conn->magic == OR_CONNECTION_MAGIC); - mem = TO_OR_CONN(conn); - memlen = sizeof(or_connection_t); - break; - case CONN_TYPE_AP: - tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC); - mem = TO_ENTRY_CONN(conn); - memlen = sizeof(entry_connection_t); - break; - case CONN_TYPE_EXIT: - tor_assert(conn->magic == EDGE_CONNECTION_MAGIC); - mem = TO_EDGE_CONN(conn); - memlen = sizeof(edge_connection_t); - break; - case CONN_TYPE_DIR: - tor_assert(conn->magic == DIR_CONNECTION_MAGIC); - mem = TO_DIR_CONN(conn); - memlen = sizeof(dir_connection_t); - break; - case CONN_TYPE_CONTROL: - tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC); - mem = TO_CONTROL_CONN(conn); - memlen = sizeof(control_connection_t); - break; - CASE_ANY_LISTENER_TYPE: - tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC); - mem = TO_LISTENER_CONN(conn); - memlen = sizeof(listener_connection_t); - break; - default: - tor_assert(conn->magic == BASE_CONNECTION_MAGIC); - mem = conn; - memlen = sizeof(connection_t); - break; - } - - if (conn->linked) { - log_info(LD_GENERAL, "Freeing linked %s connection [%s] with %d " - "bytes on inbuf, %d on outbuf.", - conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state), - (int)connection_get_inbuf_len(conn), - (int)connection_get_outbuf_len(conn)); - } - - if (!connection_is_listener(conn)) { - buf_free(conn->inbuf); - buf_free(conn->outbuf); - } else { - if (conn->socket_family == AF_UNIX) { - /* For now only control ports can be Unix domain sockets - * and listeners at the same time */ - tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); - - if (unlink(conn->address) < 0 && errno != ENOENT) { - log_warn(LD_NET, "Could not unlink %s: %s", conn->address, - strerror(errno)); - } - } - } - - tor_free(conn->address); - - if (connection_speaks_cells(conn)) { - or_connection_t *or_conn = TO_OR_CONN(conn); - tor_tls_free(or_conn->tls); - or_conn->tls = NULL; - or_handshake_state_free(or_conn->handshake_state); - or_conn->handshake_state = NULL; - tor_free(or_conn->nickname); - if (or_conn->chan) { - /* Owww, this shouldn't happen, but... */ - log_info(LD_CHANNEL, - "Freeing orconn at %p, saw channel %p with ID " - U64_FORMAT " left un-NULLed", - or_conn, TLS_CHAN_TO_BASE(or_conn->chan), - U64_PRINTF_ARG( - TLS_CHAN_TO_BASE(or_conn->chan)->global_identifier)); - if (!(TLS_CHAN_TO_BASE(or_conn->chan)->state == CHANNEL_STATE_CLOSED || - TLS_CHAN_TO_BASE(or_conn->chan)->state == CHANNEL_STATE_ERROR)) { - channel_close_for_error(TLS_CHAN_TO_BASE(or_conn->chan)); - } - - or_conn->chan->conn = NULL; - or_conn->chan = NULL; - } - } - if (conn->type == CONN_TYPE_AP) { - entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); - tor_free(entry_conn->chosen_exit_name); - tor_free(entry_conn->original_dest_address); - if (entry_conn->socks_request) - socks_request_free(entry_conn->socks_request); - if (entry_conn->pending_optimistic_data) { - generic_buffer_free(entry_conn->pending_optimistic_data); - } - if (entry_conn->sending_optimistic_data) { - generic_buffer_free(entry_conn->sending_optimistic_data); - } - } - if (CONN_IS_EDGE(conn)) { - rend_data_free(TO_EDGE_CONN(conn)->rend_data); - } - if (conn->type == CONN_TYPE_CONTROL) { - control_connection_t *control_conn = TO_CONTROL_CONN(conn); - tor_free(control_conn->safecookie_client_hash); - tor_free(control_conn->incoming_cmd); - } - - tor_free(conn->read_event); /* Probably already freed by connection_free. */ - tor_free(conn->write_event); /* Probably already freed by connection_free. */ - IF_HAS_BUFFEREVENT(conn, { - /* This was a workaround to handle bugs in some old versions of libevent - * where callbacks can occur after calling bufferevent_free(). Setting - * the callbacks to NULL prevented this. It shouldn't be necessary any - * more, but let's not tempt fate for now. */ - bufferevent_setcb(conn->bufev, NULL, NULL, NULL, NULL); - bufferevent_free(conn->bufev); - conn->bufev = NULL; - }); - - if (conn->type == CONN_TYPE_DIR) { - dir_connection_t *dir_conn = TO_DIR_CONN(conn); - tor_free(dir_conn->requested_resource); - - tor_zlib_free(dir_conn->zlib_state); - if (dir_conn->fingerprint_stack) { - SMARTLIST_FOREACH(dir_conn->fingerprint_stack, char *, cp, tor_free(cp)); - smartlist_free(dir_conn->fingerprint_stack); - } - - cached_dir_decref(dir_conn->cached_dir); - rend_data_free(dir_conn->rend_data); - } - - if (SOCKET_OK(conn->s)) { - log_debug(LD_NET,"closing fd %d.",(int)conn->s); - tor_close_socket(conn->s); - conn->s = TOR_INVALID_SOCKET; - } - - if (conn->type == CONN_TYPE_OR && - !tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) { - log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest"); - connection_or_remove_from_identity_map(TO_OR_CONN(conn)); - } - if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) { - connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); - tor_free(TO_OR_CONN(conn)->ext_or_conn_id); - tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash); - tor_free(TO_OR_CONN(conn)->ext_or_transport); - } - -#ifdef USE_BUFFEREVENTS - if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) { - ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg); - TO_OR_CONN(conn)->bucket_cfg = NULL; - } -#endif - - memwipe(mem, 0xCC, memlen); /* poison memory */ - tor_free(mem); -} - -/** Make sure conn isn't in any of the global conn lists; then free it. - */ -void -connection_free(connection_t *conn) -{ - if (!conn) - return; - tor_assert(!connection_is_on_closeable_list(conn)); - tor_assert(!connection_in_array(conn)); - if (conn->linked_conn) { - log_err(LD_BUG, "Called with conn->linked_conn still set."); - tor_fragile_assert(); - conn->linked_conn->linked_conn = NULL; - if (! conn->linked_conn->marked_for_close && - conn->linked_conn->reading_from_linked_conn) - connection_start_reading(conn->linked_conn); - conn->linked_conn = NULL; - } - if (connection_speaks_cells(conn)) { - if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) { - connection_or_remove_from_identity_map(TO_OR_CONN(conn)); - } - } - if (conn->type == CONN_TYPE_CONTROL) { - connection_control_closed(TO_CONTROL_CONN(conn)); - } - connection_unregister_events(conn); - connection_free_(conn); -} - -/** - * Called when we're about to finally unlink and free a connection: - * perform necessary accounting and cleanup - * - Directory conns that failed to fetch a rendezvous descriptor - * need to inform pending rendezvous streams. - * - OR conns need to call rep_hist_note_*() to record status. - * - AP conns need to send a socks reject if necessary. - * - Exit conns need to call connection_dns_remove() if necessary. - * - AP and Exit conns need to send an end cell if they can. - * - DNS conns need to fail any resolves that are pending on them. - * - OR and edge connections need to be unlinked from circuits. - */ -void -connection_about_to_close_connection(connection_t *conn) -{ - tor_assert(conn->marked_for_close); - - switch (conn->type) { - case CONN_TYPE_DIR: - connection_dir_about_to_close(TO_DIR_CONN(conn)); - break; - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - connection_or_about_to_close(TO_OR_CONN(conn)); - break; - case CONN_TYPE_AP: - connection_ap_about_to_close(TO_ENTRY_CONN(conn)); - break; - case CONN_TYPE_EXIT: - connection_exit_about_to_close(TO_EDGE_CONN(conn)); - break; - } -} - -/** Return true iff connection_close_immediate() has been called on this - * connection. */ -#define CONN_IS_CLOSED(c) \ - ((c)->linked ? ((c)->linked_conn_is_closed) : (! SOCKET_OK(c->s))) - -/** Close the underlying socket for conn, so we don't try to - * flush it. Must be used in conjunction with (right before) - * connection_mark_for_close(). - */ -void -connection_close_immediate(connection_t *conn) -{ - assert_connection_ok(conn,0); - if (CONN_IS_CLOSED(conn)) { - log_err(LD_BUG,"Attempt to close already-closed connection."); - tor_fragile_assert(); - return; - } - if (conn->outbuf_flushlen) { - log_info(LD_NET,"fd %d, type %s, state %s, %d bytes on outbuf.", - (int)conn->s, conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state), - (int)conn->outbuf_flushlen); - } - - connection_unregister_events(conn); - - if (SOCKET_OK(conn->s)) - tor_close_socket(conn->s); - conn->s = TOR_INVALID_SOCKET; - if (conn->linked) - conn->linked_conn_is_closed = 1; - if (conn->outbuf) - buf_clear(conn->outbuf); - conn->outbuf_flushlen = 0; -} - -/** Mark conn to be closed next time we loop through - * conn_close_if_marked() in onion_main.c. */ -void -connection_mark_for_close_(connection_t *conn, int line, const char *file) -{ - assert_connection_ok(conn,0); - tor_assert(line); - tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */ - tor_assert(file); - - if (conn->type == CONN_TYPE_OR) { - /* - * An or_connection should have been closed through one of the channel- - * aware functions in connection_or.c. We'll assume this is an error - * close and do that, and log a bug warning. - */ - log_warn(LD_CHANNEL | LD_BUG, - "Something tried to close an or_connection_t without going " - "through channels at %s:%d", - file, line); - connection_or_close_for_error(TO_OR_CONN(conn), 0); - } else { - /* Pass it down to the real function */ - connection_mark_for_close_internal_(conn, line, file); - } -} - -/** Mark conn to be closed next time we loop through - * conn_close_if_marked() in onion_main.c; the _internal version bypasses the - * CONN_TYPE_OR checks; this should be called when you either are sure that - * if this is an or_connection_t the controlling channel has been notified - * (e.g. with connection_or_notify_error()), or you actually are the - * connection_or_close_for_error() or connection_or_close_normally function. - * For all other cases, use connection_mark_and_flush() instead, which - * checks for or_connection_t properly, instead. See below. - */ -void -connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file) -{ - assert_connection_ok(conn,0); - tor_assert(line); - tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */ - tor_assert(file); - - if (conn->marked_for_close) { - log_warn(LD_BUG,"Duplicate call to connection_mark_for_close at %s:%d" - " (first at %s:%d)", file, line, conn->marked_for_close_file, - conn->marked_for_close); - tor_fragile_assert(); - return; - } - - if (conn->type == CONN_TYPE_OR) { - /* - * Bad news if this happens without telling the controlling channel; do - * this so we can find things that call this wrongly when the asserts hit. - */ - log_debug(LD_CHANNEL, - "Calling connection_mark_for_close_internal_() on an OR conn " - "at %s:%d", - file, line); - } - - conn->marked_for_close = line; - conn->marked_for_close_file = file; - add_connection_to_closeable_list(conn); - - /* in case we're going to be held-open-til-flushed, reset - * the number of seconds since last successful write, so - * we get our whole 15 seconds */ - conn->timestamp_lastwritten = time(NULL); -} - -/** Find each connection that has hold_open_until_flushed set to - * 1 but hasn't written in the past 15 seconds, and set - * hold_open_until_flushed to 0. This means it will get cleaned - * up in the next loop through close_if_marked() in onion_main.c. - */ -void -connection_expire_held_open(void) -{ - time_t now; - smartlist_t *conns = get_connection_array(); - - now = time(NULL); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - /* If we've been holding the connection open, but we haven't written - * for 15 seconds... - */ - if (conn->hold_open_until_flushed) { - tor_assert(conn->marked_for_close); - if (now - conn->timestamp_lastwritten >= 15) { - int severity; - if (conn->type == CONN_TYPE_EXIT || - (conn->type == CONN_TYPE_DIR && - conn->purpose == DIR_PURPOSE_SERVER)) - severity = LOG_INFO; - else - severity = LOG_NOTICE; - log_fn(severity, LD_NET, - "Giving up on marked_for_close conn that's been flushing " - "for 15s (fd %d, type %s, state %s).", - (int)conn->s, conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state)); - conn->hold_open_until_flushed = 0; - } - } - } SMARTLIST_FOREACH_END(conn); -} - -#if defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN) -/** Create an AF_UNIX listenaddr struct. - * listenaddress provides the path to the Unix socket. - * - * Eventually listenaddress will also optionally contain user, group, - * and file permissions for the new socket. But not yet. XXX - * Also, since we do not create the socket here the information doesn't help - * here. - * - * If not NULL readable_address will contain a copy of the path part of - * listenaddress. - * - * The listenaddr struct has to be freed by the caller. - */ -static struct sockaddr_un * -create_unix_sockaddr(const char *listenaddress, char **readable_address, - socklen_t *len_out) -{ - struct sockaddr_un *sockaddr = NULL; - - sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un)); - sockaddr->sun_family = AF_UNIX; - if (strlcpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path)) - >= sizeof(sockaddr->sun_path)) { - log_warn(LD_CONFIG, "Unix socket path '%s' is too long to fit.", - escaped(listenaddress)); - tor_free(sockaddr); - return NULL; - } - - if (readable_address) - *readable_address = tor_strdup(listenaddress); - - *len_out = sizeof(struct sockaddr_un); - return sockaddr; -} -#else -static struct sockaddr * -create_unix_sockaddr(const char *listenaddress, char **readable_address, - socklen_t *len_out) -{ - (void)listenaddress; - (void)readable_address; - log_fn(LOG_ERR, LD_BUG, - "Unix domain sockets not supported, yet we tried to create one."); - *len_out = 0; - tor_fragile_assert(); - return NULL; -} -#endif /* HAVE_SYS_UN_H */ - -/** Warn that an accept or a connect has failed because we're running up - * against our ulimit. Rate-limit these warnings so that we don't spam - * the log. */ -static void -warn_too_many_conns(void) -{ -#define WARN_TOO_MANY_CONNS_INTERVAL (6*60*60) - static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CONNS_INTERVAL); - char *m; - if ((m = rate_limit_log(&last_warned, approx_time()))) { - int n_conns = get_n_open_sockets(); - log_warn(LD_NET,"Failing because we have %d connections already. Please " - "raise your ulimit -n.%s", n_conns, m); - tor_free(m); - control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d", - n_conns); - } -} - -#ifdef HAVE_SYS_UN_H -/** Check whether we should be willing to open an AF_UNIX socket in - * path. Return 0 if we should go ahead and -1 if we shouldn't. */ -static int -check_location_for_unix_socket(const or_options_t *options, const char *path) -{ - int r = -1; - char *p = tor_strdup(path); - cpd_check_t flags = CPD_CHECK_MODE_ONLY; - if (get_parent_directory(p)<0 || p[0] != '/') { - log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " - "relative paths for unix sockets.", path); - goto done; - } - - if (options->ControlSocketsGroupWritable) - flags |= CPD_GROUP_OK; - - if (check_private_dir(p, flags, options->User) < 0) { - char *escpath, *escdir; - escpath = esc_for_log(path); - escdir = esc_for_log(p); - log_warn(LD_GENERAL, "Before Tor can create a control socket in %s, the " - "directory %s needs to exist, and to be accessible only by the " - "user%s account that is running Tor. (On some Unix systems, " - "anybody who can list a socket can connect to it, so Tor is " - "being careful.)", escpath, escdir, - options->ControlSocketsGroupWritable ? " and group" : ""); - tor_free(escpath); - tor_free(escdir); - goto done; - } - - r = 0; - done: - tor_free(p); - return r; -} -#endif - -/** Tell the TCP stack that it shouldn't wait for a long time after - * sock has closed before reusing its port. */ -static void -make_socket_reuseable(tor_socket_t sock) -{ -#ifdef _WIN32 - (void) sock; -#else - int one=1; - - /* REUSEADDR on normal places means you can rebind to the port - * right after somebody else has let it go. But REUSEADDR on win32 - * means you can bind to the port _even when somebody else - * already has it bound_. So, don't do that on Win32. */ - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, - (socklen_t)sizeof(one)) == -1) { - log_warn(LD_NET, "Error setting SO_REUSEADDR flag: %s", - tor_socket_strerror(errno)); - } -#endif -} - -/** Max backlog to pass to listen. We start at */ -static int listen_limit = INT_MAX; - -/* Listen on fd with appropriate backlog. Return as for listen. */ -static int -tor_listen(tor_socket_t fd) -{ - int r; - - if ((r = listen(fd, listen_limit)) < 0) { - if (listen_limit == SOMAXCONN) - return r; - if ((r = listen(fd, SOMAXCONN)) == 0) { - listen_limit = SOMAXCONN; - log_warn(LD_NET, "Setting listen backlog to INT_MAX connections " - "didn't work, but SOMAXCONN did. Lowering backlog limit."); - } - } - return r; -} - -/** Bind a new non-blocking socket listening to the socket described - * by listensockaddr. - * - * address is only used for logging purposes and to add the information - * to the conn. - */ -static connection_t * -connection_listener_new(const struct sockaddr *listensockaddr, - socklen_t socklen, - int type, const char *address, - const port_cfg_t *port_cfg) -{ - listener_connection_t *lis_conn; - connection_t *conn = NULL; - tor_socket_t s = TOR_INVALID_SOCKET; /* the socket we're going to make */ - or_options_t const *options = get_options(); -#if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H) - struct passwd *pw = NULL; -#endif - uint16_t usePort = 0, gotPort = 0; - int start_reading = 0; - static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; - tor_addr_t addr; - - if (get_n_open_sockets() >= get_options()->ConnLimit_-1) { - warn_too_many_conns(); - return NULL; - } - - if (listensockaddr->sa_family == AF_INET || - listensockaddr->sa_family == AF_INET6) { - int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); - if (is_tcp) - start_reading = 1; - - tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); - - log_notice(LD_NET, "Opening %s on %s", - conn_type_to_string(type), fmt_addrport(&addr, usePort)); - - s = tor_open_socket_nonblocking(tor_addr_family(&addr), - is_tcp ? SOCK_STREAM : SOCK_DGRAM, - is_tcp ? IPPROTO_TCP: IPPROTO_UDP); - if (!SOCKET_OK(s)) { - log_warn(LD_NET,"Socket creation failed: %s", - tor_socket_strerror(tor_socket_errno(-1))); - goto err; - } - - make_socket_reuseable(s); - -#if defined USE_TRANSPARENT && defined(IP_TRANSPARENT) - if (options->TransProxyType_parsed == TPT_TPROXY && - type == CONN_TYPE_AP_TRANS_LISTENER) { - int one = 1; - if (setsockopt(s, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) { - const char *extra = ""; - int e = tor_socket_errno(s); - if (e == EPERM) - extra = "TransTPROXY requires root privileges or similar" - " capabilities."; - log_warn(LD_NET, "Error setting IP_TRANSPARENT flag: %s.%s", - tor_socket_strerror(e), extra); - } - } -#endif - -#ifdef IPV6_V6ONLY - if (listensockaddr->sa_family == AF_INET6) { -#ifdef _WIN32 - /* In Redmond, this kind of thing passes for standards-conformance. */ - DWORD one = 1; -#else - int one = 1; -#endif - /* We need to set IPV6_V6ONLY so that this socket can't get used for - * IPv4 connections. */ - if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY, - (void*)&one, sizeof(one)) < 0) { - int e = tor_socket_errno(s); - log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s", - tor_socket_strerror(e)); - /* Keep going; probably not harmful. */ - } - } -#endif - - if (bind(s,listensockaddr,socklen) < 0) { - const char *helpfulhint = ""; - int e = tor_socket_errno(s); - if (ERRNO_IS_EADDRINUSE(e)) - helpfulhint = ". Is Tor already running?"; - log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, - tor_socket_strerror(e), helpfulhint); - goto err; - } - - if (is_tcp) { - if (tor_listen(s) < 0) { - log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } - } - - if (usePort != 0) { - gotPort = usePort; - } else { - tor_addr_t addr2; - struct sockaddr_storage ss; - socklen_t ss_len=sizeof(ss); - if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { - log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", - conn_type_to_string(type), - tor_socket_strerror(tor_socket_errno(s))); - gotPort = 0; - } - tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); - } -#ifdef HAVE_SYS_UN_H - } else if (listensockaddr->sa_family == AF_UNIX) { - start_reading = 1; - - /* For now only control ports can be Unix domain sockets - * and listeners at the same time */ - tor_assert(type == CONN_TYPE_CONTROL_LISTENER); - - if (check_location_for_unix_socket(options, address) < 0) - goto err; - - log_notice(LD_NET, "Opening %s on %s", - conn_type_to_string(type), address); - - tor_addr_make_unspec(&addr); - - if (unlink(address) < 0 && errno != ENOENT) { - log_warn(LD_NET, "Could not unlink %s: %s", address, - strerror(errno)); - goto err; - } - s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); - if (! SOCKET_OK(s)) { - log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno)); - goto err; - } - - if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) { - log_warn(LD_NET,"Bind to %s failed: %s.", address, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } -#ifdef HAVE_PWD_H - if (options->User) { - pw = getpwnam(options->User); - if (pw == NULL) { - log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.", - address, options->User); - goto err; - } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) { - log_warn(LD_NET,"Unable to chown() %s socket: %s.", - address, strerror(errno)); - goto err; - } - } -#endif - if (options->ControlSocketsGroupWritable) { - /* We need to use chmod; fchmod doesn't work on sockets on all - * platforms. */ - if (chmod(address, 0660) < 0) { - log_warn(LD_FS,"Unable to make %s group-writable.", address); - goto err; - } - } - - if (listen(s, SOMAXCONN) < 0) { - log_warn(LD_NET, "Could not listen on %s: %s", address, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } -#else - (void)options; -#endif /* HAVE_SYS_UN_H */ - } else { - log_err(LD_BUG, "Got unexpected address family %d.", - listensockaddr->sa_family); - tor_assert(0); - } - - lis_conn = listener_connection_new(type, listensockaddr->sa_family); - conn = TO_CONN(lis_conn); - conn->socket_family = listensockaddr->sa_family; - conn->s = s; - s = TOR_INVALID_SOCKET; /* Prevent double-close */ - conn->address = tor_strdup(address); - conn->port = gotPort; - tor_addr_copy(&conn->addr, &addr); - - if (port_cfg->isolation_flags) { - lis_conn->isolation_flags = port_cfg->isolation_flags; - if (port_cfg->session_group >= 0) { - lis_conn->session_group = port_cfg->session_group; - } else { - /* This can wrap after around INT_MAX listeners are opened. But I don't - * believe that matters, since you would need to open a ridiculous - * number of listeners while keeping the early ones open before you ever - * hit this. An OR with a dozen ports open, for example, would have to - * close and re-open its listeners every second for 4 years nonstop. - */ - lis_conn->session_group = global_next_session_group--; - } - } - if (type == CONN_TYPE_AP_LISTENER) { - lis_conn->socks_ipv4_traffic = port_cfg->ipv4_traffic; - lis_conn->socks_ipv6_traffic = port_cfg->ipv6_traffic; - lis_conn->socks_prefer_ipv6 = port_cfg->prefer_ipv6; - } else { - lis_conn->socks_ipv4_traffic = 1; - lis_conn->socks_ipv6_traffic = 1; - } - lis_conn->cache_ipv4_answers = port_cfg->cache_ipv4_answers; - lis_conn->cache_ipv6_answers = port_cfg->cache_ipv6_answers; - lis_conn->use_cached_ipv4_answers = port_cfg->use_cached_ipv4_answers; - lis_conn->use_cached_ipv6_answers = port_cfg->use_cached_ipv6_answers; - lis_conn->prefer_ipv6_virtaddr = port_cfg->prefer_ipv6_virtaddr; - lis_conn->socks_prefer_no_auth = port_cfg->socks_prefer_no_auth; - - if (connection_add(conn) < 0) { /* no space, forget it */ - log_warn(LD_NET,"connection_add for listener failed. Giving up."); - goto err; - } - - log_fn(usePort==gotPort ? LOG_DEBUG : LOG_NOTICE, LD_NET, - "%s listening on port %u.", - conn_type_to_string(type), gotPort); - - conn->state = LISTENER_STATE_READY; - if (start_reading) { - connection_start_reading(conn); - } else { - tor_assert(type == CONN_TYPE_AP_DNS_LISTENER); - dnsserv_configure_listener(conn); - } - - return conn; - - err: - if (SOCKET_OK(s)) - tor_close_socket(s); - if (conn) - connection_free(conn); - - return NULL; -} - -/** Do basic sanity checking on a newly received socket. Return 0 - * if it looks ok, else return -1. - * - * Notably, some TCP stacks can erroneously have accept() return successfully - * with socklen 0, when the client sends an RST before the accept call (as - * nmap does). We want to detect that, and not go on with the connection. - */ -static int -check_sockaddr(const struct sockaddr *sa, int len, int level) -{ - int ok = 1; - - if (sa->sa_family == AF_INET) { - struct sockaddr_in *sin=(struct sockaddr_in*)sa; - if (len != sizeof(struct sockaddr_in)) { - log_fn(level, LD_NET, "Length of address not as expected: %d vs %d", - len,(int)sizeof(struct sockaddr_in)); - ok = 0; - } - if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) { - log_fn(level, LD_NET, - "Address for new connection has address/port equal to zero."); - ok = 0; - } - } else if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6=(struct sockaddr_in6*)sa; - if (len != sizeof(struct sockaddr_in6)) { - log_fn(level, LD_NET, "Length of address not as expected: %d vs %d", - len,(int)sizeof(struct sockaddr_in6)); - ok = 0; - } - if (tor_mem_is_zero((void*)sin6->sin6_addr.s6_addr, 16) || - sin6->sin6_port == 0) { - log_fn(level, LD_NET, - "Address for new connection has address/port equal to zero."); - ok = 0; - } - } else { - ok = 0; - } - return ok ? 0 : -1; -} - -/** Check whether the socket family from an accepted socket got is the - * same as the one that listener is waiting for. If it isn't, log - * a useful message and return -1. Else return 0. - * - * This is annoying, but can apparently happen on some Darwins. */ -static int -check_sockaddr_family_match(sa_family_t got, connection_t *listener) -{ - if (got != listener->socket_family) { - log_info(LD_BUG, "A listener connection returned a socket with a " - "mismatched family. %s for addr_family %d gave us a socket " - "with address family %d. Dropping.", - conn_type_to_string(listener->type), - (int)listener->socket_family, - (int)got); - return -1; - } - return 0; -} - -/** The listener connection conn told poll() it wanted to read. - * Call accept() on conn-\>s, and add the new connection if necessary. - */ -static int -connection_handle_listener_read(connection_t *conn, int new_type) -{ - tor_socket_t news; /* the new socket */ - connection_t *newconn; - /* information about the remote peer when connecting to other routers */ - struct sockaddr_storage addrbuf; - struct sockaddr *remote = (struct sockaddr*)&addrbuf; - /* length of the remote address. Must be whatever accept() needs. */ - socklen_t remotelen = (socklen_t)sizeof(addrbuf); - const or_options_t *options = get_options(); - - tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in)); - memset(&addrbuf, 0, sizeof(addrbuf)); - - news = tor_accept_socket_nonblocking(conn->s,remote,&remotelen); - if (!SOCKET_OK(news)) { /* accept() error */ - int e = tor_socket_errno(conn->s); - if (ERRNO_IS_ACCEPT_EAGAIN(e)) { - return 0; /* he hung up before we could accept(). that's fine. */ - } else if (ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e)) { - warn_too_many_conns(); - return 0; - } - /* else there was a real error. */ - log_warn(LD_NET,"accept() failed: %s. Closing listener.", - tor_socket_strerror(e)); - connection_mark_for_close(conn); - return -1; - } - log_debug(LD_NET, - "Connection accepted on socket %d (child of fd %d).", - (int)news,(int)conn->s); - - make_socket_reuseable(news); - - if (options->ConstrainedSockets) - set_constrained_socket_buffers(news, (int)options->ConstrainedSockSize); - - if (check_sockaddr_family_match(remote->sa_family, conn) < 0) { - tor_close_socket(news); - return 0; - } - - if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6) { - tor_addr_t addr; - uint16_t port; - if (check_sockaddr(remote, remotelen, LOG_INFO)<0) { - log_info(LD_NET, - "accept() returned a strange address; closing connection."); - tor_close_socket(news); - return 0; - } - - tor_addr_from_sockaddr(&addr, remote, &port); - - /* process entrance policies here, before we even create the connection */ - if (new_type == CONN_TYPE_AP) { - /* check sockspolicy to see if we should accept it */ - if (socks_policy_permits_address(&addr) == 0) { - log_notice(LD_APP, - "Denying socks connection from untrusted address %s.", - fmt_and_decorate_addr(&addr)); - tor_close_socket(news); - return 0; - } - } - if (new_type == CONN_TYPE_DIR) { - /* check dirpolicy to see if we should accept it */ - if (dir_policy_permits_address(&addr) == 0) { - log_notice(LD_DIRSERV,"Denying dir connection from address %s.", - fmt_and_decorate_addr(&addr)); - tor_close_socket(news); - return 0; - } - } - - newconn = connection_new(new_type, conn->socket_family); - newconn->s = news; - - /* remember the remote address */ - tor_addr_copy(&newconn->addr, &addr); - newconn->port = port; - newconn->address = tor_dup_addr(&addr); - - if (new_type == CONN_TYPE_AP) { - TO_ENTRY_CONN(newconn)->socks_request->socks_prefer_no_auth = - TO_LISTENER_CONN(conn)->socks_prefer_no_auth; - } - if (new_type == CONN_TYPE_CONTROL) { - log_notice(LD_CONTROL, "New control connection opened from %s.", - fmt_and_decorate_addr(&addr)); - } - - } else if (conn->socket_family == AF_UNIX) { - /* For now only control ports can be Unix domain sockets - * and listeners at the same time */ - tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); - tor_assert(new_type == CONN_TYPE_CONTROL); - log_notice(LD_CONTROL, "New control connection opened."); - - newconn = connection_new(new_type, conn->socket_family); - newconn->s = news; - - /* remember the remote address -- do we have anything sane to put here? */ - tor_addr_make_unspec(&newconn->addr); - newconn->port = 1; - newconn->address = tor_strdup(conn->address); - } else { - tor_assert(0); - }; - - if (connection_add(newconn) < 0) { /* no space, forget it */ - connection_free(newconn); - return 0; /* no need to tear down the parent */ - } - - if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) { - if (! newconn->marked_for_close) - connection_mark_for_close(newconn); - return 0; - } - return 0; -} - -/** Initialize states for newly accepted connection conn. - * If conn is an OR, start the TLS handshake. - * If conn is a transparent AP, get its original destination - * and place it in circuit_wait. - */ -static int -connection_init_accepted_conn(connection_t *conn, - const listener_connection_t *listener) -{ - int rv; - - connection_start_reading(conn); - - switch (conn->type) { - case CONN_TYPE_EXT_OR: - /* Initiate Extended ORPort authentication. */ - return connection_ext_or_start_auth(TO_OR_CONN(conn)); - case CONN_TYPE_OR: - control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); - rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1); - if (rv < 0) { - connection_or_close_for_error(TO_OR_CONN(conn), 0); - } - return rv; - break; - case CONN_TYPE_AP: - TO_ENTRY_CONN(conn)->isolation_flags = listener->isolation_flags; - TO_ENTRY_CONN(conn)->session_group = listener->session_group; - TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch(); - TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type; - TO_ENTRY_CONN(conn)->ipv4_traffic_ok = listener->socks_ipv4_traffic; - TO_ENTRY_CONN(conn)->ipv6_traffic_ok = listener->socks_ipv6_traffic; - TO_ENTRY_CONN(conn)->prefer_ipv6_traffic = listener->socks_prefer_ipv6; - TO_ENTRY_CONN(conn)->cache_ipv4_answers = listener->cache_ipv4_answers; - TO_ENTRY_CONN(conn)->cache_ipv6_answers = listener->cache_ipv6_answers; - TO_ENTRY_CONN(conn)->use_cached_ipv4_answers = - listener->use_cached_ipv4_answers; - TO_ENTRY_CONN(conn)->use_cached_ipv6_answers = - listener->use_cached_ipv6_answers; - TO_ENTRY_CONN(conn)->prefer_ipv6_virtaddr = - listener->prefer_ipv6_virtaddr; - - switch (TO_CONN(listener)->type) { - case CONN_TYPE_AP_LISTENER: - conn->state = AP_CONN_STATE_SOCKS_WAIT; - break; - case CONN_TYPE_AP_TRANS_LISTENER: - TO_ENTRY_CONN(conn)->is_transparent_ap = 1; - conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - return connection_ap_process_transparent(TO_ENTRY_CONN(conn)); - case CONN_TYPE_AP_NATD_LISTENER: - TO_ENTRY_CONN(conn)->is_transparent_ap = 1; - conn->state = AP_CONN_STATE_NATD_WAIT; - break; - } - break; - case CONN_TYPE_DIR: - conn->purpose = DIR_PURPOSE_SERVER; - conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT; - break; - case CONN_TYPE_CONTROL: - conn->state = CONTROL_CONN_STATE_NEEDAUTH; - break; - } - return 0; -} - -/** Take conn, make a nonblocking socket; try to connect to - * addr:port (they arrive in *host order*). If fail, return -1 and if - * applicable put your best guess about errno into *socket_error. - * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0. - * - * address is used to make the logs useful. - * - * On success, add conn to the list of polled connections. - */ -int -connection_connect(connection_t *conn, const char *address, - const tor_addr_t *addr, uint16_t port, int *socket_error) -{ - tor_socket_t s; - int inprogress = 0; - struct sockaddr_storage addrbuf; - struct sockaddr *dest_addr; - int dest_addr_len; - const or_options_t *options = get_options(); - int protocol_family; - - if (get_n_open_sockets() >= get_options()->ConnLimit_-1) { - warn_too_many_conns(); - *socket_error = SOCK_ERRNO(ENOBUFS); - return -1; - } - - if (tor_addr_family(addr) == AF_INET6) - protocol_family = PF_INET6; - else - protocol_family = PF_INET; - - if (get_options()->DisableNetwork) { - /* We should never even try to connect anyplace if DisableNetwork is set. - * Warn if we do, and refuse to make the connection. */ - static ratelim_t disablenet_violated = RATELIM_INIT(30*60); - *socket_error = SOCK_ERRNO(ENETUNREACH); - log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, - "Tried to open a socket with DisableNetwork set."); - tor_fragile_assert(); - return -1; - } - - s = tor_open_socket_nonblocking(protocol_family,SOCK_STREAM,IPPROTO_TCP); - if (! SOCKET_OK(s)) { - *socket_error = tor_socket_errno(-1); - log_warn(LD_NET,"Error creating network socket: %s", - tor_socket_strerror(*socket_error)); - return -1; - } - - make_socket_reuseable(s); - - if (!tor_addr_is_loopback(addr)) { - const tor_addr_t *ext_addr = NULL; - if (protocol_family == AF_INET && - !tor_addr_is_null(&options->OutboundBindAddressIPv4_)) - ext_addr = &options->OutboundBindAddressIPv4_; - else if (protocol_family == AF_INET6 && - !tor_addr_is_null(&options->OutboundBindAddressIPv6_)) - ext_addr = &options->OutboundBindAddressIPv6_; - if (ext_addr) { - struct sockaddr_storage ext_addr_sa; - socklen_t ext_addr_len = 0; - memset(&ext_addr_sa, 0, sizeof(ext_addr_sa)); - ext_addr_len = tor_addr_to_sockaddr(ext_addr, 0, - (struct sockaddr *) &ext_addr_sa, - sizeof(ext_addr_sa)); - if (ext_addr_len == 0) { - log_warn(LD_NET, - "Error converting OutboundBindAddress %s into sockaddr. " - "Ignoring.", fmt_and_decorate_addr(ext_addr)); - } else { - if (bind(s, (struct sockaddr *) &ext_addr_sa, ext_addr_len) < 0) { - *socket_error = tor_socket_errno(s); - log_warn(LD_NET,"Error binding network socket to %s: %s", - fmt_and_decorate_addr(ext_addr), - tor_socket_strerror(*socket_error)); - tor_close_socket(s); - return -1; - } - } - } - } - - if (options->ConstrainedSockets) - set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize); - - memset(&addrbuf,0,sizeof(addrbuf)); - dest_addr = (struct sockaddr*) &addrbuf; - dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf)); - tor_assert(dest_addr_len > 0); - - log_debug(LD_NET, "Connecting to %s:%u.", - escaped_safe_str_client(address), port); - - if (connect(s, dest_addr, (socklen_t)dest_addr_len) < 0) { - int e = tor_socket_errno(s); - if (!ERRNO_IS_CONN_EINPROGRESS(e)) { - /* yuck. kill it. */ - *socket_error = e; - log_info(LD_NET, - "connect() to %s:%u failed: %s", - escaped_safe_str_client(address), - port, tor_socket_strerror(e)); - tor_close_socket(s); - return -1; - } else { - inprogress = 1; - } - } - - /* it succeeded. we're connected. */ - log_fn(inprogress?LOG_DEBUG:LOG_INFO, LD_NET, - "Connection to %s:%u %s (sock "TOR_SOCKET_T_FORMAT").", - escaped_safe_str_client(address), - port, inprogress?"in progress":"established", s); - conn->s = s; - if (connection_add_connecting(conn) < 0) { - /* no space, forget it */ - *socket_error = SOCK_ERRNO(ENOBUFS); - return -1; - } - return inprogress ? 0 : 1; -} - -/** Convert state number to string representation for logging purposes. - */ -static const char * -connection_proxy_state_to_string(int state) -{ - static const char *unknown = "???"; - static const char *states[] = { - "PROXY_NONE", - "PROXY_INFANT", - "PROXY_HTTPS_WANT_CONNECT_OK", - "PROXY_SOCKS4_WANT_CONNECT_OK", - "PROXY_SOCKS5_WANT_AUTH_METHOD_NONE", - "PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929", - "PROXY_SOCKS5_WANT_AUTH_RFC1929_OK", - "PROXY_SOCKS5_WANT_CONNECT_OK", - "PROXY_CONNECTED", - }; - - if (state < PROXY_NONE || state > PROXY_CONNECTED) - return unknown; - - return states[state]; -} - -/** Returns the global proxy type used by tor. Use this function for - * logging or high-level purposes, don't use it to fill the - * proxy_type field of or_connection_t; use the actual proxy - * protocol instead.*/ -static int -get_proxy_type(void) -{ - const or_options_t *options = get_options(); - - if (options->HTTPSProxy) - return PROXY_CONNECT; - else if (options->Socks4Proxy) - return PROXY_SOCKS4; - else if (options->Socks5Proxy) - return PROXY_SOCKS5; - else if (options->ClientTransportPlugin) - return PROXY_PLUGGABLE; - else - return PROXY_NONE; -} - -/* One byte for the version, one for the command, two for the - port, and four for the addr... and, one more for the - username NUL: */ -#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1) - -/** Write a proxy request of type (socks4, socks5, https) to conn - * for conn->addr:conn->port, authenticating with the auth details given - * in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies - * support authentication. - * - * Returns -1 if conn->addr is incompatible with the proxy protocol, and - * 0 otherwise. - * - * Use connection_read_proxy_handshake() to complete the handshake. - */ -int -connection_proxy_connect(connection_t *conn, int type) -{ - const or_options_t *options; - - tor_assert(conn); - - options = get_options(); - - switch (type) { - case PROXY_CONNECT: { - char buf[1024]; - char *base64_authenticator=NULL; - const char *authenticator = options->HTTPSProxyAuthenticator; - - /* Send HTTP CONNECT and authentication (if available) in - * one request */ - - if (authenticator) { - base64_authenticator = alloc_http_authenticator(authenticator); - if (!base64_authenticator) - log_warn(LD_OR, "Encoding https authenticator failed"); - } - - if (base64_authenticator) { - const char *addrport = fmt_addrport(&conn->addr, conn->port); - tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Proxy-Authorization: Basic %s\r\n\r\n", - addrport, - addrport, - base64_authenticator); - tor_free(base64_authenticator); - } else { - tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n", - fmt_addrport(&conn->addr, conn->port)); - } - - connection_write_to_buf(buf, strlen(buf), conn); - conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK; - break; - } - - case PROXY_SOCKS4: { - unsigned char *buf; - uint16_t portn; - uint32_t ip4addr; - size_t buf_size = 0; - char *socks_args_string = NULL; - - /* Send a SOCKS4 connect request */ - - if (tor_addr_family(&conn->addr) != AF_INET) { - log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6"); - return -1; - } - - { /* If we are here because we are trying to connect to a - pluggable transport proxy, check if we have any SOCKS - arguments to transmit. If we do, compress all arguments to - a single string in 'socks_args_string': */ - - if (get_proxy_type() == PROXY_PLUGGABLE) { - socks_args_string = - pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); - if (socks_args_string) - log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.", - socks_args_string); - } - } - - { /* Figure out the buffer size we need for the SOCKS message: */ - - buf_size = SOCKS4_STANDARD_BUFFER_SIZE; - - /* If we have a SOCKS argument string, consider its size when - calculating the buffer size: */ - if (socks_args_string) - buf_size += strlen(socks_args_string); - } - - buf = tor_malloc_zero(buf_size); - - ip4addr = tor_addr_to_ipv4n(&conn->addr); - portn = htons(conn->port); - - buf[0] = 4; /* version */ - buf[1] = SOCKS_COMMAND_CONNECT; /* command */ - memcpy(buf + 2, &portn, 2); /* port */ - memcpy(buf + 4, &ip4addr, 4); /* addr */ - - /* Next packet field is the userid. If we have pluggable - transport SOCKS arguments, we have to embed them - there. Otherwise, we use an empty userid. */ - if (socks_args_string) { /* place the SOCKS args string: */ - tor_assert(strlen(socks_args_string) > 0); - tor_assert(buf_size >= - SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string)); - strlcpy((char *)buf + 8, socks_args_string, buf_size - 8); - tor_free(socks_args_string); - } else { - buf[8] = 0; /* no userid */ - } - - connection_write_to_buf((char *)buf, buf_size, conn); - tor_free(buf); - - conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK; - break; - } - - case PROXY_SOCKS5: { - unsigned char buf[4]; /* fields: vers, num methods, method list */ - - /* Send a SOCKS5 greeting (connect request must wait) */ - - buf[0] = 5; /* version */ - - /* We have to use SOCKS5 authentication, if we have a - Socks5ProxyUsername or if we want to pass arguments to our - pluggable transport proxy: */ - if ((options->Socks5ProxyUsername) || - (get_proxy_type() == PROXY_PLUGGABLE && - (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) { - /* number of auth methods */ - buf[1] = 2; - buf[2] = 0x00; /* no authentication */ - buf[3] = 0x02; /* rfc1929 Username/Passwd auth */ - conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929; - } else { - buf[1] = 1; - buf[2] = 0x00; /* no authentication */ - conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE; - } - - connection_write_to_buf((char *)buf, 2 + buf[1], conn); - break; - } - - default: - log_err(LD_BUG, "Invalid proxy protocol, %d", type); - tor_fragile_assert(); - return -1; - } - - log_debug(LD_NET, "set state %s", - connection_proxy_state_to_string(conn->proxy_state)); - - return 0; -} - -/** Read conn's inbuf. If the http response from the proxy is all - * here, make sure it's good news, then return 1. If it's bad news, - * return -1. Else return 0 and hope for better luck next time. - */ -static int -connection_read_https_proxy_response(connection_t *conn) -{ - char *headers; - char *reason=NULL; - int status_code; - time_t date_header; - - switch (fetch_from_buf_http(conn->inbuf, - &headers, MAX_HEADERS_SIZE, - NULL, NULL, 10000, 0)) { - case -1: /* overflow */ - log_warn(LD_PROTOCOL, - "Your https proxy sent back an oversized response. Closing."); - return -1; - case 0: - log_info(LD_NET,"https proxy response not all here yet. Waiting."); - return 0; - /* case 1, fall through */ - } - - if (parse_http_response(headers, &status_code, &date_header, - NULL, &reason) < 0) { - log_warn(LD_NET, - "Unparseable headers from proxy (connecting to '%s'). Closing.", - conn->address); - tor_free(headers); - return -1; - } - tor_free(headers); - if (!reason) reason = tor_strdup("[no reason given]"); - - if (status_code == 200) { - log_info(LD_NET, - "HTTPS connect to '%s' successful! (200 %s) Starting TLS.", - conn->address, escaped(reason)); - tor_free(reason); - return 1; - } - /* else, bad news on the status code */ - switch (status_code) { - case 403: - log_warn(LD_NET, - "The https proxy refused to allow connection to %s " - "(status code %d, %s). Closing.", - conn->address, status_code, escaped(reason)); - break; - default: - log_warn(LD_NET, - "The https proxy sent back an unexpected status code %d (%s). " - "Closing.", - status_code, escaped(reason)); - break; - } - tor_free(reason); - return -1; -} - -/** Send SOCKS5 CONNECT command to conn, copying conn->addr - * and conn->port into the request. - */ -static void -connection_send_socks5_connect(connection_t *conn) -{ - unsigned char buf[1024]; - size_t reqsize = 6; - uint16_t port = htons(conn->port); - - buf[0] = 5; /* version */ - buf[1] = SOCKS_COMMAND_CONNECT; /* command */ - buf[2] = 0; /* reserved */ - - if (tor_addr_family(&conn->addr) == AF_INET) { - uint32_t addr = tor_addr_to_ipv4n(&conn->addr); - - buf[3] = 1; - reqsize += 4; - memcpy(buf + 4, &addr, 4); - memcpy(buf + 8, &port, 2); - } else { /* AF_INET6 */ - buf[3] = 4; - reqsize += 16; - memcpy(buf + 4, tor_addr_to_in6(&conn->addr), 16); - memcpy(buf + 20, &port, 2); - } - - connection_write_to_buf((char *)buf, reqsize, conn); - - conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK; -} - -/** Wrapper around fetch_from_(buf/evbuffer)_socks_client: see those functions - * for documentation of its behavior. */ -static int -connection_fetch_from_buf_socks_client(connection_t *conn, - int state, char **reason) -{ - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return fetch_from_evbuffer_socks_client(input, state, reason); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_from_buf_socks_client(conn->inbuf, state, reason); - } -} - -/** Call this from connection_*_process_inbuf() to advance the proxy - * handshake. - * - * No matter what proxy protocol is used, if this function returns 1, the - * handshake is complete, and the data remaining on inbuf may contain the - * start of the communication with the requested server. - * - * Returns 0 if the current buffer contains an incomplete response, and -1 - * on error. - */ -int -connection_read_proxy_handshake(connection_t *conn) -{ - int ret = 0; - char *reason = NULL; - - log_debug(LD_NET, "enter state %s", - connection_proxy_state_to_string(conn->proxy_state)); - - switch (conn->proxy_state) { - case PROXY_HTTPS_WANT_CONNECT_OK: - ret = connection_read_https_proxy_response(conn); - if (ret == 1) - conn->proxy_state = PROXY_CONNECTED; - break; - - case PROXY_SOCKS4_WANT_CONNECT_OK: - ret = connection_fetch_from_buf_socks_client(conn, - conn->proxy_state, - &reason); - if (ret == 1) - conn->proxy_state = PROXY_CONNECTED; - break; - - case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE: - ret = connection_fetch_from_buf_socks_client(conn, - conn->proxy_state, - &reason); - /* no auth needed, do connect */ - if (ret == 1) { - connection_send_socks5_connect(conn); - ret = 0; - } - break; - - case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929: - ret = connection_fetch_from_buf_socks_client(conn, - conn->proxy_state, - &reason); - - /* send auth if needed, otherwise do connect */ - if (ret == 1) { - connection_send_socks5_connect(conn); - ret = 0; - } else if (ret == 2) { - unsigned char buf[1024]; - size_t reqsize, usize, psize; - const char *user, *pass; - char *socks_args_string = NULL; - - if (get_proxy_type() == PROXY_PLUGGABLE) { - socks_args_string = - pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); - if (!socks_args_string) { - log_warn(LD_NET, "Could not create SOCKS args string."); - ret = -1; - break; - } - - log_debug(LD_NET, "SOCKS5 arguments: %s", socks_args_string); - tor_assert(strlen(socks_args_string) > 0); - tor_assert(strlen(socks_args_string) <= MAX_SOCKS5_AUTH_SIZE_TOTAL); - - if (strlen(socks_args_string) > MAX_SOCKS5_AUTH_FIELD_SIZE) { - user = socks_args_string; - usize = MAX_SOCKS5_AUTH_FIELD_SIZE; - pass = socks_args_string + MAX_SOCKS5_AUTH_FIELD_SIZE; - psize = strlen(socks_args_string) - MAX_SOCKS5_AUTH_FIELD_SIZE; - } else { - user = socks_args_string; - usize = strlen(socks_args_string); - pass = "\0"; - psize = 1; - } - } else if (get_options()->Socks5ProxyUsername) { - user = get_options()->Socks5ProxyUsername; - pass = get_options()->Socks5ProxyPassword; - tor_assert(user && pass); - usize = strlen(user); - psize = strlen(pass); - } else { - log_err(LD_BUG, "We entered %s for no reason!", __func__); - tor_fragile_assert(); - ret = -1; - break; - } - - /* Username and password lengths should have been checked - above and during torrc parsing. */ - tor_assert(usize <= MAX_SOCKS5_AUTH_FIELD_SIZE && - psize <= MAX_SOCKS5_AUTH_FIELD_SIZE); - reqsize = 3 + usize + psize; - - buf[0] = 1; /* negotiation version */ - buf[1] = usize; - memcpy(buf + 2, user, usize); - buf[2 + usize] = psize; - memcpy(buf + 3 + usize, pass, psize); - - if (socks_args_string) - tor_free(socks_args_string); - - connection_write_to_buf((char *)buf, reqsize, conn); - - conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK; - ret = 0; - } - break; - - case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK: - ret = connection_fetch_from_buf_socks_client(conn, - conn->proxy_state, - &reason); - /* send the connect request */ - if (ret == 1) { - connection_send_socks5_connect(conn); - ret = 0; - } - break; - - case PROXY_SOCKS5_WANT_CONNECT_OK: - ret = connection_fetch_from_buf_socks_client(conn, - conn->proxy_state, - &reason); - if (ret == 1) - conn->proxy_state = PROXY_CONNECTED; - break; - - default: - log_err(LD_BUG, "Invalid proxy_state for reading, %d", - conn->proxy_state); - tor_fragile_assert(); - ret = -1; - break; - } - - log_debug(LD_NET, "leaving state %s", - connection_proxy_state_to_string(conn->proxy_state)); - - if (ret < 0) { - if (reason) { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)", - conn->address, conn->port, escaped(reason)); - tor_free(reason); - } else { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d", - conn->address, conn->port); - } - } else if (ret == 1) { - log_info(LD_NET, "Proxy Client: connection to %s:%d successful", - conn->address, conn->port); - } - - return ret; -} - -/** Given a list of listener connections in old_conns, and list of - * port_cfg_t entries in ports, open a new listener for every port in - * ports that does not already have a listener in old_conns. - * - * Remove from old_conns every connection that has a corresponding - * entry in ports. Add to new_conns new every connection we - * launch. - * - * If control_listeners_only is true, then we only open control - * listeners, and we do not remove any noncontrol listeners from old_conns. - * - * Return 0 on success, -1 on failure. - **/ -static int -retry_listener_ports(smartlist_t *old_conns, - const smartlist_t *ports, - smartlist_t *new_conns, - int control_listeners_only) -{ - smartlist_t *launch = smartlist_new(); - int r = 0; - - if (control_listeners_only) { - SMARTLIST_FOREACH(ports, port_cfg_t *, p, { - if (p->type == CONN_TYPE_CONTROL_LISTENER) - smartlist_add(launch, p); - }); - } else { - smartlist_add_all(launch, ports); - } - - /* Iterate through old_conns, comparing it to launch: remove from both lists - * each pair of elements that corresponds to the same port. */ - SMARTLIST_FOREACH_BEGIN(old_conns, connection_t *, conn) { - const port_cfg_t *found_port = NULL; - - /* Okay, so this is a listener. Is it configured? */ - SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, wanted) { - if (conn->type != wanted->type) - continue; - if ((conn->socket_family != AF_UNIX && wanted->is_unix_addr) || - (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr)) - continue; - - if (wanted->no_listen) - continue; /* We don't want to open a listener for this one */ - - if (wanted->is_unix_addr) { - if (conn->socket_family == AF_UNIX && - !strcmp(wanted->unix_addr, conn->address)) { - found_port = wanted; - break; - } - } else { - int port_matches; - if (wanted->port == CFG_AUTO_PORT) { - port_matches = 1; - } else { - port_matches = (wanted->port == conn->port); - } - if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) { - found_port = wanted; - break; - } - } - } SMARTLIST_FOREACH_END(wanted); - - if (found_port) { - /* This listener is already running; we don't need to launch it. */ - //log_debug(LD_NET, "Already have %s on %s:%d", - // conn_type_to_string(found_port->type), conn->address, conn->port); - smartlist_remove(launch, found_port); - /* And we can remove the connection from old_conns too. */ - SMARTLIST_DEL_CURRENT(old_conns, conn); - } - } SMARTLIST_FOREACH_END(conn); - - /* Now open all the listeners that are configured but not opened. */ - SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) { - struct sockaddr *listensockaddr; - socklen_t listensocklen = 0; - char *address=NULL; - connection_t *conn; - int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port; - tor_assert(real_port <= UINT16_MAX); - if (port->no_listen) - continue; - - if (port->is_unix_addr) { - listensockaddr = (struct sockaddr *) - create_unix_sockaddr(port->unix_addr, - &address, &listensocklen); - } else { - listensockaddr = tor_malloc(sizeof(struct sockaddr_storage)); - listensocklen = tor_addr_to_sockaddr(&port->addr, - real_port, - listensockaddr, - sizeof(struct sockaddr_storage)); - address = tor_dup_addr(&port->addr); - } - - if (listensockaddr) { - conn = connection_listener_new(listensockaddr, listensocklen, - port->type, address, port); - tor_free(listensockaddr); - tor_free(address); - } else { - conn = NULL; - } - - if (!conn) { - r = -1; - } else { - if (new_conns) - smartlist_add(new_conns, conn); - } - } SMARTLIST_FOREACH_END(port); - - smartlist_free(launch); - - return r; -} - -/** Launch listeners for each port you should have open. Only launch - * listeners who are not already open, and only close listeners we no longer - * want. - * - * Add all old conns that should be closed to replaced_conns. - * Add all new connections to new_conns. - * - * If close_all_noncontrol is true, then we only open control - * listeners, and we close all other listeners. - */ -int -retry_all_listeners(smartlist_t *replaced_conns, - smartlist_t *new_conns, int close_all_noncontrol) -{ - smartlist_t *listeners = smartlist_new(); - const or_options_t *options = get_options(); - int retval = 0; - const uint16_t old_or_port = router_get_advertised_or_port(options); - const uint16_t old_or_port_ipv6 = - router_get_advertised_or_port_by_af(options,AF_INET6); - const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0); - - SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { - if (connection_is_listener(conn) && !conn->marked_for_close) - smartlist_add(listeners, conn); - } SMARTLIST_FOREACH_END(conn); - - if (retry_listener_ports(listeners, - get_configured_ports(), - new_conns, - close_all_noncontrol) < 0) - retval = -1; - - /* Any members that were still in 'listeners' don't correspond to - * any configured port. Kill 'em. */ - SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) { - log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d", - conn_type_to_string(conn->type), conn->address, conn->port); - if (replaced_conns) { - smartlist_add(replaced_conns, conn); - } else { - connection_close_immediate(conn); - connection_mark_for_close(conn); - } - } SMARTLIST_FOREACH_END(conn); - - smartlist_free(listeners); - - if (old_or_port != router_get_advertised_or_port(options) || - old_or_port_ipv6 != router_get_advertised_or_port_by_af(options, - AF_INET6) || - old_dir_port != router_get_advertised_dir_port(options, 0)) { - /* Our chosen ORPort or DirPort is not what it used to be: the - * descriptor we had (if any) should be regenerated. (We won't - * automatically notice this because of changes in the option, - * since the value could be "auto".) */ - mark_my_descriptor_dirty("Chosen Or/DirPort changed"); - } - - return retval; -} - -/** Mark every listener of type other than CONTROL_LISTENER to be closed. */ -void -connection_mark_all_noncontrol_listeners(void) -{ - SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { - if (conn->marked_for_close) - continue; - if (conn->type == CONN_TYPE_CONTROL_LISTENER) - continue; - if (connection_is_listener(conn)) - connection_mark_for_close(conn); - } SMARTLIST_FOREACH_END(conn); -} - -/** Mark every external connection not used for controllers for close. */ -void -connection_mark_all_noncontrol_connections(void) -{ - SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { - if (conn->marked_for_close) - continue; - switch (conn->type) { - case CONN_TYPE_CPUWORKER: - case CONN_TYPE_CONTROL_LISTENER: - case CONN_TYPE_CONTROL: - break; - case CONN_TYPE_AP: - connection_mark_unattached_ap(TO_ENTRY_CONN(conn), - END_STREAM_REASON_HIBERNATING); - break; - default: - connection_mark_for_close(conn); - break; - } - } SMARTLIST_FOREACH_END(conn); -} - -/** Return 1 if we should apply rate limiting to conn, and 0 - * otherwise. - * Right now this just checks if it's an internal IP address or an - * 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) -{ - const or_options_t *options = get_options(); - if (conn->linked) - return 0; /* Internal connection */ - else if (! options->CountPrivateBandwidth && - (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */ - tor_addr_is_internal(&conn->addr, 0))) - return 0; /* Internal address */ - else - return 1; -} - -#ifdef USE_BUFFEREVENTS -static struct bufferevent_rate_limit_group *global_rate_limit = NULL; -#else -extern int global_read_bucket, global_write_bucket; -extern int global_relayed_read_bucket, global_relayed_write_bucket; - -/** Did either global write bucket run dry last second? If so, - * we are likely to run dry again this second, so be stingy with the - * tokens we just put in. */ -static int write_buckets_empty_last_second = 0; -#endif - -/** How many seconds of no active local circuits will make the - * connection revert to the "relayed" bandwidth class? */ -#define CLIENT_IDLE_TIME_FOR_PRIORITY 30 - -#ifndef USE_BUFFEREVENTS -/** Return 1 if conn should use tokens from the "relayed" - * bandwidth rates, else 0. Currently, only OR conns with bandwidth - * class 1, and directory conns that are serving data out, count. - */ -static int -connection_counts_as_relayed_traffic(connection_t *conn, time_t now) -{ - if (conn->type == CONN_TYPE_OR && - connection_or_client_used(TO_OR_CONN(conn)) + - CLIENT_IDLE_TIME_FOR_PRIORITY < now) - return 1; - if (conn->type == CONN_TYPE_DIR && DIR_CONN_IS_SERVER(conn)) - return 1; - return 0; -} - -/** Helper function to decide how many bytes out of global_bucket - * we're willing to use for this transaction. base is the size - * of a cell on the network; priority says whether we should - * write many of them or just a few; and conn_bucket (if - * non-negative) provides an upper limit for our answer. */ -static ssize_t -connection_bucket_round_robin(int base, int priority, - ssize_t global_bucket, ssize_t conn_bucket) -{ - ssize_t at_most; - ssize_t num_bytes_high = (priority ? 32 : 16) * base; - ssize_t num_bytes_low = (priority ? 4 : 2) * base; - - /* Do a rudimentary round-robin so one circuit can't hog a connection. - * Pick at most 32 cells, at least 4 cells if possible, and if we're in - * the middle pick 1/8 of the available bandwidth. */ - at_most = global_bucket / 8; - at_most -= (at_most % base); /* round down */ - if (at_most > num_bytes_high) /* 16 KB, or 8 KB for low-priority */ - at_most = num_bytes_high; - else if (at_most < num_bytes_low) /* 2 KB, or 1 KB for low-priority */ - at_most = num_bytes_low; - - if (at_most > global_bucket) - at_most = global_bucket; - - if (conn_bucket >= 0 && at_most > conn_bucket) - at_most = conn_bucket; - - if (at_most < 0) - return 0; - return at_most; -} - -/** How many bytes at most can we read onto this connection? */ -static ssize_t -connection_bucket_read_limit(connection_t *conn, time_t now) -{ - int base = RELAY_PAYLOAD_SIZE; - int priority = conn->type != CONN_TYPE_DIR; - int conn_bucket = -1; - int global_bucket = global_read_bucket; - - if (connection_speaks_cells(conn)) { - or_connection_t *or_conn = TO_OR_CONN(conn); - if (conn->state == OR_CONN_STATE_OPEN) - conn_bucket = or_conn->read_bucket; - base = get_cell_network_size(or_conn->wide_circ_ids); - } - - if (!connection_is_rate_limited(conn)) { - /* be willing to read on local conns even if our buckets are empty */ - return conn_bucket>=0 ? conn_bucket : 1<<14; - } - - if (connection_counts_as_relayed_traffic(conn, now) && - global_relayed_read_bucket <= global_read_bucket) - global_bucket = global_relayed_read_bucket; - - return connection_bucket_round_robin(base, priority, - global_bucket, conn_bucket); -} - -/** How many bytes at most can we write onto this connection? */ -ssize_t -connection_bucket_write_limit(connection_t *conn, time_t now) -{ - int base = RELAY_PAYLOAD_SIZE; - int priority = conn->type != CONN_TYPE_DIR; - int conn_bucket = (int)conn->outbuf_flushlen; - int global_bucket = global_write_bucket; - - if (!connection_is_rate_limited(conn)) { - /* be willing to write to local conns even if our buckets are empty */ - return conn->outbuf_flushlen; - } - - if (connection_speaks_cells(conn)) { - /* use the per-conn write limit if it's lower, but if it's less - * than zero just use zero */ - or_connection_t *or_conn = TO_OR_CONN(conn); - if (conn->state == OR_CONN_STATE_OPEN) - if (or_conn->write_bucket < conn_bucket) - conn_bucket = or_conn->write_bucket >= 0 ? - or_conn->write_bucket : 0; - base = get_cell_network_size(or_conn->wide_circ_ids); - } - - if (connection_counts_as_relayed_traffic(conn, now) && - global_relayed_write_bucket <= global_write_bucket) - global_bucket = global_relayed_write_bucket; - - return connection_bucket_round_robin(base, priority, - global_bucket, conn_bucket); -} -#else -static ssize_t -connection_bucket_read_limit(connection_t *conn, time_t now) -{ - (void) now; - return bufferevent_get_max_to_read(conn->bufev); -} -ssize_t -connection_bucket_write_limit(connection_t *conn, time_t now) -{ - (void) now; - return bufferevent_get_max_to_write(conn->bufev); -} -#endif - -/** Return 1 if the global write buckets are low enough that we - * shouldn't send attempt bytes of low-priority directory stuff - * out to conn. Else return 0. - - * Priority is 1 for v1 requests (directories and running-routers), - * and 2 for v2 requests (statuses and descriptors). But see FFFF in - * directory_handle_command_get() for why we don't use priority 2 yet. - * - * There are a lot of parameters we could use here: - * - global_relayed_write_bucket. Low is bad. - * - global_write_bucket. Low is bad. - * - bandwidthrate. Low is bad. - * - bandwidthburst. Not a big factor? - * - attempt. High is bad. - * - total bytes queued on outbufs. High is bad. But I'm wary of - * using this, since a few slow-flushing queues will pump up the - * number without meaning what we meant to mean. What we really - * mean is "total directory bytes added to outbufs recently", but - * that's harder to quantify and harder to keep track of. - */ -int -global_write_bucket_low(connection_t *conn, size_t attempt, int priority) -{ -#ifdef USE_BUFFEREVENTS - ssize_t smaller_bucket = bufferevent_get_max_to_write(conn->bufev); -#else - int smaller_bucket = global_write_bucket < global_relayed_write_bucket ? - global_write_bucket : global_relayed_write_bucket; -#endif - if (authdir_mode(get_options()) && priority>1) - return 0; /* there's always room to answer v2 if we're an auth dir */ - - if (!connection_is_rate_limited(conn)) - return 0; /* local conns don't get limited */ - - if (smaller_bucket < (int)attempt) - return 1; /* not enough space no matter the priority */ - -#ifndef USE_BUFFEREVENTS - if (write_buckets_empty_last_second) - return 1; /* we're already hitting our limits, no more please */ -#endif - - if (priority == 1) { /* old-style v1 query */ - /* Could we handle *two* of these requests within the next two seconds? */ - const or_options_t *options = get_options(); - int64_t can_write = (int64_t)smaller_bucket - + 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate : - options->BandwidthRate); - if (can_write < 2*(int64_t)attempt) - return 1; - } else { /* v2 query */ - /* no further constraints yet */ - } - return 0; -} - -/** Helper: adjusts our bandwidth history and informs the controller as - * appropriate, given that we have just read num_read bytes and written - * num_written bytes on conn. */ -static void -record_num_bytes_transferred_impl(connection_t *conn, - time_t now, size_t num_read, size_t num_written) -{ - /* Count bytes of answering direct and tunneled directory requests */ - if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) { - if (num_read > 0) - rep_hist_note_dir_bytes_read(num_read, now); - if (num_written > 0) - rep_hist_note_dir_bytes_written(num_written, now); - } - - if (!connection_is_rate_limited(conn)) - return; /* local IPs are free */ - - if (conn->type == CONN_TYPE_OR) - rep_hist_note_or_conn_bytes(conn->global_identifier, num_read, - num_written, now); - - if (num_read > 0) { - rep_hist_note_bytes_read(num_read, now); - } - if (num_written > 0) { - rep_hist_note_bytes_written(num_written, now); - } - if (conn->type == CONN_TYPE_EXIT) - rep_hist_note_exit_bytes(conn->port, num_written, num_read); -} - -#ifdef USE_BUFFEREVENTS -/** Wrapper around fetch_from_(buf/evbuffer)_socks_client: see those functions - * for documentation of its behavior. */ -static void -record_num_bytes_transferred(connection_t *conn, - time_t now, size_t num_read, size_t num_written) -{ - /* XXX024 check if this is necessary */ - if (num_written >= INT_MAX || num_read >= INT_MAX) { - log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " - "connection type=%s, state=%s", - (unsigned long)num_read, (unsigned long)num_written, - conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state)); - if (num_written >= INT_MAX) num_written = 1; - if (num_read >= INT_MAX) num_read = 1; - tor_fragile_assert(); - } - - record_num_bytes_transferred_impl(conn,now,num_read,num_written); -} -#endif - -#ifndef USE_BUFFEREVENTS -/** Last time at which the global or relay buckets were emptied in msec - * since midnight. */ -static uint32_t global_relayed_read_emptied = 0, - global_relayed_write_emptied = 0, - global_read_emptied = 0, - global_write_emptied = 0; - -/** Helper: convert given tvnow time value to milliseconds since - * midnight. */ -static uint32_t -msec_since_midnight(const struct timeval *tvnow) -{ - return (uint32_t)(((tvnow->tv_sec % 86400L) * 1000L) + - ((uint32_t)tvnow->tv_usec / (uint32_t)1000L)); -} - -/** Check if a bucket which had tokens_before tokens and which got - * tokens_removed tokens removed at timestamp tvnow has run - * out of tokens, and if so, note the milliseconds since midnight in - * timestamp_var for the next TB_EMPTY event. */ -void -connection_buckets_note_empty_ts(uint32_t *timestamp_var, - int tokens_before, size_t tokens_removed, - const struct timeval *tvnow) -{ - if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed) - *timestamp_var = msec_since_midnight(tvnow); -} - -/** We just read num_read and wrote num_written bytes - * onto conn. Decrement buckets appropriately. */ -static void -connection_buckets_decrement(connection_t *conn, time_t now, - size_t num_read, size_t num_written) -{ - if (num_written >= INT_MAX || num_read >= INT_MAX) { - log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, " - "connection type=%s, state=%s", - (unsigned long)num_read, (unsigned long)num_written, - conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state)); - if (num_written >= INT_MAX) num_written = 1; - if (num_read >= INT_MAX) num_read = 1; - tor_fragile_assert(); - } - - record_num_bytes_transferred_impl(conn, now, num_read, num_written); - - if (!connection_is_rate_limited(conn)) - return; /* local IPs are free */ - - /* If one or more of our token buckets ran dry just now, note the - * timestamp for TB_EMPTY events. */ - if (get_options()->TestingEnableTbEmptyEvent) { - struct timeval tvnow; - tor_gettimeofday_cached(&tvnow); - if (connection_counts_as_relayed_traffic(conn, now)) { - connection_buckets_note_empty_ts(&global_relayed_read_emptied, - global_relayed_read_bucket, num_read, &tvnow); - connection_buckets_note_empty_ts(&global_relayed_write_emptied, - global_relayed_write_bucket, num_written, &tvnow); - } - connection_buckets_note_empty_ts(&global_read_emptied, - global_read_bucket, num_read, &tvnow); - connection_buckets_note_empty_ts(&global_write_emptied, - global_write_bucket, num_written, &tvnow); - if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { - or_connection_t *or_conn = TO_OR_CONN(conn); - connection_buckets_note_empty_ts(&or_conn->read_emptied_time, - or_conn->read_bucket, num_read, &tvnow); - connection_buckets_note_empty_ts(&or_conn->write_emptied_time, - or_conn->write_bucket, num_written, &tvnow); - } - } - - if (connection_counts_as_relayed_traffic(conn, now)) { - global_relayed_read_bucket -= (int)num_read; - global_relayed_write_bucket -= (int)num_written; - } - global_read_bucket -= (int)num_read; - global_write_bucket -= (int)num_written; - if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { - TO_OR_CONN(conn)->read_bucket -= (int)num_read; - TO_OR_CONN(conn)->write_bucket -= (int)num_written; - } -} - -/** If we have exhausted our global buckets, or the buckets for conn, - * stop reading. */ -static void -connection_consider_empty_read_buckets(connection_t *conn) -{ - const char *reason; - - if (!connection_is_rate_limited(conn)) - return; /* Always okay. */ - - if (global_read_bucket <= 0) { - reason = "global read bucket exhausted. Pausing."; - } else if (connection_counts_as_relayed_traffic(conn, approx_time()) && - global_relayed_read_bucket <= 0) { - reason = "global relayed read bucket exhausted. Pausing."; - } else if (connection_speaks_cells(conn) && - conn->state == OR_CONN_STATE_OPEN && - TO_OR_CONN(conn)->read_bucket <= 0) { - reason = "connection read bucket exhausted. Pausing."; - } else - return; /* all good, no need to stop it */ - - LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason)); - conn->read_blocked_on_bw = 1; - connection_stop_reading(conn); -} - -/** If we have exhausted our global buckets, or the buckets for conn, - * stop writing. */ -static void -connection_consider_empty_write_buckets(connection_t *conn) -{ - const char *reason; - - if (!connection_is_rate_limited(conn)) - return; /* Always okay. */ - - if (global_write_bucket <= 0) { - reason = "global write bucket exhausted. Pausing."; - } else if (connection_counts_as_relayed_traffic(conn, approx_time()) && - global_relayed_write_bucket <= 0) { - reason = "global relayed write bucket exhausted. Pausing."; - } else if (connection_speaks_cells(conn) && - conn->state == OR_CONN_STATE_OPEN && - TO_OR_CONN(conn)->write_bucket <= 0) { - reason = "connection write bucket exhausted. Pausing."; - } else - return; /* all good, no need to stop it */ - - LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason)); - conn->write_blocked_on_bw = 1; - connection_stop_writing(conn); -} - -/** Initialize the global read bucket to options-\>BandwidthBurst. */ -void -connection_bucket_init(void) -{ - const or_options_t *options = get_options(); - /* start it at max traffic */ - global_read_bucket = (int)options->BandwidthBurst; - global_write_bucket = (int)options->BandwidthBurst; - if (options->RelayBandwidthRate) { - global_relayed_read_bucket = (int)options->RelayBandwidthBurst; - global_relayed_write_bucket = (int)options->RelayBandwidthBurst; - } else { - global_relayed_read_bucket = (int)options->BandwidthBurst; - global_relayed_write_bucket = (int)options->BandwidthBurst; - } -} - -/** Refill a single bucket called name with bandwidth rate per - * second rate and bandwidth burst burst, assuming that - * milliseconds_elapsed milliseconds have passed since the last - * call. */ -static void -connection_bucket_refill_helper(int *bucket, int rate, int burst, - int milliseconds_elapsed, - const char *name) -{ - int starting_bucket = *bucket; - if (starting_bucket < burst && milliseconds_elapsed > 0) { - int64_t incr = (((int64_t)rate) * milliseconds_elapsed) / 1000; - if ((burst - starting_bucket) < incr) { - *bucket = burst; /* We would overflow the bucket; just set it to - * the maximum. */ - } else { - *bucket += (int)incr; - if (*bucket > burst || *bucket < starting_bucket) { - /* If we overflow the burst, or underflow our starting bucket, - * cap the bucket value to burst. */ - /* XXXX this might be redundant now, but it doesn't show up - * in profiles. Remove it after analysis. */ - *bucket = burst; - } - } - log_debug(LD_NET,"%s now %d.", name, *bucket); - } -} - -/** Helper: return the time in milliseconds since last_empty_time - * when a bucket ran empty that previously had tokens_before tokens - * now has tokens_after tokens after refilling at timestamp - * tvnow, capped at milliseconds_elapsed milliseconds since - * last refilling that bucket. Return 0 if the bucket has not been empty - * since the last refill or has not been refilled. */ -uint32_t -bucket_millis_empty(int tokens_before, uint32_t last_empty_time, - int tokens_after, int milliseconds_elapsed, - const struct timeval *tvnow) -{ - uint32_t result = 0, refilled; - if (tokens_before <= 0 && tokens_after > tokens_before) { - refilled = msec_since_midnight(tvnow); - result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) % - (86400L * 1000L)); - if (result > (uint32_t)milliseconds_elapsed) - result = (uint32_t)milliseconds_elapsed; - } - return result; -} - -/** Time has passed; increment buckets appropriately. */ -void -connection_bucket_refill(int milliseconds_elapsed, time_t now) -{ - const or_options_t *options = get_options(); - smartlist_t *conns = get_connection_array(); - int bandwidthrate, bandwidthburst, relayrate, relayburst; - - int prev_global_read = global_read_bucket; - int prev_global_write = global_write_bucket; - int prev_relay_read = global_relayed_read_bucket; - int prev_relay_write = global_relayed_write_bucket; - struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */ - - bandwidthrate = (int)options->BandwidthRate; - bandwidthburst = (int)options->BandwidthBurst; - - if (options->RelayBandwidthRate) { - relayrate = (int)options->RelayBandwidthRate; - relayburst = (int)options->RelayBandwidthBurst; - } else { - relayrate = bandwidthrate; - relayburst = bandwidthburst; - } - - tor_assert(milliseconds_elapsed >= 0); - - write_buckets_empty_last_second = - global_relayed_write_bucket <= 0 || global_write_bucket <= 0; - - /* refill the global buckets */ - connection_bucket_refill_helper(&global_read_bucket, - bandwidthrate, bandwidthburst, - milliseconds_elapsed, - "global_read_bucket"); - connection_bucket_refill_helper(&global_write_bucket, - bandwidthrate, bandwidthburst, - milliseconds_elapsed, - "global_write_bucket"); - connection_bucket_refill_helper(&global_relayed_read_bucket, - relayrate, relayburst, - milliseconds_elapsed, - "global_relayed_read_bucket"); - connection_bucket_refill_helper(&global_relayed_write_bucket, - relayrate, relayburst, - milliseconds_elapsed, - "global_relayed_write_bucket"); - - /* If buckets were empty before and have now been refilled, tell any - * interested controllers. */ - if (get_options()->TestingEnableTbEmptyEvent) { - uint32_t global_read_empty_time, global_write_empty_time, - relay_read_empty_time, relay_write_empty_time; - tor_gettimeofday_cached(&tvnow); - global_read_empty_time = bucket_millis_empty(prev_global_read, - global_read_emptied, global_read_bucket, - milliseconds_elapsed, &tvnow); - global_write_empty_time = bucket_millis_empty(prev_global_write, - global_write_emptied, global_write_bucket, - milliseconds_elapsed, &tvnow); - control_event_tb_empty("GLOBAL", global_read_empty_time, - global_write_empty_time, milliseconds_elapsed); - relay_read_empty_time = bucket_millis_empty(prev_relay_read, - global_relayed_read_emptied, - global_relayed_read_bucket, - milliseconds_elapsed, &tvnow); - relay_write_empty_time = bucket_millis_empty(prev_relay_write, - global_relayed_write_emptied, - global_relayed_write_bucket, - milliseconds_elapsed, &tvnow); - control_event_tb_empty("RELAY", relay_read_empty_time, - relay_write_empty_time, milliseconds_elapsed); - } - - /* refill the per-connection buckets */ - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (connection_speaks_cells(conn)) { - or_connection_t *or_conn = TO_OR_CONN(conn); - int orbandwidthrate = or_conn->bandwidthrate; - int orbandwidthburst = or_conn->bandwidthburst; - - int prev_conn_read = or_conn->read_bucket; - int prev_conn_write = or_conn->write_bucket; - - if (connection_bucket_should_increase(or_conn->read_bucket, or_conn)) { - connection_bucket_refill_helper(&or_conn->read_bucket, - orbandwidthrate, - orbandwidthburst, - milliseconds_elapsed, - "or_conn->read_bucket"); - } - if (connection_bucket_should_increase(or_conn->write_bucket, or_conn)) { - connection_bucket_refill_helper(&or_conn->write_bucket, - orbandwidthrate, - orbandwidthburst, - milliseconds_elapsed, - "or_conn->write_bucket"); - } - - /* If buckets were empty before and have now been refilled, tell any - * interested controllers. */ - if (get_options()->TestingEnableTbEmptyEvent) { - char *bucket; - uint32_t conn_read_empty_time, conn_write_empty_time; - tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT, - U64_PRINTF_ARG(or_conn->base_.global_identifier)); - conn_read_empty_time = bucket_millis_empty(prev_conn_read, - or_conn->read_emptied_time, - or_conn->read_bucket, - milliseconds_elapsed, &tvnow); - conn_write_empty_time = bucket_millis_empty(prev_conn_write, - or_conn->write_emptied_time, - or_conn->write_bucket, - milliseconds_elapsed, &tvnow); - control_event_tb_empty(bucket, conn_read_empty_time, - conn_write_empty_time, - milliseconds_elapsed); - tor_free(bucket); - } - } - - if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */ - && global_read_bucket > 0 /* and we're allowed to read */ - && (!connection_counts_as_relayed_traffic(conn, now) || - global_relayed_read_bucket > 0) /* even if we're relayed traffic */ - && (!connection_speaks_cells(conn) || - conn->state != OR_CONN_STATE_OPEN || - TO_OR_CONN(conn)->read_bucket > 0)) { - /* and either a non-cell conn or a cell conn with non-empty bucket */ - LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET, - "waking up conn (fd %d) for read", (int)conn->s)); - conn->read_blocked_on_bw = 0; - connection_start_reading(conn); - } - - if (conn->write_blocked_on_bw == 1 - && global_write_bucket > 0 /* and we're allowed to write */ - && (!connection_counts_as_relayed_traffic(conn, now) || - global_relayed_write_bucket > 0) /* even if it's relayed traffic */ - && (!connection_speaks_cells(conn) || - conn->state != OR_CONN_STATE_OPEN || - TO_OR_CONN(conn)->write_bucket > 0)) { - LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET, - "waking up conn (fd %d) for write", (int)conn->s)); - conn->write_blocked_on_bw = 0; - connection_start_writing(conn); - } - } SMARTLIST_FOREACH_END(conn); -} - -/** Is the bucket for connection conn low enough that we - * should add another pile of tokens to it? - */ -static int -connection_bucket_should_increase(int bucket, or_connection_t *conn) -{ - tor_assert(conn); - - if (conn->base_.state != OR_CONN_STATE_OPEN) - return 0; /* only open connections play the rate limiting game */ - if (bucket >= conn->bandwidthburst) - return 0; - - return 1; -} -#else -static void -connection_buckets_decrement(connection_t *conn, time_t now, - size_t num_read, size_t num_written) -{ - (void) conn; - (void) now; - (void) num_read; - (void) num_written; - /* Libevent does this for us. */ -} - -void -connection_bucket_refill(int seconds_elapsed, time_t now) -{ - (void) seconds_elapsed; - (void) now; - /* Libevent does this for us. */ -} -void -connection_bucket_init(void) -{ - const or_options_t *options = get_options(); - const struct timeval *tick = tor_libevent_get_one_tick_timeout(); - struct ev_token_bucket_cfg *bucket_cfg; - - uint64_t rate, burst; - if (options->RelayBandwidthRate) { - rate = options->RelayBandwidthRate; - burst = options->RelayBandwidthBurst; - } else { - rate = options->BandwidthRate; - burst = options->BandwidthBurst; - } - - /* This can't overflow, since TokenBucketRefillInterval <= 1000, - * and rate started out less than INT32_MAX. */ - rate = (rate * options->TokenBucketRefillInterval) / 1000; - - bucket_cfg = ev_token_bucket_cfg_new((uint32_t)rate, (uint32_t)burst, - (uint32_t)rate, (uint32_t)burst, - tick); - - if (!global_rate_limit) { - global_rate_limit = - bufferevent_rate_limit_group_new(tor_libevent_get_base(), bucket_cfg); - } else { - bufferevent_rate_limit_group_set_cfg(global_rate_limit, bucket_cfg); - } - ev_token_bucket_cfg_free(bucket_cfg); -} - -void -connection_get_rate_limit_totals(uint64_t *read_out, uint64_t *written_out) -{ - if (global_rate_limit == NULL) { - *read_out = *written_out = 0; - } else { - bufferevent_rate_limit_group_get_totals( - global_rate_limit, read_out, written_out); - } -} - -/** Perform whatever operations are needed on conn to enable - * rate-limiting. */ -void -connection_enable_rate_limiting(connection_t *conn) -{ - if (conn->bufev) { - if (!global_rate_limit) - connection_bucket_init(); - tor_add_bufferevent_to_rate_limit_group(conn->bufev, global_rate_limit); - } -} - -static void -connection_consider_empty_write_buckets(connection_t *conn) -{ - (void) conn; -} -static void -connection_consider_empty_read_buckets(connection_t *conn) -{ - (void) conn; -} -#endif - -/** Read bytes from conn-\>s and process them. - * - * It calls connection_read_to_buf() to bring in any new bytes, - * and then calls connection_process_inbuf() to process them. - * - * Mark the connection and return -1 if you want to close it, else - * return 0. - */ -static int -connection_handle_read_impl(connection_t *conn) -{ - ssize_t max_to_read=-1, try_to_read; - size_t before, n_read = 0; - int socket_error = 0; - - if (conn->marked_for_close) - return 0; /* do nothing */ - - conn->timestamp_lastread = approx_time(); - - switch (conn->type) { - case CONN_TYPE_OR_LISTENER: - return connection_handle_listener_read(conn, CONN_TYPE_OR); - case CONN_TYPE_EXT_OR_LISTENER: - return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR); - case CONN_TYPE_AP_LISTENER: - case CONN_TYPE_AP_TRANS_LISTENER: - case CONN_TYPE_AP_NATD_LISTENER: - return connection_handle_listener_read(conn, CONN_TYPE_AP); - case CONN_TYPE_DIR_LISTENER: - return connection_handle_listener_read(conn, CONN_TYPE_DIR); - case CONN_TYPE_CONTROL_LISTENER: - return connection_handle_listener_read(conn, CONN_TYPE_CONTROL); - case CONN_TYPE_AP_DNS_LISTENER: - /* This should never happen; eventdns.c handles the reads here. */ - tor_fragile_assert(); - return 0; - } - - loop_again: - try_to_read = max_to_read; - tor_assert(!conn->marked_for_close); - - before = buf_datalen(conn->inbuf); - if (connection_read_to_buf(conn, &max_to_read, &socket_error) < 0) { - /* There's a read error; kill the connection.*/ - if (conn->type == CONN_TYPE_OR) { - connection_or_notify_error(TO_OR_CONN(conn), - socket_error != 0 ? - errno_to_orconn_end_reason(socket_error) : - END_OR_CONN_REASON_CONNRESET, - socket_error != 0 ? - tor_socket_strerror(socket_error) : - "(unknown, errno was 0)"); - } - if (CONN_IS_EDGE(conn)) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - connection_edge_end_errno(edge_conn); - if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) { - /* broken, don't send a socks reply back */ - TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; - } - } - connection_close_immediate(conn); /* Don't flush; connection is dead. */ - /* - * This can bypass normal channel checking since we did - * connection_or_notify_error() above. - */ - connection_mark_for_close_internal(conn); - return -1; - } - n_read += buf_datalen(conn->inbuf) - before; - if (CONN_IS_EDGE(conn) && try_to_read != max_to_read) { - /* instruct it not to try to package partial cells. */ - if (connection_process_inbuf(conn, 0) < 0) { - return -1; - } - if (!conn->marked_for_close && - connection_is_reading(conn) && - !conn->inbuf_reached_eof && - max_to_read > 0) - goto loop_again; /* try reading again, in case more is here now */ - } - /* one last try, packaging partial cells and all. */ - if (!conn->marked_for_close && - connection_process_inbuf(conn, 1) < 0) { - return -1; - } - if (conn->linked_conn) { - /* The other side's handle_write() will never actually get called, so - * we need to invoke the appropriate callbacks ourself. */ - connection_t *linked = conn->linked_conn; - - if (n_read) { - /* Probably a no-op, since linked conns typically don't count for - * bandwidth rate limiting. But do it anyway so we can keep stats - * accurately. Note that since we read the bytes from conn, and - * we're writing the bytes onto the linked connection, we count - * these as written bytes. */ - connection_buckets_decrement(linked, approx_time(), 0, n_read); - - if (connection_flushed_some(linked) < 0) - connection_mark_for_close(linked); - if (!connection_wants_to_flush(linked)) - connection_finished_flushing(linked); - } - - if (!buf_datalen(linked->outbuf) && conn->active_on_link) - connection_stop_reading_from_linked_conn(conn); - } - /* If we hit the EOF, call connection_reached_eof(). */ - if (!conn->marked_for_close && - conn->inbuf_reached_eof && - connection_reached_eof(conn) < 0) { - return -1; - } - return 0; -} - -/* DOCDOC connection_handle_read */ -int -connection_handle_read(connection_t *conn) -{ - int res; - - tor_gettimeofday_cache_clear(); - res = connection_handle_read_impl(conn); - return res; -} - -/** Pull in new bytes from conn-\>s or conn-\>linked_conn onto conn-\>inbuf, - * either directly or via TLS. Reduce the token buckets by the number of bytes - * read. - * - * If *max_to_read is -1, then decide it ourselves, else go with the - * value passed to us. When returning, if it's changed, subtract the - * number of bytes we read from *max_to_read. - * - * Return -1 if we want to break conn, else return 0. - */ -static int -connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, - int *socket_error) -{ - int result; - ssize_t at_most = *max_to_read; - size_t slack_in_buf, more_to_read; - size_t n_read = 0, n_written = 0; - - if (at_most == -1) { /* we need to initialize it */ - /* how many bytes are we allowed to read? */ - at_most = connection_bucket_read_limit(conn, approx_time()); - } - - slack_in_buf = buf_slack(conn->inbuf); - again: - if ((size_t)at_most > slack_in_buf && slack_in_buf >= 1024) { - more_to_read = at_most - slack_in_buf; - at_most = slack_in_buf; - } else { - more_to_read = 0; - } - - if (connection_speaks_cells(conn) && - conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) { - int pending; - or_connection_t *or_conn = TO_OR_CONN(conn); - size_t initial_size; - if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING || - conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { - /* continue handshaking even if global token bucket is empty */ - return connection_tls_continue_handshake(or_conn); - } - - log_debug(LD_NET, - "%d: starting, inbuf_datalen %ld (%d pending in tls object)." - " at_most %ld.", - (int)conn->s,(long)buf_datalen(conn->inbuf), - tor_tls_get_pending_bytes(or_conn->tls), (long)at_most); - - initial_size = buf_datalen(conn->inbuf); - /* else open, or closing */ - result = read_to_buf_tls(or_conn->tls, at_most, conn->inbuf); - if (TOR_TLS_IS_ERROR(result) || result == TOR_TLS_CLOSE) - or_conn->tls_error = result; - else - or_conn->tls_error = 0; - - switch (result) { - case TOR_TLS_CLOSE: - case TOR_TLS_ERROR_IO: - log_debug(LD_NET,"TLS connection closed %son read. Closing. " - "(Nickname %s, address %s)", - result == TOR_TLS_CLOSE ? "cleanly " : "", - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); - return result; - CASE_TOR_TLS_ERROR_ANY_NONIO: - log_debug(LD_NET,"tls error [%s]. breaking (nickname %s, address %s).", - tor_tls_err_to_string(result), - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); - return result; - case TOR_TLS_WANTWRITE: - connection_start_writing(conn); - return 0; - case TOR_TLS_WANTREAD: - if (conn->in_connection_handle_write) { - /* We've been invoked from connection_handle_write, because we're - * waiting for a TLS renegotiation, the renegotiation started, and - * SSL_read returned WANTWRITE. But now SSL_read is saying WANTREAD - * again. Stop waiting for write events now, or else we'll - * busy-loop until data arrives for us to read. */ - connection_stop_writing(conn); - if (!connection_is_reading(conn)) - connection_start_reading(conn); - } - /* we're already reading, one hopes */ - result = 0; - break; - case TOR_TLS_DONE: /* no data read, so nothing to process */ - result = 0; - break; /* so we call bucket_decrement below */ - default: - break; - } - pending = tor_tls_get_pending_bytes(or_conn->tls); - if (pending) { - /* If we have any pending bytes, we read them now. This *can* - * take us over our read allotment, but really we shouldn't be - * believing that SSL bytes are the same as TCP bytes anyway. */ - int r2 = read_to_buf_tls(or_conn->tls, pending, conn->inbuf); - if (r2<0) { - log_warn(LD_BUG, "apparently, reading pending bytes can fail."); - return -1; - } - } - result = (int)(buf_datalen(conn->inbuf)-initial_size); - tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); - log_debug(LD_GENERAL, "After TLS read of %d: %ld read, %ld written", - result, (long)n_read, (long)n_written); - } else if (conn->linked) { - if (conn->linked_conn) { - result = move_buf_to_buf(conn->inbuf, conn->linked_conn->outbuf, - &conn->linked_conn->outbuf_flushlen); - } else { - result = 0; - } - //log_notice(LD_GENERAL, "Moved %d bytes on an internal link!", result); - /* If the other side has disappeared, or if it's been marked for close and - * we flushed its outbuf, then we should set our inbuf_reached_eof. */ - if (!conn->linked_conn || - (conn->linked_conn->marked_for_close && - buf_datalen(conn->linked_conn->outbuf) == 0)) - conn->inbuf_reached_eof = 1; - - n_read = (size_t) result; - } else { - /* !connection_speaks_cells, !conn->linked_conn. */ - int reached_eof = 0; - CONN_LOG_PROTECT(conn, - result = read_to_buf(conn->s, at_most, conn->inbuf, &reached_eof, - socket_error)); - if (reached_eof) - conn->inbuf_reached_eof = 1; - -// log_fn(LOG_DEBUG,"read_to_buf returned %d.",read_result); - - if (result < 0) - return -1; - n_read = (size_t) result; - } - - if (n_read > 0) { - /* change *max_to_read */ - *max_to_read = at_most - n_read; - - /* Update edge_conn->n_read and ocirc->n_read_circ_bw */ - if (conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - circuit_t *circ = circuit_get_by_edge_conn(edge_conn); - origin_circuit_t *ocirc; - - /* Check for overflow: */ - if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read)) - edge_conn->n_read += (int)n_read; - else - edge_conn->n_read = UINT32_MAX; - - if (circ && CIRCUIT_IS_ORIGIN(circ)) { - ocirc = TO_ORIGIN_CIRCUIT(circ); - if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read_circ_bw > n_read)) - ocirc->n_read_circ_bw += (int)n_read; - else - ocirc->n_read_circ_bw = UINT32_MAX; - } - } - - /* If CONN_BW events are enabled, update conn->n_read_conn_bw for - * OR/DIR/EXIT connections, checking for overflow. */ - if (get_options()->TestingEnableConnBwEvent && - (conn->type == CONN_TYPE_OR || - conn->type == CONN_TYPE_DIR || - conn->type == CONN_TYPE_EXIT)) { - if (PREDICT_LIKELY(UINT32_MAX - conn->n_read_conn_bw > n_read)) - conn->n_read_conn_bw += (int)n_read; - else - conn->n_read_conn_bw = UINT32_MAX; - } - } - - connection_buckets_decrement(conn, approx_time(), n_read, n_written); - - if (more_to_read && result == at_most) { - slack_in_buf = buf_slack(conn->inbuf); - at_most = more_to_read; - goto again; - } - - /* Call even if result is 0, since the global read bucket may - * have reached 0 on a different conn, and this guy needs to - * know to stop reading. */ - connection_consider_empty_read_buckets(conn); - if (n_written > 0 && connection_is_writing(conn)) - connection_consider_empty_write_buckets(conn); - - return 0; -} - -#ifdef USE_BUFFEREVENTS -/* XXXX These generic versions could be simplified by making them - type-specific */ - -/** Callback: Invoked whenever bytes are added to or drained from an input - * evbuffer. Used to track the number of bytes read. */ -static void -evbuffer_inbuf_callback(struct evbuffer *buf, - const struct evbuffer_cb_info *info, void *arg) -{ - connection_t *conn = arg; - (void) buf; - /* XXXX These need to get real counts on the non-nested TLS case. - NM */ - if (info->n_added) { - time_t now = approx_time(); - conn->timestamp_lastread = now; - record_num_bytes_transferred(conn, now, info->n_added, 0); - connection_consider_empty_read_buckets(conn); - if (conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - /*XXXX024 check for overflow*/ - edge_conn->n_read += (int)info->n_added; - } - } -} - -/** Callback: Invoked whenever bytes are added to or drained from an output - * evbuffer. Used to track the number of bytes written. */ -static void -evbuffer_outbuf_callback(struct evbuffer *buf, - const struct evbuffer_cb_info *info, void *arg) -{ - connection_t *conn = arg; - (void)buf; - if (info->n_deleted) { - time_t now = approx_time(); - conn->timestamp_lastwritten = now; - record_num_bytes_transferred(conn, now, 0, info->n_deleted); - connection_consider_empty_write_buckets(conn); - if (conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - /*XXXX024 check for overflow*/ - edge_conn->n_written += (int)info->n_deleted; - } - } -} - -/** Callback: invoked whenever a bufferevent has read data. */ -void -connection_handle_read_cb(struct bufferevent *bufev, void *arg) -{ - connection_t *conn = arg; - (void) bufev; - if (!conn->marked_for_close) { - if (connection_process_inbuf(conn, 1)<0) /* XXXX Always 1? */ - if (!conn->marked_for_close) - connection_mark_for_close(conn); - } -} - -/** Callback: invoked whenever a bufferevent has written data. */ -void -connection_handle_write_cb(struct bufferevent *bufev, void *arg) -{ - connection_t *conn = arg; - struct evbuffer *output; - if (connection_flushed_some(conn)<0) { - if (!conn->marked_for_close) - connection_mark_for_close(conn); - return; - } - - output = bufferevent_get_output(bufev); - if (!evbuffer_get_length(output)) { - connection_finished_flushing(conn); - if (conn->marked_for_close && conn->hold_open_until_flushed) { - conn->hold_open_until_flushed = 0; - if (conn->linked) { - /* send eof */ - bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED); - } - } - } -} - -/** Callback: invoked whenever a bufferevent has had an event (like a - * connection, or an eof, or an error) occur. */ -void -connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg) -{ - connection_t *conn = arg; - (void) bufev; - if (conn->marked_for_close) - return; - - if (event & BEV_EVENT_CONNECTED) { - tor_assert(connection_state_is_connecting(conn)); - if (connection_finished_connecting(conn)<0) - return; - } - if (event & BEV_EVENT_EOF) { - if (!conn->marked_for_close) { - conn->inbuf_reached_eof = 1; - if (connection_reached_eof(conn)<0) - return; - } - } - if (event & BEV_EVENT_ERROR) { - int socket_error = evutil_socket_geterror(conn->s); - if (conn->type == CONN_TYPE_OR && - conn->state == OR_CONN_STATE_CONNECTING) { - connection_or_connect_failed(TO_OR_CONN(conn), - errno_to_orconn_end_reason(socket_error), - tor_socket_strerror(socket_error)); - } else if (CONN_IS_EDGE(conn)) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - if (!edge_conn->edge_has_sent_end) - connection_edge_end_errno(edge_conn); - if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) { - /* broken, don't send a socks reply back */ - TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; - } - } - connection_close_immediate(conn); /* Connection is dead. */ - if (!conn->marked_for_close) - connection_mark_for_close(conn); - } -} - -/** Set up the generic callbacks for the bufferevent on conn. */ -void -connection_configure_bufferevent_callbacks(connection_t *conn) -{ - struct bufferevent *bufev; - struct evbuffer *input, *output; - tor_assert(conn->bufev); - bufev = conn->bufev; - bufferevent_setcb(bufev, - connection_handle_read_cb, - connection_handle_write_cb, - connection_handle_event_cb, - conn); - /* Set a fairly high write low-watermark so that we get the write callback - called whenever data is written to bring us under 128K. Leave the - high-watermark at 0. - */ - bufferevent_setwatermark(bufev, EV_WRITE, 128*1024, 0); - - input = bufferevent_get_input(bufev); - output = bufferevent_get_output(bufev); - evbuffer_add_cb(input, evbuffer_inbuf_callback, conn); - evbuffer_add_cb(output, evbuffer_outbuf_callback, conn); -} -#endif - -/** A pass-through to fetch_from_buf. */ -int -connection_fetch_from_buf(char *string, size_t len, connection_t *conn) -{ - IF_HAS_BUFFEREVENT(conn, { - /* XXX overflow -seb */ - return (int)bufferevent_read(conn->bufev, string, len); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_from_buf(string, len, conn->inbuf); - } -} - -/** As fetch_from_buf_line(), but read from a connection's input buffer. */ -int -connection_fetch_from_buf_line(connection_t *conn, char *data, - size_t *data_len) -{ - IF_HAS_BUFFEREVENT(conn, { - int r; - size_t eol_len=0; - struct evbuffer *input = bufferevent_get_input(conn->bufev); - struct evbuffer_ptr ptr = - evbuffer_search_eol(input, NULL, &eol_len, EVBUFFER_EOL_LF); - if (ptr.pos == -1) - return 0; /* No EOL found. */ - if ((size_t)ptr.pos+eol_len >= *data_len) { - return -1; /* Too long */ - } - *data_len = ptr.pos+eol_len; - r = evbuffer_remove(input, data, ptr.pos+eol_len); - tor_assert(r >= 0); - data[ptr.pos+eol_len] = '\0'; - return 1; - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_from_buf_line(conn->inbuf, data, data_len); - } -} - -/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or - * its bufferevent as appropriate. */ -int -connection_fetch_from_buf_http(connection_t *conn, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, - size_t max_bodylen, int force_complete) -{ - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return fetch_from_evbuffer_http(input, headers_out, max_headerlen, - body_out, body_used, max_bodylen, force_complete); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen, - body_out, body_used, max_bodylen, force_complete); - } -} - -/** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush - * from its outbuf. */ -int -connection_wants_to_flush(connection_t *conn) -{ - return conn->outbuf_flushlen > 0; -} - -/** Are there too many bytes on edge connection conn's outbuf to - * send back a relay-level sendme yet? Return 1 if so, 0 if not. Used by - * connection_edge_consider_sending_sendme(). - */ -int -connection_outbuf_too_full(connection_t *conn) -{ - return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); -} - -/** Try to flush more bytes onto conn-\>s. - * - * This function gets called either from conn_write_callback() in onion_main.c - * when libevent tells us that conn wants to write, or below - * from connection_write_to_buf() when an entire TLS record is ready. - * - * Update conn-\>timestamp_lastwritten to now, and call flush_buf - * or flush_buf_tls appropriately. If it succeeds and there are no more - * more bytes on conn-\>outbuf, then call connection_finished_flushing - * on it too. - * - * If force, then write as many bytes as possible, ignoring bandwidth - * limits. (Used for flushing messages to controller connections on fatal - * errors.) - * - * Mark the connection and return -1 if you want to close it, else - * return 0. - */ -static int -connection_handle_write_impl(connection_t *conn, int force) -{ - int e; - socklen_t len=(socklen_t)sizeof(e); - int result; - ssize_t max_to_write; - time_t now = approx_time(); - size_t n_read = 0, n_written = 0; - int dont_stop_writing = 0; - - tor_assert(!connection_is_listener(conn)); - - if (conn->marked_for_close || !SOCKET_OK(conn->s)) - return 0; /* do nothing */ - - if (conn->in_flushed_some) { - log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some"); - return 0; - } - - conn->timestamp_lastwritten = now; - - /* Sometimes, "writable" means "connected". */ - if (connection_state_is_connecting(conn)) { - if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { - log_warn(LD_BUG, "getsockopt() syscall failed"); - if (CONN_IS_EDGE(conn)) - connection_edge_end_errno(TO_EDGE_CONN(conn)); - connection_mark_for_close(conn); - return -1; - } - if (e) { - /* some sort of error, but maybe just inprogress still */ - if (!ERRNO_IS_CONN_EINPROGRESS(e)) { - log_info(LD_NET,"in-progress connect failed. Removing. (%s)", - tor_socket_strerror(e)); - if (CONN_IS_EDGE(conn)) - connection_edge_end_errno(TO_EDGE_CONN(conn)); - if (conn->type == CONN_TYPE_OR) - connection_or_notify_error(TO_OR_CONN(conn), - errno_to_orconn_end_reason(e), - tor_socket_strerror(e)); - - connection_close_immediate(conn); - /* - * This can bypass normal channel checking since we did - * connection_or_notify_error() above. - */ - connection_mark_for_close_internal(conn); - return -1; - } else { - return 0; /* no change, see if next time is better */ - } - } - /* The connection is successful. */ - if (connection_finished_connecting(conn)<0) - return -1; - } - - max_to_write = force ? (ssize_t)conn->outbuf_flushlen - : connection_bucket_write_limit(conn, now); - - if (connection_speaks_cells(conn) && - conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) { - or_connection_t *or_conn = TO_OR_CONN(conn); - size_t initial_size; - if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING || - conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { - connection_stop_writing(conn); - if (connection_tls_continue_handshake(or_conn) < 0) { - /* Don't flush; connection is dead. */ - connection_or_notify_error(or_conn, - END_OR_CONN_REASON_MISC, - "TLS error in connection_tls_" - "continue_handshake()"); - connection_close_immediate(conn); - /* - * This can bypass normal channel checking since we did - * connection_or_notify_error() above. - */ - connection_mark_for_close_internal(conn); - return -1; - } - return 0; - } else if (conn->state == OR_CONN_STATE_TLS_SERVER_RENEGOTIATING) { - return connection_handle_read(conn); - } - - /* else open, or closing */ - initial_size = buf_datalen(conn->outbuf); - result = flush_buf_tls(or_conn->tls, conn->outbuf, - max_to_write, &conn->outbuf_flushlen); - - /* If we just flushed the last bytes, tell the channel on the - * or_conn to check if it needs to geoip_change_dirreq_state() */ - /* XXXX move this to flushed_some or finished_flushing -NM */ - if (buf_datalen(conn->outbuf) == 0 && or_conn->chan) - channel_notify_flushed(TLS_CHAN_TO_BASE(or_conn->chan)); - - switch (result) { - CASE_TOR_TLS_ERROR_ANY: - case TOR_TLS_CLOSE: - log_info(LD_NET, result != TOR_TLS_CLOSE ? - "tls error. breaking.":"TLS connection closed on flush"); - /* Don't flush; connection is dead. */ - connection_or_notify_error(or_conn, - END_OR_CONN_REASON_MISC, - result != TOR_TLS_CLOSE ? - "TLS error in during flush" : - "TLS closed during flush"); - connection_close_immediate(conn); - /* - * This can bypass normal channel checking since we did - * connection_or_notify_error() above. - */ - connection_mark_for_close_internal(conn); - return -1; - case TOR_TLS_WANTWRITE: - log_debug(LD_NET,"wanted write."); - /* we're already writing */ - dont_stop_writing = 1; - break; - case TOR_TLS_WANTREAD: - /* Make sure to avoid a loop if the receive buckets are empty. */ - log_debug(LD_NET,"wanted read."); - if (!connection_is_reading(conn)) { - connection_stop_writing(conn); - conn->write_blocked_on_bw = 1; - /* we'll start reading again when we get more tokens in our - * read bucket; then we'll start writing again too. - */ - } - /* else no problem, we're already reading */ - return 0; - /* case TOR_TLS_DONE: - * for TOR_TLS_DONE, fall through to check if the flushlen - * is empty, so we can stop writing. - */ - } - - tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); - log_debug(LD_GENERAL, "After TLS write of %d: %ld read, %ld written", - result, (long)n_read, (long)n_written); - /* So we notice bytes were written even on error */ - /* XXXX024 This cast is safe since we can never write INT_MAX bytes in a - * single set of TLS operations. But it looks kinda ugly. If we refactor - * the *_buf_tls functions, we should make them return ssize_t or size_t - * or something. */ - result = (int)(initial_size-buf_datalen(conn->outbuf)); - } else { - CONN_LOG_PROTECT(conn, - result = flush_buf(conn->s, conn->outbuf, - max_to_write, &conn->outbuf_flushlen)); - if (result < 0) { - if (CONN_IS_EDGE(conn)) - connection_edge_end_errno(TO_EDGE_CONN(conn)); - if (conn->type == CONN_TYPE_AP) { - /* writing failed; we couldn't send a SOCKS reply if we wanted to */ - TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; - } - - connection_close_immediate(conn); /* Don't flush; connection is dead. */ - connection_mark_for_close(conn); - return -1; - } - n_written = (size_t) result; - } - - if (n_written && conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - circuit_t *circ = circuit_get_by_edge_conn(edge_conn); - origin_circuit_t *ocirc; - - /* Check for overflow: */ - if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written)) - edge_conn->n_written += (int)n_written; - else - edge_conn->n_written = UINT32_MAX; - - if (circ && CIRCUIT_IS_ORIGIN(circ)) { - ocirc = TO_ORIGIN_CIRCUIT(circ); - if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written_circ_bw > n_written)) - ocirc->n_written_circ_bw += (int)n_written; - else - ocirc->n_written_circ_bw = UINT32_MAX; - } - } - - /* If CONN_BW events are enabled, update conn->n_written_conn_bw for - * OR/DIR/EXIT connections, checking for overflow. */ - if (n_written && get_options()->TestingEnableConnBwEvent && - (conn->type == CONN_TYPE_OR || - conn->type == CONN_TYPE_DIR || - conn->type == CONN_TYPE_EXIT)) { - if (PREDICT_LIKELY(UINT32_MAX - conn->n_written_conn_bw > n_written)) - conn->n_written_conn_bw += (int)n_written; - else - conn->n_written_conn_bw = UINT32_MAX; - } - - connection_buckets_decrement(conn, approx_time(), n_read, n_written); - - if (result > 0) { - /* If we wrote any bytes from our buffer, then call the appropriate - * functions. */ - if (connection_flushed_some(conn) < 0) { - if (connection_speaks_cells(conn)) { - connection_or_notify_error(TO_OR_CONN(conn), - END_OR_CONN_REASON_MISC, - "Got error back from " - "connection_flushed_some()"); - } - - /* - * This can bypass normal channel checking since we did - * connection_or_notify_error() above. - */ - connection_mark_for_close_internal(conn); - } - } - - if (!connection_wants_to_flush(conn) && - !dont_stop_writing) { /* it's done flushing */ - if (connection_finished_flushing(conn) < 0) { - /* already marked */ - return -1; - } - return 0; - } - - /* Call even if result is 0, since the global write bucket may - * have reached 0 on a different conn, and this guy needs to - * know to stop writing. */ - connection_consider_empty_write_buckets(conn); - if (n_read > 0 && connection_is_reading(conn)) - connection_consider_empty_read_buckets(conn); - - return 0; -} - -/* DOCDOC connection_handle_write */ -int -connection_handle_write(connection_t *conn, int force) -{ - int res; - tor_gettimeofday_cache_clear(); - conn->in_connection_handle_write = 1; - res = connection_handle_write_impl(conn, force); - conn->in_connection_handle_write = 0; - return res; -} - -/** - * Try to flush data that's waiting for a write on conn. Return - * -1 on failure, 0 on success. - * - * Don't use this function for regular writing; the buffers/bufferevents - * system should be good enough at scheduling writes there. Instead, this - * function is for cases when we're about to exit or something and we want - * to report it right away. - */ -int -connection_flush(connection_t *conn) -{ - IF_HAS_BUFFEREVENT(conn, { - int r = bufferevent_flush(conn->bufev, EV_WRITE, BEV_FLUSH); - return (r < 0) ? -1 : 0; - }); - return connection_handle_write(conn, 1); -} - -/** Append len bytes of string onto conn's - * outbuf, and ask it to start writing. - * - * If zlib is nonzero, this is a directory connection that should get - * its contents compressed or decompressed as they're written. If zlib is - * negative, this is the last data to be compressed, and the connection's zlib - * state should be flushed. - * - * 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. - */ -MOCK_IMPL(void, -connection_write_to_buf_impl_,(const char *string, size_t len, - connection_t *conn, int zlib)) -{ - /* XXXX This function really needs to return -1 on failure. */ - int r; - size_t old_datalen; - if (!len && !(zlib<0)) - return; - /* if it's marked for close, only allow write if we mean to flush it */ - if (conn->marked_for_close && !conn->hold_open_until_flushed) - return; - - IF_HAS_BUFFEREVENT(conn, { - if (zlib) { - int done = zlib < 0; - r = write_to_evbuffer_zlib(bufferevent_get_output(conn->bufev), - TO_DIR_CONN(conn)->zlib_state, - string, len, done); - } else { - r = bufferevent_write(conn->bufev, string, len); - } - if (r < 0) { - /* XXXX mark for close? */ - log_warn(LD_NET, "bufferevent_write failed! That shouldn't happen."); - } - return; - }); - - old_datalen = buf_datalen(conn->outbuf); - if (zlib) { - dir_connection_t *dir_conn = TO_DIR_CONN(conn); - int done = zlib < 0; - CONN_LOG_PROTECT(conn, r = write_to_buf_zlib(conn->outbuf, - dir_conn->zlib_state, - string, len, done)); - } else { - CONN_LOG_PROTECT(conn, r = write_to_buf(string, len, conn->outbuf)); - } - if (r < 0) { - if (CONN_IS_EDGE(conn)) { - /* if it failed, it means we have our package/delivery windows set - wrong compared to our max outbuf size. close the whole circuit. */ - log_warn(LD_NET, - "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); - circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), - END_CIRC_REASON_INTERNAL); - } else { - log_warn(LD_NET, - "write_to_buf failed. Closing connection (fd %d).", - (int)conn->s); - connection_mark_for_close(conn); - } - return; - } - - /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING - * state, we don't want to try to write it right away, since - * conn->write_event won't be set yet. Otherwise, write data from - * this conn as the socket is available. */ - if (conn->write_event) { - connection_start_writing(conn); - } - if (zlib) { - conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; - } else { - conn->outbuf_flushlen += len; - - /* Should we try flushing the outbuf now? */ - if (conn->in_flushed_some) { - /* Don't flush the outbuf when the reason we're writing more stuff is - * _because_ we flushed the outbuf. That's unfair. */ - return; - } - - if (conn->type == CONN_TYPE_CONTROL && - !connection_is_rate_limited(conn) && - conn->outbuf_flushlen-len < 1<<16 && - conn->outbuf_flushlen >= 1<<16) { - /* just try to flush all of it */ - } else - return; /* no need to try flushing */ - - if (connection_handle_write(conn, 0) < 0) { - if (!conn->marked_for_close) { - /* this connection is broken. remove it. */ - log_warn(LD_BUG, "unhandled error on write for " - "conn (type %d, fd %d); removing", - conn->type, (int)conn->s); - tor_fragile_assert(); - /* do a close-immediate here, so we don't try to flush */ - connection_close_immediate(conn); - } - return; - } - } -} - -/** Return a connection with given type, address, port, and purpose; - * or NULL if no such connection exists. */ -connection_t * -connection_get_by_type_addr_port_purpose(int type, - const tor_addr_t *addr, uint16_t port, - int purpose) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && - tor_addr_eq(&conn->addr, addr) && - conn->port == port && - conn->purpose == purpose && - !conn->marked_for_close) - return conn; - }); - return NULL; -} - -/** Return the stream with id id if it is not already marked for - * close. - */ -connection_t * -connection_get_by_global_id(uint64_t id) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->global_identifier == id) - return conn; - }); - return NULL; -} - -/** Return a connection of type type that is not marked for close. - */ -connection_t * -connection_get_by_type(int type) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && !conn->marked_for_close) - return conn; - }); - return NULL; -} - -/** Return a connection of type type that is in state state, - * and that is not marked for close. - */ -connection_t * -connection_get_by_type_state(int type, int state) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && conn->state == state && !conn->marked_for_close) - return conn; - }); - return NULL; -} - -/** Return a connection of type type that has rendquery equal - * to rendquery, and that is not marked for close. If state - * is non-zero, conn must be of that state too. - */ -connection_t * -connection_get_by_type_state_rendquery(int type, int state, - const char *rendquery) -{ - smartlist_t *conns = get_connection_array(); - - tor_assert(type == CONN_TYPE_DIR || - type == CONN_TYPE_AP || type == CONN_TYPE_EXIT); - tor_assert(rendquery); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->type == type && - !conn->marked_for_close && - (!state || state == conn->state)) { - if (type == CONN_TYPE_DIR && - TO_DIR_CONN(conn)->rend_data && - !rend_cmp_service_ids(rendquery, - TO_DIR_CONN(conn)->rend_data->onion_address)) - return conn; - else if (CONN_IS_EDGE(conn) && - TO_EDGE_CONN(conn)->rend_data && - !rend_cmp_service_ids(rendquery, - TO_EDGE_CONN(conn)->rend_data->onion_address)) - return conn; - } - } SMARTLIST_FOREACH_END(conn); - return NULL; -} - -/** Return a directory connection (if any one exists) that is fetching - * the item described by state/resource */ -dir_connection_t * -connection_dir_get_by_purpose_and_resource(int purpose, - const char *resource) -{ - smartlist_t *conns = get_connection_array(); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - dir_connection_t *dirconn; - if (conn->type != CONN_TYPE_DIR || conn->marked_for_close || - conn->purpose != purpose) - continue; - dirconn = TO_DIR_CONN(conn); - if (dirconn->requested_resource == NULL) { - if (resource == NULL) - return dirconn; - } else if (resource) { - if (0 == strcmp(resource, dirconn->requested_resource)) - return dirconn; - } - } SMARTLIST_FOREACH_END(conn); - - return NULL; -} - -/** Return an open, non-marked connection of a given type and purpose, or NULL - * if no such connection exists. */ -connection_t * -connection_get_by_type_purpose(int type, int purpose) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && - !conn->marked_for_close && - (purpose == conn->purpose)) - return conn; - }); - return NULL; -} - -/** Return 1 if conn is a listener conn, else return 0. */ -int -connection_is_listener(connection_t *conn) -{ - if (conn->type == CONN_TYPE_OR_LISTENER || - conn->type == CONN_TYPE_EXT_OR_LISTENER || - conn->type == CONN_TYPE_AP_LISTENER || - conn->type == CONN_TYPE_AP_TRANS_LISTENER || - conn->type == CONN_TYPE_AP_DNS_LISTENER || - conn->type == CONN_TYPE_AP_NATD_LISTENER || - conn->type == CONN_TYPE_DIR_LISTENER || - conn->type == CONN_TYPE_CONTROL_LISTENER) - return 1; - return 0; -} - -/** Return 1 if conn is in state "open" and is not marked - * for close, else return 0. - */ -int -connection_state_is_open(connection_t *conn) -{ - tor_assert(conn); - - if (conn->marked_for_close) - return 0; - - if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) || - (conn->type == CONN_TYPE_EXT_OR) || - (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) || - (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) || - (conn->type == CONN_TYPE_CONTROL && - conn->state == CONTROL_CONN_STATE_OPEN)) - return 1; - - return 0; -} - -/** Return 1 if conn is in 'connecting' state, else return 0. */ -int -connection_state_is_connecting(connection_t *conn) -{ - tor_assert(conn); - - if (conn->marked_for_close) - return 0; - switch (conn->type) - { - case CONN_TYPE_OR: - return conn->state == OR_CONN_STATE_CONNECTING; - case CONN_TYPE_EXIT: - return conn->state == EXIT_CONN_STATE_CONNECTING; - case CONN_TYPE_DIR: - return conn->state == DIR_CONN_STATE_CONNECTING; - } - - return 0; -} - -/** Allocates a base64'ed authenticator for use in http or https - * auth, based on the input string authenticator. Returns it - * if success, else returns NULL. */ -char * -alloc_http_authenticator(const char *authenticator) -{ - /* an authenticator in Basic authentication - * is just the string "username:password" */ - const size_t authenticator_length = strlen(authenticator); - /* The base64_encode function needs a minimum buffer length - * of 66 bytes. */ - const size_t base64_authenticator_length = (authenticator_length/48+1)*66; - char *base64_authenticator = tor_malloc(base64_authenticator_length); - if (base64_encode(base64_authenticator, base64_authenticator_length, - authenticator, authenticator_length) < 0) { - tor_free(base64_authenticator); /* free and set to null */ - } else { - int i = 0, j = 0; - ssize_t len = strlen(base64_authenticator); - - /* remove all newline occurrences within the string */ - for (i=0; i < len; ++i) { - if ('\n' != base64_authenticator[i]) { - base64_authenticator[j] = base64_authenticator[i]; - ++j; - } - } - base64_authenticator[j]='\0'; - } - return base64_authenticator; -} - -/** Given a socket handle, check whether the local address (sockname) of the - * socket is one that we've connected from before. If so, double-check - * whether our address has changed and we need to generate keys. If we do, - * call init_keys(). - */ -static void -client_check_address_changed(tor_socket_t sock) -{ - struct sockaddr_storage out_sockaddr; - socklen_t out_addr_len = (socklen_t) sizeof(out_sockaddr); - tor_addr_t out_addr, iface_addr; - tor_addr_t **last_interface_ip_ptr; - sa_family_t family; - - if (!outgoing_addrs) - outgoing_addrs = smartlist_new(); - - if (getsockname(sock, (struct sockaddr*)&out_sockaddr, &out_addr_len)<0) { - int e = tor_socket_errno(sock); - log_warn(LD_NET, "getsockname() to check for address change failed: %s", - tor_socket_strerror(e)); - return; - } - tor_addr_from_sockaddr(&out_addr, (struct sockaddr*)&out_sockaddr, NULL); - family = tor_addr_family(&out_addr); - - if (family == AF_INET) - last_interface_ip_ptr = &last_interface_ipv4; - else if (family == AF_INET6) - last_interface_ip_ptr = &last_interface_ipv6; - else - return; - - if (! *last_interface_ip_ptr) { - tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t)); - if (get_interface_address6(LOG_INFO, family, a)==0) { - *last_interface_ip_ptr = a; - } else { - tor_free(a); - } - } - - /* If we've used this address previously, we're okay. */ - SMARTLIST_FOREACH(outgoing_addrs, const tor_addr_t *, a_ptr, - if (tor_addr_eq(a_ptr, &out_addr)) - return; - ); - - /* Uh-oh. We haven't connected from this address before. Has the interface - * address changed? */ - if (get_interface_address6(LOG_INFO, family, &iface_addr)<0) - return; - - if (tor_addr_eq(&iface_addr, *last_interface_ip_ptr)) { - /* Nope, it hasn't changed. Add this address to the list. */ - smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); - } else { - /* The interface changed. We're a client, so we need to regenerate our - * keys. First, reset the state. */ - log_notice(LD_NET, "Our IP address has changed. Rotating keys..."); - tor_addr_copy(*last_interface_ip_ptr, &iface_addr); - SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t*, a_ptr, tor_free(a_ptr)); - smartlist_clear(outgoing_addrs); - smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); - /* Okay, now change our keys. */ - ip_address_changed(1); - } -} - -/** Some systems have limited system buffers for recv and xmit on - * sockets allocated in a virtual server or similar environment. For a Tor - * server this can produce the "Error creating network socket: No buffer - * space available" error once all available TCP buffer space is consumed. - * This method will attempt to constrain the buffers allocated for the socket - * to the desired size to stay below system TCP buffer limits. - */ -static void -set_constrained_socket_buffers(tor_socket_t sock, int size) -{ - void *sz = (void*)&size; - socklen_t sz_sz = (socklen_t) sizeof(size); - if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, sz, sz_sz) < 0) { - int e = tor_socket_errno(sock); - log_warn(LD_NET, "setsockopt() to constrain send " - "buffer to %d bytes failed: %s", size, tor_socket_strerror(e)); - } - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, sz, sz_sz) < 0) { - int e = tor_socket_errno(sock); - log_warn(LD_NET, "setsockopt() to constrain recv " - "buffer to %d bytes failed: %s", size, tor_socket_strerror(e)); - } -} - -/** Process new bytes that have arrived on conn-\>inbuf. - * - * This function just passes conn to the connection-specific - * connection_*_process_inbuf() function. It also passes in - * package_partial if wanted. - */ -static int -connection_process_inbuf(connection_t *conn, int package_partial) -{ - tor_assert(conn); - - switch (conn->type) { - case CONN_TYPE_OR: - return connection_or_process_inbuf(TO_OR_CONN(conn)); - case CONN_TYPE_EXT_OR: - return connection_ext_or_process_inbuf(TO_OR_CONN(conn)); - case CONN_TYPE_EXIT: - case CONN_TYPE_AP: - return connection_edge_process_inbuf(TO_EDGE_CONN(conn), - package_partial); - case CONN_TYPE_DIR: - return connection_dir_process_inbuf(TO_DIR_CONN(conn)); - case CONN_TYPE_CPUWORKER: - return connection_cpu_process_inbuf(conn); - case CONN_TYPE_CONTROL: - return connection_control_process_inbuf(TO_CONTROL_CONN(conn)); - default: - log_err(LD_BUG,"got unexpected conn type %d.", conn->type); - tor_fragile_assert(); - return -1; - } -} - -/** Called whenever we've written data on a connection. */ -static int -connection_flushed_some(connection_t *conn) -{ - int r = 0; - tor_assert(!conn->in_flushed_some); - conn->in_flushed_some = 1; - if (conn->type == CONN_TYPE_DIR && - conn->state == DIR_CONN_STATE_SERVER_WRITING) { - r = connection_dirserv_flushed_some(TO_DIR_CONN(conn)); - } else if (conn->type == CONN_TYPE_OR) { - r = connection_or_flushed_some(TO_OR_CONN(conn)); - } else if (CONN_IS_EDGE(conn)) { - r = connection_edge_flushed_some(TO_EDGE_CONN(conn)); - } - conn->in_flushed_some = 0; - return r; -} - -/** We just finished flushing bytes to the appropriately low network layer, - * and there are no more bytes remaining in conn-\>outbuf, conn-\>bev, or - * conn-\>tls to be flushed. - * - * This function just passes conn to the connection-specific - * connection_*_finished_flushing() function. - */ -static int -connection_finished_flushing(connection_t *conn) -{ - tor_assert(conn); - - /* If the connection is closed, don't try to do anything more here. */ - if (CONN_IS_CLOSED(conn)) - return 0; - -// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s); - - IF_HAS_NO_BUFFEREVENT(conn) - connection_stop_writing(conn); - - switch (conn->type) { - case CONN_TYPE_OR: - return connection_or_finished_flushing(TO_OR_CONN(conn)); - case CONN_TYPE_EXT_OR: - return connection_ext_or_finished_flushing(TO_OR_CONN(conn)); - case CONN_TYPE_AP: - case CONN_TYPE_EXIT: - return connection_edge_finished_flushing(TO_EDGE_CONN(conn)); - case CONN_TYPE_DIR: - return connection_dir_finished_flushing(TO_DIR_CONN(conn)); - case CONN_TYPE_CPUWORKER: - return connection_cpu_finished_flushing(conn); - case CONN_TYPE_CONTROL: - return connection_control_finished_flushing(TO_CONTROL_CONN(conn)); - default: - log_err(LD_BUG,"got unexpected conn type %d.", conn->type); - tor_fragile_assert(); - return -1; - } -} - -/** Called when our attempt to connect() to another server has just - * succeeded. - * - * This function just passes conn to the connection-specific - * connection_*_finished_connecting() function. - */ -static int -connection_finished_connecting(connection_t *conn) -{ - tor_assert(conn); - - if (!server_mode(get_options())) { - /* See whether getsockname() says our address changed. We need to do this - * now that the connection has finished, because getsockname() on Windows - * won't work until then. */ - client_check_address_changed(conn->s); - } - - switch (conn->type) - { - case CONN_TYPE_OR: - return connection_or_finished_connecting(TO_OR_CONN(conn)); - case CONN_TYPE_EXIT: - return connection_edge_finished_connecting(TO_EDGE_CONN(conn)); - case CONN_TYPE_DIR: - return connection_dir_finished_connecting(TO_DIR_CONN(conn)); - default: - log_err(LD_BUG,"got unexpected conn type %d.", conn->type); - tor_fragile_assert(); - return -1; - } -} - -/** Callback: invoked when a connection reaches an EOF event. */ -static int -connection_reached_eof(connection_t *conn) -{ - switch (conn->type) { - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - return connection_or_reached_eof(TO_OR_CONN(conn)); - case CONN_TYPE_AP: - case CONN_TYPE_EXIT: - return connection_edge_reached_eof(TO_EDGE_CONN(conn)); - case CONN_TYPE_DIR: - return connection_dir_reached_eof(TO_DIR_CONN(conn)); - case CONN_TYPE_CPUWORKER: - return connection_cpu_reached_eof(conn); - case CONN_TYPE_CONTROL: - return connection_control_reached_eof(TO_CONTROL_CONN(conn)); - default: - log_err(LD_BUG,"got unexpected conn type %d.", conn->type); - tor_fragile_assert(); - return -1; - } -} - -/** Log how many bytes are used by buffers of different kinds and sizes. */ -void -connection_dump_buffer_mem_stats(int severity) -{ - uint64_t used_by_type[CONN_TYPE_MAX_+1]; - uint64_t alloc_by_type[CONN_TYPE_MAX_+1]; - int n_conns_by_type[CONN_TYPE_MAX_+1]; - uint64_t total_alloc = 0; - uint64_t total_used = 0; - int i; - smartlist_t *conns = get_connection_array(); - - memset(used_by_type, 0, sizeof(used_by_type)); - memset(alloc_by_type, 0, sizeof(alloc_by_type)); - memset(n_conns_by_type, 0, sizeof(n_conns_by_type)); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { - int tp = c->type; - ++n_conns_by_type[tp]; - if (c->inbuf) { - used_by_type[tp] += buf_datalen(c->inbuf); - alloc_by_type[tp] += buf_allocation(c->inbuf); - } - if (c->outbuf) { - used_by_type[tp] += buf_datalen(c->outbuf); - alloc_by_type[tp] += buf_allocation(c->outbuf); - } - } SMARTLIST_FOREACH_END(c); - for (i=0; i <= CONN_TYPE_MAX_; ++i) { - total_used += used_by_type[i]; - total_alloc += alloc_by_type[i]; - } - - tor_log(severity, LD_GENERAL, - "In buffers for %d connections: "U64_FORMAT" used/"U64_FORMAT" allocated", - smartlist_len(conns), - U64_PRINTF_ARG(total_used), U64_PRINTF_ARG(total_alloc)); - for (i=CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) { - if (!n_conns_by_type[i]) - continue; - tor_log(severity, LD_GENERAL, - " For %d %s connections: "U64_FORMAT" used/"U64_FORMAT" allocated", - n_conns_by_type[i], conn_type_to_string(i), - U64_PRINTF_ARG(used_by_type[i]), U64_PRINTF_ARG(alloc_by_type[i])); - } -} - -/** Verify that connection conn has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void -assert_connection_ok(connection_t *conn, time_t now) -{ - (void) now; /* XXXX unused. */ - tor_assert(conn); - tor_assert(conn->type >= CONN_TYPE_MIN_); - tor_assert(conn->type <= CONN_TYPE_MAX_); - -#ifdef USE_BUFFEREVENTS - if (conn->bufev) { - tor_assert(conn->read_event == NULL); - tor_assert(conn->write_event == NULL); - tor_assert(conn->inbuf == NULL); - tor_assert(conn->outbuf == NULL); - } -#endif - - switch (conn->type) { - case CONN_TYPE_OR: - case CONN_TYPE_EXT_OR: - tor_assert(conn->magic == OR_CONNECTION_MAGIC); - break; - case CONN_TYPE_AP: - tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC); - break; - case CONN_TYPE_EXIT: - tor_assert(conn->magic == EDGE_CONNECTION_MAGIC); - break; - case CONN_TYPE_DIR: - tor_assert(conn->magic == DIR_CONNECTION_MAGIC); - break; - case CONN_TYPE_CONTROL: - tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC); - break; - CASE_ANY_LISTENER_TYPE: - tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC); - break; - default: - tor_assert(conn->magic == BASE_CONNECTION_MAGIC); - break; - } - - if (conn->linked_conn) { - tor_assert(conn->linked_conn->linked_conn == conn); - tor_assert(conn->linked); - } - if (conn->linked) - tor_assert(!SOCKET_OK(conn->s)); - - if (conn->outbuf_flushlen > 0) { - /* With optimistic data, we may have queued data in - * EXIT_CONN_STATE_RESOLVING while the conn is not yet marked to writing. - * */ - tor_assert((conn->type == CONN_TYPE_EXIT && - conn->state == EXIT_CONN_STATE_RESOLVING) || - connection_is_writing(conn) || - conn->write_blocked_on_bw || - (CONN_IS_EDGE(conn) && - TO_EDGE_CONN(conn)->edge_blocked_on_circ)); - } - - if (conn->hold_open_until_flushed) - tor_assert(conn->marked_for_close); - - /* XXXX check: read_blocked_on_bw, write_blocked_on_bw, s, conn_array_index, - * marked_for_close. */ - - /* buffers */ - if (conn->inbuf) - assert_buf_ok(conn->inbuf); - if (conn->outbuf) - assert_buf_ok(conn->outbuf); - - if (conn->type == CONN_TYPE_OR) { - or_connection_t *or_conn = TO_OR_CONN(conn); - if (conn->state == OR_CONN_STATE_OPEN) { - /* tor_assert(conn->bandwidth > 0); */ - /* the above isn't necessarily true: if we just did a TLS - * handshake but we didn't recognize the other peer, or it - * gave a bad cert/etc, then we won't have assigned bandwidth, - * yet it will be open. -RD - */ -// tor_assert(conn->read_bucket >= 0); - } -// tor_assert(conn->addr && conn->port); - tor_assert(conn->address); - if (conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) - tor_assert(or_conn->tls); - } - - if (CONN_IS_EDGE(conn)) { - /* XXX unchecked: package window, deliver window. */ - if (conn->type == CONN_TYPE_AP) { - entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); - if (entry_conn->chosen_exit_optional || entry_conn->chosen_exit_retries) - tor_assert(entry_conn->chosen_exit_name); - - tor_assert(entry_conn->socks_request); - if (conn->state == AP_CONN_STATE_OPEN) { - tor_assert(entry_conn->socks_request->has_finished); - if (!conn->marked_for_close) { - tor_assert(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer); - assert_cpath_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer); - } - } - } - if (conn->type == CONN_TYPE_EXIT) { - tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT || - conn->purpose == EXIT_PURPOSE_RESOLVE); - } - } else if (conn->type == CONN_TYPE_DIR) { - } else { - /* Purpose is only used for dir and exit types currently */ - tor_assert(!conn->purpose); - } - - switch (conn->type) - { - CASE_ANY_LISTENER_TYPE: - tor_assert(conn->state == LISTENER_STATE_READY); - break; - case CONN_TYPE_OR: - tor_assert(conn->state >= OR_CONN_STATE_MIN_); - tor_assert(conn->state <= OR_CONN_STATE_MAX_); - break; - case CONN_TYPE_EXT_OR: - tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_); - tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_); - break; - case CONN_TYPE_EXIT: - tor_assert(conn->state >= EXIT_CONN_STATE_MIN_); - tor_assert(conn->state <= EXIT_CONN_STATE_MAX_); - tor_assert(conn->purpose >= EXIT_PURPOSE_MIN_); - tor_assert(conn->purpose <= EXIT_PURPOSE_MAX_); - break; - case CONN_TYPE_AP: - tor_assert(conn->state >= AP_CONN_STATE_MIN_); - tor_assert(conn->state <= AP_CONN_STATE_MAX_); - tor_assert(TO_ENTRY_CONN(conn)->socks_request); - break; - case CONN_TYPE_DIR: - tor_assert(conn->state >= DIR_CONN_STATE_MIN_); - tor_assert(conn->state <= DIR_CONN_STATE_MAX_); - tor_assert(conn->purpose >= DIR_PURPOSE_MIN_); - tor_assert(conn->purpose <= DIR_PURPOSE_MAX_); - break; - case CONN_TYPE_CPUWORKER: - tor_assert(conn->state >= CPUWORKER_STATE_MIN_); - tor_assert(conn->state <= CPUWORKER_STATE_MAX_); - break; - case CONN_TYPE_CONTROL: - tor_assert(conn->state >= CONTROL_CONN_STATE_MIN_); - tor_assert(conn->state <= CONTROL_CONN_STATE_MAX_); - break; - default: - tor_assert(0); - } -} - -/** Fills addr and port with the details of the global - * proxy server we are using. - * conn contains the connection we are using the proxy for. - * - * Return 0 on success, -1 on failure. - */ -int -get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, - const connection_t *conn) -{ - const or_options_t *options = get_options(); - - if (options->HTTPSProxy) { - tor_addr_copy(addr, &options->HTTPSProxyAddr); - *port = options->HTTPSProxyPort; - *proxy_type = PROXY_CONNECT; - return 0; - } else if (options->Socks4Proxy) { - tor_addr_copy(addr, &options->Socks4ProxyAddr); - *port = options->Socks4ProxyPort; - *proxy_type = PROXY_SOCKS4; - return 0; - } else if (options->Socks5Proxy) { - tor_addr_copy(addr, &options->Socks5ProxyAddr); - *port = options->Socks5ProxyPort; - *proxy_type = PROXY_SOCKS5; - return 0; - } else if (options->ClientTransportPlugin || - options->Bridges) { - const transport_t *transport = NULL; - int r; - r = get_transport_by_bridge_addrport(&conn->addr, conn->port, &transport); - if (r<0) - return -1; - if (transport) { /* transport found */ - tor_addr_copy(addr, &transport->addr); - *port = transport->port; - *proxy_type = transport->socks_version; - return 0; - } - } - - *proxy_type = PROXY_NONE; - return 0; -} - -/** Log a failed connection to a proxy server. - * conn is the connection we use the proxy server for. */ -void -log_failed_proxy_connection(connection_t *conn) -{ - tor_addr_t proxy_addr; - uint16_t proxy_port; - int proxy_type; - - if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, conn) != 0) - return; /* if we have no proxy set up, leave this function. */ - - log_warn(LD_NET, - "The connection to the %s proxy server at %s just failed. " - "Make sure that the proxy server is up and running.", - proxy_type_to_string(get_proxy_type()), - fmt_addrport(&proxy_addr, proxy_port)); -} - -/** Return string representation of proxy_type. */ -static const char * -proxy_type_to_string(int proxy_type) -{ - switch (proxy_type) { - case PROXY_CONNECT: return "HTTP"; - case PROXY_SOCKS4: return "SOCKS4"; - case PROXY_SOCKS5: return "SOCKS5"; - case PROXY_PLUGGABLE: return "pluggable transports SOCKS"; - case PROXY_NONE: return "NULL"; - default: tor_assert(0); - } - return NULL; /*Unreached*/ -} - -/** Call connection_free_() on every connection in our array, and release all - * storage held by connection.c. This is used by cpuworkers and dnsworkers - * when they fork, so they don't keep resources held open (especially - * sockets). - * - * Don't do the checks in connection_free(), because they will - * fail. - */ -void -connection_free_all(void) -{ - smartlist_t *conns = get_connection_array(); - - /* We don't want to log any messages to controllers. */ - SMARTLIST_FOREACH(conns, connection_t *, conn, - if (conn->type == CONN_TYPE_CONTROL) - TO_CONTROL_CONN(conn)->event_mask = 0); - - control_update_global_event_mask(); - - /* Unlink everything from the identity map. */ - connection_or_clear_identity_map(); - connection_or_clear_ext_or_id_map(); - - /* Clear out our list of broken connections */ - clear_broken_connection_map(0); - - SMARTLIST_FOREACH(conns, connection_t *, conn, connection_free_(conn)); - - if (outgoing_addrs) { - SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr)); - smartlist_free(outgoing_addrs); - outgoing_addrs = NULL; - } - - tor_free(last_interface_ipv4); - tor_free(last_interface_ipv6); - -#ifdef USE_BUFFEREVENTS - if (global_rate_limit) - bufferevent_rate_limit_group_free(global_rate_limit); -#endif -} - diff --git a/src/tor/connection.h b/src/tor/connection.h deleted file mode 100644 index 4073d9f..0000000 --- a/src/tor/connection.h +++ /dev/null @@ -1,231 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection.h - * \brief Header file for connection.c. - **/ - -#ifndef TOR_CONNECTION_H -#define TOR_CONNECTION_H - -/* XXXX For buf_datalen in inline function */ -#include "buffers.h" - -const char *conn_type_to_string(int type); -const char *conn_state_to_string(int type, int state); - -dir_connection_t *dir_connection_new(int socket_family); -or_connection_t *or_connection_new(int type, int socket_family); -edge_connection_t *edge_connection_new(int type, int socket_family); -entry_connection_t *entry_connection_new(int type, int socket_family); -control_connection_t *control_connection_new(int socket_family); -listener_connection_t *listener_connection_new(int type, int socket_family); -connection_t *connection_new(int type, int socket_family); - -void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -void connection_free(connection_t *conn); -void connection_free_all(void); -void connection_about_to_close_connection(connection_t *conn); -void connection_close_immediate(connection_t *conn); -void connection_mark_for_close_(connection_t *conn, - int line, const char *file); -void connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file); - -#define connection_mark_for_close(c) \ - connection_mark_for_close_((c), __LINE__, SHORT_FILE__) -#define connection_mark_for_close_internal(c) \ - connection_mark_for_close_internal_((c), __LINE__, SHORT_FILE__) - -/** - * Mark 'c' for close, but try to hold it open until all the data is written. - * Use the _internal versions of connection_mark_for_close; this should be - * called when you either are sure that if this is an or_connection_t the - * controlling channel has been notified (e.g. with - * connection_or_notify_error()), or you actually are the - * connection_or_close_for_error() or connection_or_close_normally function. - * For all other cases, use connection_mark_and_flush() instead, which - * checks for or_connection_t properly, instead. See below. - */ -#define connection_mark_and_flush_internal_(c,line,file) \ - do { \ - connection_t *tmp_conn_ = (c); \ - connection_mark_for_close_internal_(tmp_conn_, (line), (file)); \ - tmp_conn_->hold_open_until_flushed = 1; \ - IF_HAS_BUFFEREVENT(tmp_conn_, \ - connection_start_writing(tmp_conn_)); \ - } while (0) - -#define connection_mark_and_flush_internal(c) \ - connection_mark_and_flush_internal_((c), __LINE__, SHORT_FILE__) - -/** - * Mark 'c' for close, but try to hold it open until all the data is written. - */ -#define connection_mark_and_flush_(c,line,file) \ - do { \ - connection_t *tmp_conn_ = (c); \ - if (tmp_conn_->type == CONN_TYPE_OR) { \ - log_warn(LD_CHANNEL | LD_BUG, \ - "Something tried to close (and flush) an or_connection_t" \ - " without going through channels at %s:%d", \ - file, line); \ - connection_or_close_for_error(TO_OR_CONN(tmp_conn_), 1); \ - } else { \ - connection_mark_and_flush_internal_(c, line, file); \ - } \ - } while (0) - -#define connection_mark_and_flush(c) \ - connection_mark_and_flush_((c), __LINE__, SHORT_FILE__) - -void connection_expire_held_open(void); - -int connection_connect(connection_t *conn, const char *address, - const tor_addr_t *addr, - uint16_t port, int *socket_error); - -/** Maximum size of information that we can fit into SOCKS5 username - or password fields. */ -#define MAX_SOCKS5_AUTH_FIELD_SIZE 255 - -/** Total maximum size of information that we can fit into SOCKS5 - username and password fields. */ -#define MAX_SOCKS5_AUTH_SIZE_TOTAL 2*MAX_SOCKS5_AUTH_FIELD_SIZE - -int connection_proxy_connect(connection_t *conn, int type); -int connection_read_proxy_handshake(connection_t *conn); -void log_failed_proxy_connection(connection_t *conn); -int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, - const connection_t *conn); - -int retry_all_listeners(smartlist_t *replaced_conns, - smartlist_t *new_conns, - int close_all_noncontrol); - -void connection_mark_all_noncontrol_listeners(void); -void connection_mark_all_noncontrol_connections(void); - -ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); -int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); -void connection_bucket_init(void); -void connection_bucket_refill(int seconds_elapsed, time_t now); - -int connection_handle_read(connection_t *conn); - -int connection_fetch_from_buf(char *string, size_t len, connection_t *conn); -int connection_fetch_from_buf_line(connection_t *conn, char *data, - size_t *data_len); -int connection_fetch_from_buf_http(connection_t *conn, - char **headers_out, size_t max_headerlen, - char **body_out, size_t *body_used, - size_t max_bodylen, int force_complete); - -int connection_wants_to_flush(connection_t *conn); -int connection_outbuf_too_full(connection_t *conn); -int connection_handle_write(connection_t *conn, int force); -int connection_flush(connection_t *conn); - -MOCK_DECL(void, connection_write_to_buf_impl_, - (const char *string, size_t len, connection_t *conn, int zlib)); -/* DOCDOC connection_write_to_buf */ -static void connection_write_to_buf(const char *string, size_t len, - connection_t *conn); -/* DOCDOC connection_write_to_buf_zlib */ -static void connection_write_to_buf_zlib(const char *string, size_t len, - dir_connection_t *conn, int done); -static INLINE void -connection_write_to_buf(const char *string, size_t len, connection_t *conn) -{ - connection_write_to_buf_impl_(string, len, conn, 0); -} -static INLINE void -connection_write_to_buf_zlib(const char *string, size_t len, - dir_connection_t *conn, int done) -{ - connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1); -} - -/* DOCDOC connection_get_inbuf_len */ -static size_t connection_get_inbuf_len(connection_t *conn); -/* DOCDOC connection_get_outbuf_len */ -static size_t connection_get_outbuf_len(connection_t *conn); - -static INLINE size_t -connection_get_inbuf_len(connection_t *conn) -{ - IF_HAS_BUFFEREVENT(conn, { - return evbuffer_get_length(bufferevent_get_input(conn->bufev)); - }) ELSE_IF_NO_BUFFEREVENT { - return conn->inbuf ? buf_datalen(conn->inbuf) : 0; - } -} - -static INLINE size_t -connection_get_outbuf_len(connection_t *conn) -{ - IF_HAS_BUFFEREVENT(conn, { - return evbuffer_get_length(bufferevent_get_output(conn->bufev)); - }) ELSE_IF_NO_BUFFEREVENT { - return conn->outbuf ? buf_datalen(conn->outbuf) : 0; - } -} - -connection_t *connection_get_by_global_id(uint64_t id); - -connection_t *connection_get_by_type(int type); -connection_t *connection_get_by_type_purpose(int type, int purpose); -connection_t *connection_get_by_type_addr_port_purpose(int type, - const tor_addr_t *addr, - uint16_t port, int purpose); -connection_t *connection_get_by_type_state(int type, int state); -connection_t *connection_get_by_type_state_rendquery(int type, int state, - const char *rendquery); -dir_connection_t *connection_dir_get_by_purpose_and_resource( - int state, const char *resource); - -#define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR) -int connection_is_listener(connection_t *conn); -int connection_state_is_open(connection_t *conn); -int connection_state_is_connecting(connection_t *conn); - -char *alloc_http_authenticator(const char *authenticator); - -void assert_connection_ok(connection_t *conn, time_t now); -int connection_or_nonopen_was_started_here(or_connection_t *conn); -void connection_dump_buffer_mem_stats(int severity); -void remove_file_if_very_old(const char *fname, time_t now); - -#ifdef USE_BUFFEREVENTS -int connection_type_uses_bufferevent(connection_t *conn); -void connection_configure_bufferevent_callbacks(connection_t *conn); -void connection_handle_read_cb(struct bufferevent *bufev, void *arg); -void connection_handle_write_cb(struct bufferevent *bufev, void *arg); -void connection_handle_event_cb(struct bufferevent *bufev, short event, - void *arg); -void connection_get_rate_limit_totals(uint64_t *read_out, - uint64_t *written_out); -void connection_enable_rate_limiting(connection_t *conn); -#else -#define connection_type_uses_bufferevent(c) (0) -#endif - -#ifdef CONNECTION_PRIVATE -STATIC void connection_free_(connection_t *conn); - -/* Used only by connection.c and test*.c */ -uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, - int tokens_after, int milliseconds_elapsed, - const struct timeval *tvnow); -void connection_buckets_note_empty_ts(uint32_t *timestamp_var, - int tokens_before, - size_t tokens_removed, - const struct timeval *tvnow); -#endif - -#endif - diff --git a/src/tor/connection_edge.c b/src/tor/connection_edge.c deleted file mode 100644 index a794f37..0000000 --- a/src/tor/connection_edge.c +++ /dev/null @@ -1,3119 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection_edge.c - * \brief Handle edge streams. - **/ -#define CONNECTION_EDGE_PRIVATE - -#include "or.h" -#include "addressmap.h" -#include "buffers.h" -#include "channel.h" -#include "circpathbias.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "dns.h" -#include "dnsserv.h" -#include "dirserv.h" -#include "hibernate.h" -#include "onion_main.h" -#include "nodelist.h" -#include "policies.h" -#include "reasons.h" -#include "relay.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rendservice.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerset.h" -#include "circuitbuild.h" - -#ifdef HAVE_LINUX_TYPES_H -#include -#endif -#ifdef HAVE_LINUX_NETFILTER_IPV4_H -#include -#define TRANS_NETFILTER -#endif - -#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) -#include -#include -#define TRANS_PF -#endif - -#define SOCKS4_GRANTED 90 -#define SOCKS4_REJECT 91 - -static int connection_ap_handshake_process_socks(entry_connection_t *conn); -static int connection_ap_process_natd(entry_connection_t *conn); -static int connection_exit_connect_dir(edge_connection_t *exitconn); -static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port); -static int connection_ap_supports_optimistic_data(const entry_connection_t *); -static void connection_ap_handshake_socks_resolved_addr( - entry_connection_t *conn, - const tor_addr_t *answer, - int ttl, - time_t expires); - -/** An AP stream has failed/finished. If it hasn't already sent back - * a socks reply, send one now (based on endreason). Also set - * has_sent_end to 1, and mark the conn. - */ -void -connection_mark_unattached_ap_(entry_connection_t *conn, int endreason, - int line, const char *file) -{ - connection_t *base_conn = ENTRY_TO_CONN(conn); - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn); - tor_assert(base_conn->type == CONN_TYPE_AP); - ENTRY_TO_EDGE_CONN(conn)->edge_has_sent_end = 1; /* no circ yet */ - - /* If this is a rendezvous stream and it is failing without ever - * being attached to a circuit, assume that an attempt to connect to - * the destination hidden service has just ended. - * - * XXXX This condition doesn't limit to only streams failing - * without ever being attached. That sloppiness should be harmless, - * but we should fix it someday anyway. */ - if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) && - connection_edge_is_rendezvous_stream(edge_conn)) { - rend_client_note_connection_attempt_ended( - edge_conn->rend_data->onion_address); - } - - if (base_conn->marked_for_close) { - /* This call will warn as appropriate. */ - connection_mark_for_close_(base_conn, line, file); - return; - } - - if (!conn->socks_request->has_finished) { - if (endreason & END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED) - log_warn(LD_BUG, - "stream (marked at %s:%d) sending two socks replies?", - file, line); - - if (SOCKS_COMMAND_IS_CONNECT(conn->socks_request->command)) - connection_ap_handshake_socks_reply(conn, NULL, 0, endreason); - else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) - connection_ap_handshake_socks_resolved(conn, - RESOLVED_TYPE_ERROR_TRANSIENT, - 0, NULL, -1, -1); - else /* unknown or no handshake at all. send no response. */ - conn->socks_request->has_finished = 1; - } - - connection_mark_and_flush_(base_conn, line, file); - - ENTRY_TO_EDGE_CONN(conn)->end_reason = endreason; -} - -/** There was an EOF. Send an end and mark the connection for close. - */ -int -connection_edge_reached_eof(edge_connection_t *conn) -{ - if (connection_get_inbuf_len(TO_CONN(conn)) && - connection_state_is_open(TO_CONN(conn))) { - /* it still has stuff to process. don't let it die yet. */ - return 0; - } - log_info(LD_EDGE,"conn (fd "TOR_SOCKET_T_FORMAT") reached eof. Closing.", - conn->base_.s); - if (!conn->base_.marked_for_close) { - /* only mark it if not already marked. it's possible to - * get the 'end' right around when the client hangs up on us. */ - connection_edge_end(conn, END_STREAM_REASON_DONE); - if (conn->base_.type == CONN_TYPE_AP) { - /* eof, so don't send a socks reply back */ - if (EDGE_TO_ENTRY_CONN(conn)->socks_request) - EDGE_TO_ENTRY_CONN(conn)->socks_request->has_finished = 1; - } - connection_mark_for_close(TO_CONN(conn)); - } - return 0; -} - -/** Handle new bytes on conn->inbuf based on state: - * - If it's waiting for socks info, try to read another step of the - * socks handshake out of conn->inbuf. - * - If it's waiting for the original destination, fetch it. - * - If it's open, then package more relay cells from the stream. - * - Else, leave the bytes on inbuf alone for now. - * - * Mark and return -1 if there was an unexpected error with the conn, - * else return 0. - */ -int -connection_edge_process_inbuf(edge_connection_t *conn, int package_partial) -{ - tor_assert(conn); - - switch (conn->base_.state) { - case AP_CONN_STATE_SOCKS_WAIT: - if (connection_ap_handshake_process_socks(EDGE_TO_ENTRY_CONN(conn)) <0) { - /* already marked */ - return -1; - } - return 0; - case AP_CONN_STATE_NATD_WAIT: - if (connection_ap_process_natd(EDGE_TO_ENTRY_CONN(conn)) < 0) { - /* already marked */ - return -1; - } - return 0; - case AP_CONN_STATE_OPEN: - case EXIT_CONN_STATE_OPEN: - if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) { - /* (We already sent an end cell if possible) */ - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - return 0; - case AP_CONN_STATE_CONNECT_WAIT: - if (connection_ap_supports_optimistic_data(EDGE_TO_ENTRY_CONN(conn))) { - log_info(LD_EDGE, - "data from edge while in '%s' state. Sending it anyway. " - "package_partial=%d, buflen=%ld", - conn_state_to_string(conn->base_.type, conn->base_.state), - package_partial, - (long)connection_get_inbuf_len(TO_CONN(conn))); - if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) { - /* (We already sent an end cell if possible) */ - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - return 0; - } - /* Fall through if the connection is on a circuit without optimistic - * data support. */ - case EXIT_CONN_STATE_CONNECTING: - case AP_CONN_STATE_RENDDESC_WAIT: - case AP_CONN_STATE_CIRCUIT_WAIT: - case AP_CONN_STATE_RESOLVE_WAIT: - case AP_CONN_STATE_CONTROLLER_WAIT: - log_info(LD_EDGE, - "data from edge while in '%s' state. Leaving it on buffer.", - conn_state_to_string(conn->base_.type, conn->base_.state)); - return 0; - } - log_warn(LD_BUG,"Got unexpected state %d. Closing.",conn->base_.state); - tor_fragile_assert(); - connection_edge_end(conn, END_STREAM_REASON_INTERNAL); - connection_mark_for_close(TO_CONN(conn)); - return -1; -} - -/** This edge needs to be closed, because its circuit has closed. - * Mark it for close and return 0. - */ -int -connection_edge_destroy(circid_t circ_id, edge_connection_t *conn) -{ - if (!conn->base_.marked_for_close) { - log_info(LD_EDGE, "CircID %u: At an edge. Marking connection for close.", - (unsigned) circ_id); - if (conn->base_.type == CONN_TYPE_AP) { - entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn); - connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_DESTROY); - control_event_stream_bandwidth(conn); - control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED, - END_STREAM_REASON_DESTROY); - conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED; - } else { - /* closing the circuit, nothing to send an END to */ - conn->edge_has_sent_end = 1; - conn->end_reason = END_STREAM_REASON_DESTROY; - conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED; - connection_mark_and_flush(TO_CONN(conn)); - } - } - conn->cpath_layer = NULL; - conn->on_circuit = NULL; - return 0; -} - -/** Send a raw end cell to the stream with ID stream_id out over the - * circ towards the hop identified with cpath_layer. If this - * is not a client connection, set the relay end cell's reason for closing - * as reason */ -static int -relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ, - uint8_t reason, crypt_path_t *cpath_layer) -{ - char payload[1]; - - if (CIRCUIT_PURPOSE_IS_CLIENT(circ->purpose)) { - /* Never send the server an informative reason code; it doesn't need to - * know why the client stream is failing. */ - reason = END_STREAM_REASON_MISC; - } - - payload[0] = (char) reason; - - return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END, - payload, 1, cpath_layer); -} - -/** Send a relay end cell from stream conn down conn's circuit, and - * remember that we've done so. If this is not a client connection, set the - * relay end cell's reason for closing as reason. - * - * Return -1 if this function has already been called on this conn, - * else return 0. - */ -int -connection_edge_end(edge_connection_t *conn, uint8_t reason) -{ - char payload[RELAY_PAYLOAD_SIZE]; - size_t payload_len=1; - circuit_t *circ; - uint8_t control_reason = reason; - - if (conn->edge_has_sent_end) { - log_warn(LD_BUG,"(Harmless.) Calling connection_edge_end (reason %d) " - "on an already ended stream?", reason); - tor_fragile_assert(); - return -1; - } - - if (conn->base_.marked_for_close) { - log_warn(LD_BUG, - "called on conn that's already marked for close at %s:%d.", - conn->base_.marked_for_close_file, conn->base_.marked_for_close); - return 0; - } - - circ = circuit_get_by_edge_conn(conn); - if (circ && CIRCUIT_PURPOSE_IS_CLIENT(circ->purpose)) { - /* If this is a client circuit, don't send the server an informative - * reason code; it doesn't need to know why the client stream is - * failing. */ - reason = END_STREAM_REASON_MISC; - } - - payload[0] = (char)reason; - if (reason == END_STREAM_REASON_EXITPOLICY && - !connection_edge_is_rendezvous_stream(conn)) { - int addrlen; - if (tor_addr_family(&conn->base_.addr) == AF_INET) { - set_uint32(payload+1, tor_addr_to_ipv4n(&conn->base_.addr)); - addrlen = 4; - } else { - memcpy(payload+1, tor_addr_to_in6_addr8(&conn->base_.addr), 16); - addrlen = 16; - } - set_uint32(payload+1+addrlen, htonl(dns_clip_ttl(conn->address_ttl))); - payload_len += 4+addrlen; - } - - if (circ && !circ->marked_for_close) { - log_debug(LD_EDGE,"Sending end on conn (fd "TOR_SOCKET_T_FORMAT").", - conn->base_.s); - connection_edge_send_command(conn, RELAY_COMMAND_END, - payload, payload_len); - } else { - log_debug(LD_EDGE,"No circ to send end on conn " - "(fd "TOR_SOCKET_T_FORMAT").", - conn->base_.s); - } - - conn->edge_has_sent_end = 1; - conn->end_reason = control_reason; - return 0; -} - -/** An error has just occurred on an operation on an edge connection - * conn. Extract the errno; convert it to an end reason, and send an - * appropriate relay end cell to the other end of the connection's circuit. - **/ -int -connection_edge_end_errno(edge_connection_t *conn) -{ - uint8_t reason; - tor_assert(conn); - reason = errno_to_stream_end_reason(tor_socket_errno(conn->base_.s)); - return connection_edge_end(conn, reason); -} - -/** We just wrote some data to conn; act appropriately. - * - * (That is, if it's open, consider sending a stream-level sendme cell if we - * have just flushed enough.) - */ -int -connection_edge_flushed_some(edge_connection_t *conn) -{ - switch (conn->base_.state) { - case AP_CONN_STATE_OPEN: - case EXIT_CONN_STATE_OPEN: - connection_edge_consider_sending_sendme(conn); - break; - } - return 0; -} - -/** Connection conn has finished writing and has no bytes left on - * its outbuf. - * - * If it's in state 'open', stop writing, consider responding with a - * sendme, and return. - * Otherwise, stop writing and return. - * - * If conn is broken, mark it for close and return -1, else - * return 0. - */ -int -connection_edge_finished_flushing(edge_connection_t *conn) -{ - tor_assert(conn); - - switch (conn->base_.state) { - case AP_CONN_STATE_OPEN: - case EXIT_CONN_STATE_OPEN: - connection_edge_consider_sending_sendme(conn); - return 0; - case AP_CONN_STATE_SOCKS_WAIT: - case AP_CONN_STATE_NATD_WAIT: - case AP_CONN_STATE_RENDDESC_WAIT: - case AP_CONN_STATE_CIRCUIT_WAIT: - case AP_CONN_STATE_CONNECT_WAIT: - case AP_CONN_STATE_CONTROLLER_WAIT: - case AP_CONN_STATE_RESOLVE_WAIT: - return 0; - default: - log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state); - tor_fragile_assert(); - return -1; - } - return 0; -} - -/** Longest size for the relay payload of a RELAY_CONNECTED cell that we're - * able to generate. */ -/* 4 zero bytes; 1 type byte; 16 byte IPv6 address; 4 byte TTL. */ -#define MAX_CONNECTED_CELL_PAYLOAD_LEN 25 - -/** Set the buffer at payload_out -- which must have at least - * MAX_CONNECTED_CELL_PAYLOAD_LEN bytes available -- to the body of a - * RELAY_CONNECTED cell indicating that we have connected to addr, and - * that the name resolution that led us to addr will be valid for - * ttl seconds. Return -1 on error, or the number of bytes used on - * success. */ -STATIC int -connected_cell_format_payload(uint8_t *payload_out, - const tor_addr_t *addr, - uint32_t ttl) -{ - const sa_family_t family = tor_addr_family(addr); - int connected_payload_len; - - /* should be needless */ - memset(payload_out, 0, MAX_CONNECTED_CELL_PAYLOAD_LEN); - - if (family == AF_INET) { - set_uint32(payload_out, tor_addr_to_ipv4n(addr)); - connected_payload_len = 4; - } else if (family == AF_INET6) { - set_uint32(payload_out, 0); - set_uint8(payload_out + 4, 6); - memcpy(payload_out + 5, tor_addr_to_in6_addr8(addr), 16); - connected_payload_len = 21; - } else { - return -1; - } - - set_uint32(payload_out + connected_payload_len, htonl(dns_clip_ttl(ttl))); - connected_payload_len += 4; - - tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN); - - return connected_payload_len; -} - -/** Connected handler for exit connections: start writing pending - * data, deliver 'CONNECTED' relay cells as appropriate, and check - * any pending data that may have been received. */ -int -connection_edge_finished_connecting(edge_connection_t *edge_conn) -{ - connection_t *conn; - - tor_assert(edge_conn); - tor_assert(edge_conn->base_.type == CONN_TYPE_EXIT); - conn = TO_CONN(edge_conn); - tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING); - - log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.", - escaped_safe_str(conn->address), conn->port, - safe_str(fmt_and_decorate_addr(&conn->addr))); - - rep_hist_note_exit_stream_opened(conn->port); - - conn->state = EXIT_CONN_STATE_OPEN; - IF_HAS_NO_BUFFEREVENT(conn) - connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ - if (connection_get_outbuf_len(conn)) /* in case there are any queued relay - * cells */ - connection_start_writing(conn); - /* deliver a 'connected' relay cell back through the circuit. */ - if (connection_edge_is_rendezvous_stream(edge_conn)) { - if (connection_edge_send_command(edge_conn, - RELAY_COMMAND_CONNECTED, NULL, 0) < 0) - return 0; /* circuit is closed, don't continue */ - } else { - uint8_t connected_payload[MAX_CONNECTED_CELL_PAYLOAD_LEN]; - int connected_payload_len = - connected_cell_format_payload(connected_payload, &conn->addr, - edge_conn->address_ttl); - if (connected_payload_len < 0) - return -1; - - if (connection_edge_send_command(edge_conn, - RELAY_COMMAND_CONNECTED, - (char*)connected_payload, connected_payload_len) < 0) - return 0; /* circuit is closed, don't continue */ - } - tor_assert(edge_conn->package_window > 0); - /* in case the server has written anything */ - return connection_edge_process_inbuf(edge_conn, 1); -} - -/** Common code to connection_(ap|exit)_about_to_close. */ -static void -connection_edge_about_to_close(edge_connection_t *edge_conn) -{ - if (!edge_conn->edge_has_sent_end) { - connection_t *conn = TO_CONN(edge_conn); - log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) " - "hasn't sent end yet?", - conn->marked_for_close_file, conn->marked_for_close); - tor_fragile_assert(); - } -} - -/** Called when we're about to finally unlink and free an AP (client) - * connection: perform necessary accounting and cleanup */ -void -connection_ap_about_to_close(entry_connection_t *entry_conn) -{ - circuit_t *circ; - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); - connection_t *conn = ENTRY_TO_CONN(entry_conn); - - if (entry_conn->socks_request->has_finished == 0) { - /* since conn gets removed right after this function finishes, - * there's no point trying to send back a reply at this point. */ - log_warn(LD_BUG,"Closing stream (marked at %s:%d) without sending" - " back a socks reply.", - conn->marked_for_close_file, conn->marked_for_close); - } - if (!edge_conn->end_reason) { - log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having" - " set end_reason.", - conn->marked_for_close_file, conn->marked_for_close); - } - if (entry_conn->dns_server_request) { - log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having" - " replied to DNS request.", - conn->marked_for_close_file, conn->marked_for_close); - dnsserv_reject_request(entry_conn); - } - control_event_stream_bandwidth(edge_conn); - control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED, - edge_conn->end_reason); - circ = circuit_get_by_edge_conn(edge_conn); - if (circ) - circuit_detach_stream(circ, edge_conn); -} - -/** Called when we're about to finally unlink and free an exit - * connection: perform necessary accounting and cleanup */ -void -connection_exit_about_to_close(edge_connection_t *edge_conn) -{ - circuit_t *circ; - connection_t *conn = TO_CONN(edge_conn); - - connection_edge_about_to_close(edge_conn); - - circ = circuit_get_by_edge_conn(edge_conn); - if (circ) - circuit_detach_stream(circ, edge_conn); - if (conn->state == EXIT_CONN_STATE_RESOLVING) { - connection_dns_remove(edge_conn); - } -} - -/** Define a schedule for how long to wait between retrying - * application connections. Rather than waiting a fixed amount of - * time between each retry, we wait 10 seconds each for the first - * two tries, and 15 seconds for each retry after - * that. Hopefully this will improve the expected user experience. */ -static int -compute_retry_timeout(entry_connection_t *conn) -{ - int timeout = get_options()->CircuitStreamTimeout; - if (timeout) /* if our config options override the default, use them */ - return timeout; - if (conn->num_socks_retries < 2) /* try 0 and try 1 */ - return 10; - return 15; -} - -/** Find all general-purpose AP streams waiting for a response that sent their - * begin/resolve cell too long ago. Detach from their current circuit, and - * mark their current circuit as unsuitable for new streams. Then call - * connection_ap_handshake_attach_circuit() to attach to a new circuit (if - * available) or launch a new one. - * - * For rendezvous streams, simply give up after SocksTimeout seconds (with no - * retry attempt). - */ -void -connection_ap_expire_beginning(void) -{ - edge_connection_t *conn; - entry_connection_t *entry_conn; - circuit_t *circ; - time_t now = time(NULL); - const or_options_t *options = get_options(); - int severity; - int cutoff; - int seconds_idle, seconds_since_born; - smartlist_t *conns = get_connection_array(); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { - if (base_conn->type != CONN_TYPE_AP || base_conn->marked_for_close) - continue; - entry_conn = TO_ENTRY_CONN(base_conn); - conn = ENTRY_TO_EDGE_CONN(entry_conn); - /* if it's an internal linked connection, don't yell its status. */ - severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) - ? LOG_INFO : LOG_NOTICE; - seconds_idle = (int)( now - base_conn->timestamp_lastread ); - seconds_since_born = (int)( now - base_conn->timestamp_created ); - - if (base_conn->state == AP_CONN_STATE_OPEN) - continue; - - /* We already consider SocksTimeout in - * connection_ap_handshake_attach_circuit(), but we need to consider - * it here too because controllers that put streams in controller_wait - * state never ask Tor to attach the circuit. */ - if (AP_CONN_STATE_IS_UNATTACHED(base_conn->state)) { - if (seconds_since_born >= options->SocksTimeout) { - log_fn(severity, LD_APP, - "Tried for %d seconds to get a connection to %s:%d. " - "Giving up. (%s)", - seconds_since_born, - safe_str_client(entry_conn->socks_request->address), - entry_conn->socks_request->port, - conn_state_to_string(CONN_TYPE_AP, base_conn->state)); - connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT); - } - continue; - } - - /* We're in state connect_wait or resolve_wait now -- waiting for a - * reply to our relay cell. See if we want to retry/give up. */ - - cutoff = compute_retry_timeout(entry_conn); - if (seconds_idle < cutoff) - continue; - circ = circuit_get_by_edge_conn(conn); - if (!circ) { /* it's vanished? */ - log_info(LD_APP,"Conn is waiting (address %s), but lost its circ.", - safe_str_client(entry_conn->socks_request->address)); - connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT); - continue; - } - if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { - if (seconds_idle >= options->SocksTimeout) { - log_fn(severity, LD_REND, - "Rend stream is %d seconds late. Giving up on address" - " '%s.onion'.", - seconds_idle, - safe_str_client(entry_conn->socks_request->address)); - /* Roll back path bias use state so that we probe the circuit - * if nothing else succeeds on it */ - pathbias_mark_use_rollback(TO_ORIGIN_CIRCUIT(circ)); - - connection_edge_end(conn, END_STREAM_REASON_TIMEOUT); - connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT); - } - continue; - } - if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL && - circ->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT && - circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) { - log_warn(LD_BUG, "circuit->purpose == CIRCUIT_PURPOSE_C_GENERAL failed. " - "The purpose on the circuit was %s; it was in state %s, " - "path_state %s.", - circuit_purpose_to_string(circ->purpose), - circuit_state_to_string(circ->state), - CIRCUIT_IS_ORIGIN(circ) ? - pathbias_state_to_string(TO_ORIGIN_CIRCUIT(circ)->path_state) : - "none"); - } - log_fn(cutoff < 15 ? LOG_INFO : severity, LD_APP, - "We tried for %d seconds to connect to '%s' using exit %s." - " Retrying on a new circuit.", - seconds_idle, - safe_str_client(entry_conn->socks_request->address), - conn->cpath_layer ? - extend_info_describe(conn->cpath_layer->extend_info): - "*unnamed*"); - /* send an end down the circuit */ - connection_edge_end(conn, END_STREAM_REASON_TIMEOUT); - /* un-mark it as ending, since we're going to reuse it */ - conn->edge_has_sent_end = 0; - conn->end_reason = 0; - /* make us not try this circuit again, but allow - * current streams on it to survive if they can */ - mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); - - /* give our stream another 'cutoff' seconds to try */ - conn->base_.timestamp_lastread += cutoff; - if (entry_conn->num_socks_retries < 250) /* avoid overflow */ - entry_conn->num_socks_retries++; - /* move it back into 'pending' state, and try to attach. */ - if (connection_ap_detach_retriable(entry_conn, TO_ORIGIN_CIRCUIT(circ), - END_STREAM_REASON_TIMEOUT)<0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(entry_conn, - END_STREAM_REASON_CANT_ATTACH); - } - } SMARTLIST_FOREACH_END(base_conn); -} - -/** Tell any AP streams that are waiting for a new circuit to try again, - * either attaching to an available circ or launching a new one. - */ -void -connection_ap_attach_pending(void) -{ - entry_connection_t *entry_conn; - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->marked_for_close || - conn->type != CONN_TYPE_AP || - conn->state != AP_CONN_STATE_CIRCUIT_WAIT) - continue; - entry_conn = TO_ENTRY_CONN(conn); - if (connection_ap_handshake_attach_circuit(entry_conn) < 0) { - if (!conn->marked_for_close) - connection_mark_unattached_ap(entry_conn, - END_STREAM_REASON_CANT_ATTACH); - } - } SMARTLIST_FOREACH_END(conn); -} - -/** Tell any AP streams that are waiting for a one-hop tunnel to - * failed_digest that they are going to fail. */ -/* XXX024 We should get rid of this function, and instead attach - * one-hop streams to circ->p_streams so they get marked in - * circuit_mark_for_close like normal p_streams. */ -void -connection_ap_fail_onehop(const char *failed_digest, - cpath_build_state_t *build_state) -{ - entry_connection_t *entry_conn; - char digest[DIGEST_LEN]; - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->marked_for_close || - conn->type != CONN_TYPE_AP || - conn->state != AP_CONN_STATE_CIRCUIT_WAIT) - continue; - entry_conn = TO_ENTRY_CONN(conn); - if (!entry_conn->want_onehop) - continue; - if (hexdigest_to_digest(entry_conn->chosen_exit_name, digest) < 0 || - tor_memneq(digest, failed_digest, DIGEST_LEN)) - continue; - if (tor_digest_is_zero(digest)) { - /* we don't know the digest; have to compare addr:port */ - tor_addr_t addr; - if (!build_state || !build_state->chosen_exit || - !entry_conn->socks_request || !entry_conn->socks_request->address) - continue; - if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != entry_conn->socks_request->port) - continue; - } - log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn " - "just failed.", entry_conn->chosen_exit_name, - entry_conn->socks_request->address); - connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT); - } SMARTLIST_FOREACH_END(conn); -} - -/** A circuit failed to finish on its last hop info. If there - * are any streams waiting with this exit node in mind, but they - * don't absolutely require it, make them give up on it. - */ -void -circuit_discard_optional_exit_enclaves(extend_info_t *info) -{ - entry_connection_t *entry_conn; - const node_t *r1, *r2; - - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->marked_for_close || - conn->type != CONN_TYPE_AP || - conn->state != AP_CONN_STATE_CIRCUIT_WAIT) - continue; - entry_conn = TO_ENTRY_CONN(conn); - if (!entry_conn->chosen_exit_optional && - !entry_conn->chosen_exit_retries) - continue; - r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0); - r2 = node_get_by_id(info->identity_digest); - if (!r1 || !r2 || r1 != r2) - continue; - tor_assert(entry_conn->socks_request); - if (entry_conn->chosen_exit_optional) { - log_info(LD_APP, "Giving up on enclave exit '%s' for destination %s.", - safe_str_client(entry_conn->chosen_exit_name), - escaped_safe_str_client(entry_conn->socks_request->address)); - entry_conn->chosen_exit_optional = 0; - tor_free(entry_conn->chosen_exit_name); /* clears it */ - /* if this port is dangerous, warn or reject it now that we don't - * think it'll be using an enclave. */ - consider_plaintext_ports(entry_conn, entry_conn->socks_request->port); - } - if (entry_conn->chosen_exit_retries) { - if (--entry_conn->chosen_exit_retries == 0) { /* give up! */ - clear_trackexithost_mappings(entry_conn->chosen_exit_name); - tor_free(entry_conn->chosen_exit_name); /* clears it */ - /* if this port is dangerous, warn or reject it now that we don't - * think it'll be using an enclave. */ - consider_plaintext_ports(entry_conn, entry_conn->socks_request->port); - } - } - } SMARTLIST_FOREACH_END(conn); -} - -/** The AP connection conn has just failed while attaching or - * sending a BEGIN or resolving on circ, but another circuit - * might work. Detach the circuit, and either reattach it, launch a - * new circuit, tell the controller, or give up as appropriate. - * - * Returns -1 on err, 1 on success, 0 on not-yet-sure. - */ -int -connection_ap_detach_retriable(entry_connection_t *conn, - origin_circuit_t *circ, - int reason) -{ - control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason); - ENTRY_TO_CONN(conn)->timestamp_lastread = time(NULL); - - /* Roll back path bias use state so that we probe the circuit - * if nothing else succeeds on it */ - pathbias_mark_use_rollback(circ); - - if (conn->pending_optimistic_data) { - generic_buffer_set_to_copy(&conn->sending_optimistic_data, - conn->pending_optimistic_data); - } - - if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) { - /* If we're attaching streams ourself, or if this connection is - * a tunneled directory connection, then just attach it. */ - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT; - circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn)); - return connection_ap_handshake_attach_circuit(conn); - } else { - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; - circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn)); - return 0; - } -} - -/** Check if conn is using a dangerous port. Then warn and/or - * reject depending on our config options. */ -static int -consider_plaintext_ports(entry_connection_t *conn, uint16_t port) -{ - const or_options_t *options = get_options(); - int reject = smartlist_contains_int_as_string( - options->RejectPlaintextPorts, port); - - if (smartlist_contains_int_as_string(options->WarnPlaintextPorts, port)) { - log_warn(LD_APP, "Application request to port %d: this port is " - "commonly used for unencrypted protocols. Please make sure " - "you don't send anything you would mind the rest of the " - "Internet reading!%s", port, reject ? " Closing." : ""); - control_event_client_status(LOG_WARN, "DANGEROUS_PORT PORT=%d RESULT=%s", - port, reject ? "REJECT" : "WARN"); - } - - if (reject) { - log_info(LD_APP, "Port %d listed in RejectPlaintextPorts. Closing.", port); - connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); - return -1; - } - - return 0; -} - -/** How many times do we try connecting with an exit configured via - * TrackHostExits before concluding that it won't work any more and trying a - * different one? */ -#define TRACKHOSTEXITS_RETRIES 5 - -/** Call connection_ap_handshake_rewrite_and_attach() unless a controller - * asked us to leave streams unattached. Return 0 in that case. - * - * See connection_ap_handshake_rewrite_and_attach()'s - * documentation for arguments and return value. - */ -int -connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath) -{ - const or_options_t *options = get_options(); - - if (options->LeaveStreamsUnattached) { - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; - return 0; - } - return connection_ap_handshake_rewrite_and_attach(conn, circ, cpath); -} - -/** Connection conn just finished its socks handshake, or the - * controller asked us to take care of it. If circ is defined, - * then that's where we'll want to attach it. Otherwise we have to - * figure it out ourselves. - * - * First, parse whether it's a .exit address, remap it, and so on. Then - * if it's for a general circuit, try to attach it to a circuit (or launch - * one as needed), else if it's for a rendezvous circuit, fetch a - * rendezvous descriptor first (or attach/launch a circuit if the - * rendezvous descriptor is already here and fresh enough). - * - * The stream will exit from the hop - * indicated by cpath, or from the last hop in circ's cpath if - * cpath is NULL. - */ -int -connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath) -{ - socks_request_t *socks = conn->socks_request; - hostname_type_t addresstype; - const or_options_t *options = get_options(); - tor_addr_t addr_tmp; - /* We set this to true if this is an address we should automatically - * remap to a local address in VirtualAddrNetwork */ - int automap = 0; - char orig_address[MAX_SOCKS_ADDR_LEN]; - time_t map_expires = TIME_MAX; - time_t now = time(NULL); - connection_t *base_conn = ENTRY_TO_CONN(conn); - addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE; - - tor_strlower(socks->address); /* normalize it */ - strlcpy(orig_address, socks->address, sizeof(orig_address)); - log_debug(LD_APP,"Client asked for %s:%d", - safe_str_client(socks->address), - socks->port); - - if (!strcmpend(socks->address, ".exit") && !options->AllowDotExit) { - log_warn(LD_APP, "The \".exit\" notation is disabled in Tor due to " - "security risks. Set AllowDotExit in your torrc to enable " - "it (at your own risk)."); - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - if (! conn->original_dest_address) - conn->original_dest_address = tor_strdup(conn->socks_request->address); - - if (socks->command == SOCKS_COMMAND_RESOLVE && - tor_addr_parse(&addr_tmp, socks->address)<0 && - options->AutomapHostsOnResolve) { - automap = addressmap_address_should_automap(socks->address, options); - if (automap) { - const char *new_addr; - int addr_type = RESOLVED_TYPE_IPV4; - if (conn->socks_request->socks_version != 4) { - if (!conn->ipv4_traffic_ok || - (conn->ipv6_traffic_ok && conn->prefer_ipv6_traffic) || - conn->prefer_ipv6_virtaddr) - addr_type = RESOLVED_TYPE_IPV6; - } - new_addr = addressmap_register_virtual_address( - addr_type, tor_strdup(socks->address)); - if (! new_addr) { - log_warn(LD_APP, "Unable to automap address %s", - escaped_safe_str(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INTERNAL); - return -1; - } - log_info(LD_APP, "Automapping %s to %s", - escaped_safe_str_client(socks->address), - safe_str_client(new_addr)); - strlcpy(socks->address, new_addr, sizeof(socks->address)); - } - } - - if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) { - unsigned rewrite_flags = 0; - if (conn->use_cached_ipv4_answers) - rewrite_flags |= AMR_FLAG_USE_IPV4_DNS; - if (conn->use_cached_ipv6_answers) - rewrite_flags |= AMR_FLAG_USE_IPV6_DNS; - - if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address), - rewrite_flags, &map_expires)) { - char *result = tor_strdup(socks->address); - /* remember _what_ is supposed to have been resolved. */ - tor_snprintf(socks->address, sizeof(socks->address), "REVERSE[%s]", - orig_address); - connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_HOSTNAME, - strlen(result), (uint8_t*)result, - -1, - map_expires); - connection_mark_unattached_ap(conn, - END_STREAM_REASON_DONE | - END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); - return 0; - } - if (options->ClientDNSRejectInternalAddresses) { - /* Don't let people try to do a reverse lookup on 10.0.0.1. */ - tor_addr_t addr; - int ok; - ok = tor_addr_parse_PTR_name( - &addr, socks->address, AF_UNSPEC, 1); - if (ok == 1 && tor_addr_is_internal(&addr, 0)) { - connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_ERROR, - 0, NULL, -1, TIME_MAX); - connection_mark_unattached_ap(conn, - END_STREAM_REASON_SOCKSPROTOCOL | - END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); - return -1; - } - } - } else if (!automap) { - /* For address map controls, remap the address. */ - unsigned rewrite_flags = 0; - if (conn->use_cached_ipv4_answers) - rewrite_flags |= AMR_FLAG_USE_IPV4_DNS; - if (conn->use_cached_ipv6_answers) - rewrite_flags |= AMR_FLAG_USE_IPV6_DNS; - if (addressmap_rewrite(socks->address, sizeof(socks->address), - rewrite_flags, &map_expires, &exit_source)) { - control_event_stream_status(conn, STREAM_EVENT_REMAP, - REMAP_STREAM_SOURCE_CACHE); - } - } - - if (!automap && address_is_in_virtual_range(socks->address)) { - /* This address was probably handed out by client_dns_get_unmapped_address, - * but the mapping was discarded for some reason. We *don't* want to send - * the address through Tor; that's likely to fail, and may leak - * information. - */ - log_warn(LD_APP,"Missing mapping for virtual address '%s'. Refusing.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INTERNAL); - return -1; - } - - /* Parse the address provided by SOCKS. Modify it in-place if it - * specifies a hidden-service (.onion) or particular exit node (.exit). - */ - addresstype = parse_extended_hostname(socks->address); - - if (addresstype == BAD_HOSTNAME) { - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - if (addresstype == EXIT_HOSTNAME) { - /* foo.exit -- modify conn->chosen_exit_node to specify the exit - * node, and conn->address to hold only the address portion. */ - char *s = strrchr(socks->address,'.'); - - /* If StrictNodes is not set, then .exit overrides ExcludeNodes. */ - routerset_t *excludeset = options->StrictNodes ? - options->ExcludeExitNodesUnion_ : options->ExcludeExitNodes; - const node_t *node; - - if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) { - /* Whoops; this one is stale. It must have gotten added earlier, - * when AllowDotExit was on. */ - log_warn(LD_APP,"Stale automapped address for '%s.exit', with " - "AllowDotExit disabled. Refusing.", - safe_str_client(socks->address)); - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - if (exit_source == ADDRMAPSRC_DNS || - (exit_source == ADDRMAPSRC_NONE && !options->AllowDotExit)) { - /* It shouldn't be possible to get a .exit address from any of these - * sources. */ - log_warn(LD_BUG,"Address '%s.exit', with impossible source for the " - ".exit part. Refusing.", - safe_str_client(socks->address)); - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - tor_assert(!automap); - if (s) { - /* The address was of the form "(stuff).(name).exit */ - if (s[1] != '\0') { - conn->chosen_exit_name = tor_strdup(s+1); - node = node_get_by_nickname(conn->chosen_exit_name, 1); - - if (exit_source == ADDRMAPSRC_TRACKEXIT) { - /* We 5 tries before it expires the addressmap */ - conn->chosen_exit_retries = TRACKHOSTEXITS_RETRIES; - } - *s = 0; - } else { - /* Oops, the address was (stuff)..exit. That's not okay. */ - log_warn(LD_APP,"Malformed exit address '%s.exit'. Refusing.", - safe_str_client(socks->address)); - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - } else { - /* It looks like they just asked for "foo.exit". */ - - conn->chosen_exit_name = tor_strdup(socks->address); - node = node_get_by_nickname(conn->chosen_exit_name, 1); - if (node) { - *socks->address = 0; - node_get_address_string(node, socks->address, sizeof(socks->address)); - } - } - /* Now make sure that the chosen exit exists... */ - if (!node) { - log_warn(LD_APP, - "Unrecognized relay in exit address '%s.exit'. Refusing.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - /* ...and make sure that it isn't excluded. */ - if (routerset_contains_node(excludeset, node)) { - log_warn(LD_APP, - "Excluded relay in exit address '%s.exit'. Refusing.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - /* XXXX024-1090 Should we also allow foo.bar.exit if ExitNodes is set and - Bar is not listed in it? I say yes, but our revised manpage branch - implies no. */ - } - - if (addresstype != ONION_HOSTNAME) { - /* not a hidden-service request (i.e. normal or .exit) */ - if (address_is_invalid_destination(socks->address, 1)) { - control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", - escaped(socks->address)); - log_warn(LD_APP, - "Destination '%s' seems to be an invalid hostname. Failing.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - if (options->Tor2webMode) { - log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s " - "because tor2web mode is enabled.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); - return -1; - } - - if (socks->command == SOCKS_COMMAND_RESOLVE) { - tor_addr_t answer; - /* Reply to resolves immediately if we can. */ - if (tor_addr_parse(&answer, socks->address) >= 0) {/* is it an IP? */ - /* remember _what_ is supposed to have been resolved. */ - strlcpy(socks->address, orig_address, sizeof(socks->address)); - connection_ap_handshake_socks_resolved_addr(conn, &answer, -1, - map_expires); - connection_mark_unattached_ap(conn, - END_STREAM_REASON_DONE | - END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); - return 0; - } - tor_assert(!automap); - rep_hist_note_used_resolve(now); /* help predict this next time */ - } else if (socks->command == SOCKS_COMMAND_CONNECT) { - tor_assert(!automap); - if (socks->port == 0) { - log_notice(LD_APP,"Application asked to connect to port 0. Refusing."); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - if (options->ClientRejectInternalAddresses && - !conn->use_begindir && !conn->chosen_exit_name && !circ) { - tor_addr_t addr; - if (tor_addr_hostname_is_local(socks->address) || - (tor_addr_parse(&addr, socks->address) >= 0 && - tor_addr_is_internal(&addr, 0))) { - /* If this is an explicit private address with no chosen exit node, - * then we really don't want to try to connect to it. That's - * probably an error. */ - if (conn->is_transparent_ap) { -#define WARN_INTRVL_LOOP 300 - static ratelim_t loop_warn_limit = RATELIM_INIT(WARN_INTRVL_LOOP); - char *m; - if ((m = rate_limit_log(&loop_warn_limit, approx_time()))) { - log_warn(LD_NET, - "Rejecting request for anonymous connection to private " - "address %s on a TransPort or NATDPort. Possible loop " - "in your NAT rules?%s", safe_str_client(socks->address), - m); - tor_free(m); - } - } else { -#define WARN_INTRVL_PRIV 300 - static ratelim_t priv_warn_limit = RATELIM_INIT(WARN_INTRVL_PRIV); - char *m; - if ((m = rate_limit_log(&priv_warn_limit, approx_time()))) { - log_warn(LD_NET, - "Rejecting SOCKS request for anonymous connection to " - "private address %s.%s", - safe_str_client(socks->address),m); - tor_free(m); - } - } - connection_mark_unattached_ap(conn, END_STREAM_REASON_PRIVATE_ADDR); - return -1; - } - } - - { - tor_addr_t addr; - /* XXX Duplicate call to tor_addr_parse. */ - if (tor_addr_parse(&addr, socks->address) >= 0) { - sa_family_t family = tor_addr_family(&addr); - if ((family == AF_INET && ! conn->ipv4_traffic_ok) || - (family == AF_INET6 && ! conn->ipv4_traffic_ok)) { - log_warn(LD_NET, "Rejecting SOCKS request for an IP address " - "family that this listener does not support."); - connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); - return -1; - } else if (family == AF_INET6 && socks->socks_version == 4) { - log_warn(LD_NET, "Rejecting SOCKS4 request for an IPv6 address."); - connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); - return -1; - } else if (socks->socks_version == 4 && !conn->ipv4_traffic_ok) { - log_warn(LD_NET, "Rejecting SOCKS4 request on a listener with " - "no IPv4 traffic supported."); - connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); - return -1; - } else if (family == AF_INET6) { - conn->ipv4_traffic_ok = 0; - } else if (family == AF_INET) { - conn->ipv6_traffic_ok = 0; - } - } - } - - if (socks->socks_version == 4) - conn->ipv6_traffic_ok = 0; - - if (!conn->use_begindir && !conn->chosen_exit_name && !circ) { - /* see if we can find a suitable enclave exit */ - const node_t *r = - router_find_exact_exit_enclave(socks->address, socks->port); - if (r) { - log_info(LD_APP, - "Redirecting address %s to exit at enclave router %s", - safe_str_client(socks->address), node_describe(r)); - /* use the hex digest, not nickname, in case there are two - routers with this nickname */ - conn->chosen_exit_name = - tor_strdup(hex_str(r->identity, DIGEST_LEN)); - conn->chosen_exit_optional = 1; - } - } - - /* warn or reject if it's using a dangerous port */ - if (!conn->use_begindir && !conn->chosen_exit_name && !circ) - if (consider_plaintext_ports(conn, socks->port) < 0) - return -1; - - if (!conn->use_begindir) { - /* help predict this next time */ - rep_hist_note_used_port(now, socks->port); - } - } else if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) { - rep_hist_note_used_resolve(now); /* help predict this next time */ - /* no extra processing needed */ - } else { - tor_fragile_assert(); - } - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - if ((circ && connection_ap_handshake_attach_chosen_circuit( - conn, circ, cpath) < 0) || - (!circ && - connection_ap_handshake_attach_circuit(conn) < 0)) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return -1; - } - return 0; - } else { - /* it's a hidden-service request */ - rend_cache_entry_t *entry; - int r; - rend_service_authorization_t *client_auth; - rend_data_t *rend_data; - tor_assert(!automap); - if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) { - /* if it's a resolve request, fail it right now, rather than - * building all the circuits and then realizing it won't work. */ - log_warn(LD_APP, - "Resolve requests to hidden services not allowed. Failing."); - connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR, - 0,NULL,-1,TIME_MAX); - connection_mark_unattached_ap(conn, - END_STREAM_REASON_SOCKSPROTOCOL | - END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); - return -1; - } - - if (circ) { - log_warn(LD_CONTROL, "Attachstream to a circuit is not " - "supported for .onion addresses currently. Failing."); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - ENTRY_TO_EDGE_CONN(conn)->rend_data = rend_data = - tor_malloc_zero(sizeof(rend_data_t)); - strlcpy(rend_data->onion_address, socks->address, - sizeof(rend_data->onion_address)); - log_info(LD_REND,"Got a hidden service request for ID '%s'", - safe_str_client(rend_data->onion_address)); - /* see if we already have it cached */ - r = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry); - if (r<0) { - log_warn(LD_BUG,"Invalid service name '%s'", - safe_str_client(rend_data->onion_address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; - } - - /* Help predict this next time. We're not sure if it will need - * a stable circuit yet, but we know we'll need *something*. */ - rep_hist_note_used_internal(now, 0, 1); - - /* Look up if we have client authorization for it. */ - client_auth = rend_client_lookup_service_authorization( - rend_data->onion_address); - if (client_auth) { - log_info(LD_REND, "Using previously configured client authorization " - "for hidden service request."); - memcpy(rend_data->descriptor_cookie, - client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN); - rend_data->auth_type = client_auth->auth_type; - } - if (r==0) { - base_conn->state = AP_CONN_STATE_RENDDESC_WAIT; - log_info(LD_REND, "Unknown descriptor %s. Fetching.", - safe_str_client(rend_data->onion_address)); - rend_client_refetch_v2_renddesc(rend_data); - } else { /* r > 0 */ - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - log_info(LD_REND, "Descriptor is here. Great."); - if (connection_ap_handshake_attach_circuit(conn) < 0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return -1; - } - } - return 0; - } - return 0; /* unreached but keeps the compiler happy */ -} - -#ifdef TRANS_PF -static int pf_socket = -1; -int -get_pf_socket(void) -{ - int pf; - /* This should be opened before dropping privileges. */ - if (pf_socket >= 0) - return pf_socket; - -#ifdef OPENBSD - /* only works on OpenBSD */ - pf = tor_open_cloexec("/dev/pf", O_RDONLY, 0); -#else - /* works on NetBSD and FreeBSD */ - pf = tor_open_cloexec("/dev/pf", O_RDWR, 0); -#endif - - if (pf < 0) { - log_warn(LD_NET, "open(\"/dev/pf\") failed: %s", strerror(errno)); - return -1; - } - - pf_socket = pf; - return pf_socket; -} -#endif - -/** Fetch the original destination address and port from a - * system-specific interface and put them into a - * socks_request_t as if they came from a socks request. - * - * Return -1 if an error prevents fetching the destination, - * else return 0. - */ -static int -connection_ap_get_original_destination(entry_connection_t *conn, - socks_request_t *req) -{ -#ifdef TRANS_NETFILTER - /* Linux 2.4+ */ - struct sockaddr_storage orig_dst; - socklen_t orig_dst_len = sizeof(orig_dst); - tor_addr_t addr; - - if (getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST, - (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) { - int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s); - log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e)); - return -1; - } - - tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port); - tor_addr_to_str(req->address, &addr, sizeof(req->address), 1); - - return 0; -#elif defined(TRANS_PF) - struct sockaddr_storage proxy_addr; - socklen_t proxy_addr_len = sizeof(proxy_addr); - struct sockaddr *proxy_sa = (struct sockaddr*) &proxy_addr; - struct pfioc_natlook pnl; - tor_addr_t addr; - int pf = -1; - - if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&proxy_addr, - &proxy_addr_len) < 0) { - int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s); - log_warn(LD_NET, "getsockname() to determine transocks destination " - "failed: %s", tor_socket_strerror(e)); - return -1; - } - - memset(&pnl, 0, sizeof(pnl)); - pnl.proto = IPPROTO_TCP; - pnl.direction = PF_OUT; - if (proxy_sa->sa_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)proxy_sa; - pnl.af = AF_INET; - pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&ENTRY_TO_CONN(conn)->addr); - pnl.sport = htons(ENTRY_TO_CONN(conn)->port); - pnl.daddr.v4.s_addr = sin->sin_addr.s_addr; - pnl.dport = sin->sin_port; - } else if (proxy_sa->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)proxy_sa; - pnl.af = AF_INET6; - memcpy(&pnl.saddr.v6, tor_addr_to_in6(&ENTRY_TO_CONN(conn)->addr), - sizeof(struct in6_addr)); - pnl.sport = htons(ENTRY_TO_CONN(conn)->port); - memcpy(&pnl.daddr.v6, &sin6->sin6_addr, sizeof(struct in6_addr)); - pnl.dport = sin6->sin6_port; - } else { - log_warn(LD_NET, "getsockname() gave an unexpected address family (%d)", - (int)proxy_sa->sa_family); - return -1; - } - - pf = get_pf_socket(); - if (pf<0) - return -1; - - if (ioctl(pf, DIOCNATLOOK, &pnl) < 0) { - log_warn(LD_NET, "ioctl(DIOCNATLOOK) failed: %s", strerror(errno)); - return -1; - } - - if (pnl.af == AF_INET) { - tor_addr_from_ipv4n(&addr, pnl.rdaddr.v4.s_addr); - } else if (pnl.af == AF_INET6) { - tor_addr_from_in6(&addr, &pnl.rdaddr.v6); - } else { - tor_fragile_assert(); - return -1; - } - - tor_addr_to_str(req->address, &addr, sizeof(req->address), 1); - req->port = ntohs(pnl.rdport); - - return 0; -#else - (void)conn; - (void)req; - log_warn(LD_BUG, "Called connection_ap_get_original_destination, but no " - "transparent proxy method was configured."); - return -1; -#endif -} - -/** connection_edge_process_inbuf() found a conn in state - * socks_wait. See if conn->inbuf has the right bytes to proceed with - * the socks handshake. - * - * If the handshake is complete, send it to - * connection_ap_handshake_rewrite_and_attach(). - * - * Return -1 if an unexpected error with conn occurs (and mark it for close), - * else return 0. - */ -static int -connection_ap_handshake_process_socks(entry_connection_t *conn) -{ - socks_request_t *socks; - int sockshere; - const or_options_t *options = get_options(); - int had_reply = 0; - connection_t *base_conn = ENTRY_TO_CONN(conn); - - tor_assert(conn); - tor_assert(base_conn->type == CONN_TYPE_AP); - tor_assert(base_conn->state == AP_CONN_STATE_SOCKS_WAIT); - tor_assert(conn->socks_request); - socks = conn->socks_request; - - log_debug(LD_APP,"entered."); - - IF_HAS_BUFFEREVENT(base_conn, { - struct evbuffer *input = bufferevent_get_input(base_conn->bufev); - sockshere = fetch_from_evbuffer_socks(input, socks, - options->TestSocks, options->SafeSocks); - }) ELSE_IF_NO_BUFFEREVENT { - sockshere = fetch_from_buf_socks(base_conn->inbuf, socks, - options->TestSocks, options->SafeSocks); - }; - - if (socks->replylen) { - had_reply = 1; - connection_write_to_buf((const char*)socks->reply, socks->replylen, - base_conn); - socks->replylen = 0; - if (sockshere == -1) { - /* An invalid request just got a reply, no additional - * one is necessary. */ - socks->has_finished = 1; - } - } - - if (sockshere == 0) { - log_debug(LD_APP,"socks handshake not all here yet."); - return 0; - } else if (sockshere == -1) { - if (!had_reply) { - log_warn(LD_APP,"Fetching socks handshake failed. Closing."); - connection_ap_handshake_socks_reply(conn, NULL, 0, - END_STREAM_REASON_SOCKSPROTOCOL); - } - connection_mark_unattached_ap(conn, - END_STREAM_REASON_SOCKSPROTOCOL | - END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); - return -1; - } /* else socks handshake is done, continue processing */ - - if (SOCKS_COMMAND_IS_CONNECT(socks->command)) - control_event_stream_status(conn, STREAM_EVENT_NEW, 0); - else - control_event_stream_status(conn, STREAM_EVENT_NEW_RESOLVE, 0); - - return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL); -} - -/** connection_init_accepted_conn() found a new trans AP conn. - * Get the original destination and send it to - * connection_ap_handshake_rewrite_and_attach(). - * - * Return -1 if an unexpected error with conn (and it should be marked - * for close), else return 0. - */ -int -connection_ap_process_transparent(entry_connection_t *conn) -{ - socks_request_t *socks; - - tor_assert(conn); - tor_assert(conn->socks_request); - socks = conn->socks_request; - - /* pretend that a socks handshake completed so we don't try to - * send a socks reply down a transparent conn */ - socks->command = SOCKS_COMMAND_CONNECT; - socks->has_finished = 1; - - log_debug(LD_APP,"entered."); - - if (connection_ap_get_original_destination(conn, socks) < 0) { - log_warn(LD_APP,"Fetching original destination failed. Closing."); - connection_mark_unattached_ap(conn, - END_STREAM_REASON_CANT_FETCH_ORIG_DEST); - return -1; - } - /* we have the original destination */ - - control_event_stream_status(conn, STREAM_EVENT_NEW, 0); - - return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL); -} - -/** connection_edge_process_inbuf() found a conn in state natd_wait. See if - * conn-\>inbuf has the right bytes to proceed. See FreeBSD's libalias(3) and - * ProxyEncodeTcpStream() in src/lib/libalias/alias_proxy.c for the encoding - * form of the original destination. - * - * If the original destination is complete, send it to - * connection_ap_handshake_rewrite_and_attach(). - * - * Return -1 if an unexpected error with conn (and it should be marked - * for close), else return 0. - */ -static int -connection_ap_process_natd(entry_connection_t *conn) -{ - char tmp_buf[36], *tbuf, *daddr; - size_t tlen = 30; - int err, port_ok; - socks_request_t *socks; - - tor_assert(conn); - tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_NATD_WAIT); - tor_assert(conn->socks_request); - socks = conn->socks_request; - - log_debug(LD_APP,"entered."); - - /* look for LF-terminated "[DEST ip_addr port]" - * where ip_addr is a dotted-quad and port is in string form */ - err = connection_fetch_from_buf_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen); - if (err == 0) - return 0; - if (err < 0) { - log_warn(LD_APP,"NATD handshake failed (DEST too long). Closing"); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INVALID_NATD_DEST); - return -1; - } - - if (strcmpstart(tmp_buf, "[DEST ")) { - log_warn(LD_APP,"NATD handshake was ill-formed; closing. The client " - "said: %s", - escaped(tmp_buf)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INVALID_NATD_DEST); - return -1; - } - - daddr = tbuf = &tmp_buf[0] + 6; /* after end of "[DEST " */ - if (!(tbuf = strchr(tbuf, ' '))) { - log_warn(LD_APP,"NATD handshake was ill-formed; closing. The client " - "said: %s", - escaped(tmp_buf)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INVALID_NATD_DEST); - return -1; - } - *tbuf++ = '\0'; - - /* pretend that a socks handshake completed so we don't try to - * send a socks reply down a natd conn */ - strlcpy(socks->address, daddr, sizeof(socks->address)); - socks->port = (uint16_t) - tor_parse_long(tbuf, 10, 1, 65535, &port_ok, &daddr); - if (!port_ok) { - log_warn(LD_APP,"NATD handshake failed; port %s is ill-formed or out " - "of range.", escaped(tbuf)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_INVALID_NATD_DEST); - return -1; - } - - socks->command = SOCKS_COMMAND_CONNECT; - socks->has_finished = 1; - - control_event_stream_status(conn, STREAM_EVENT_NEW, 0); - - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT; - - return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL); -} - -/** Iterate over the two bytes of stream_id until we get one that is not - * already in use; return it. Return 0 if can't get a unique stream_id. - */ -streamid_t -get_unique_stream_id_by_circ(origin_circuit_t *circ) -{ - edge_connection_t *tmpconn; - streamid_t test_stream_id; - uint32_t attempts=0; - - again: - test_stream_id = circ->next_stream_id++; - if (++attempts > 1<<16) { - /* Make sure we don't loop forever if all stream_id's are used. */ - log_warn(LD_APP,"No unused stream IDs. Failing."); - return 0; - } - if (test_stream_id == 0) - goto again; - for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) - if (tmpconn->stream_id == test_stream_id) - goto again; - return test_stream_id; -} - -/** Return true iff conn is linked to a circuit and configured to use - * an exit that supports optimistic data. */ -static int -connection_ap_supports_optimistic_data(const entry_connection_t *conn) -{ - const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn); - /* We can only send optimistic data if we're connected to an open - general circuit. */ - if (edge_conn->on_circuit == NULL || - edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN || - edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL) - return 0; - - return conn->may_use_optimistic_data; -} - -/** Return a bitmask of BEGIN_FLAG_* flags that we should transmit in the - * RELAY_BEGIN cell for ap_conn. */ -static uint32_t -connection_ap_get_begincell_flags(entry_connection_t *ap_conn) -{ - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); - const node_t *exitnode = NULL; - const crypt_path_t *cpath_layer = edge_conn->cpath_layer; - uint32_t flags = 0; - - /* No flags for begindir */ - if (ap_conn->use_begindir) - return 0; - - /* No flags for hidden services. */ - if (edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL) - return 0; - - /* If only IPv4 is supported, no flags */ - if (ap_conn->ipv4_traffic_ok && !ap_conn->ipv6_traffic_ok) - return 0; - - if (! cpath_layer || - ! cpath_layer->extend_info) - return 0; - - if (!ap_conn->ipv4_traffic_ok) - flags |= BEGIN_FLAG_IPV4_NOT_OK; - - exitnode = node_get_by_id(cpath_layer->extend_info->identity_digest); - - if (ap_conn->ipv6_traffic_ok && exitnode) { - tor_addr_t a; - tor_addr_make_null(&a, AF_INET6); - if (compare_tor_addr_to_node_policy(&a, ap_conn->socks_request->port, - exitnode) - != ADDR_POLICY_REJECTED) { - /* Only say "IPv6 OK" if the exit node supports IPv6. Otherwise there's - * no point. */ - flags |= BEGIN_FLAG_IPV6_OK; - } - } - - if (flags == BEGIN_FLAG_IPV6_OK) { - /* When IPv4 and IPv6 are both allowed, consider whether to say we - * prefer IPv6. Otherwise there's no point in declaring a preference */ - if (ap_conn->prefer_ipv6_traffic) - flags |= BEGIN_FLAG_IPV6_PREFERRED; - } - - if (flags == BEGIN_FLAG_IPV4_NOT_OK) { - log_warn(LD_EDGE, "I'm about to ask a node for a connection that I " - "am telling it to fulfil with neither IPv4 nor IPv6. That's " - "not going to work. Did you perhaps ask for an IPv6 address " - "on an IPv4Only port, or vice versa?"); - } - - return flags; -} - -/** Write a relay begin cell, using destaddr and destport from ap_conn's - * socks_request field, and send it down circ. - * - * If ap_conn is broken, mark it for close and return -1. Else return 0. - */ -int -connection_ap_handshake_send_begin(entry_connection_t *ap_conn) -{ - char payload[CELL_PAYLOAD_SIZE]; - int payload_len; - int begin_type; - origin_circuit_t *circ; - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); - connection_t *base_conn = TO_CONN(edge_conn); - tor_assert(edge_conn->on_circuit); - circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit); - - tor_assert(base_conn->type == CONN_TYPE_AP); - tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - tor_assert(ap_conn->socks_request); - tor_assert(SOCKS_COMMAND_IS_CONNECT(ap_conn->socks_request->command)); - - edge_conn->stream_id = get_unique_stream_id_by_circ(circ); - if (edge_conn->stream_id==0) { - /* XXXX024 Instead of closing this stream, we should make it get - * retried on another circuit. */ - connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - - /* Mark this circuit "unusable for new streams". */ - mark_circuit_unusable_for_new_conns(circ); - return -1; - } - - /* Set up begin cell flags. */ - edge_conn->begincell_flags = connection_ap_get_begincell_flags(ap_conn); - - tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:%d", - (circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL) ? - ap_conn->socks_request->address : "", - ap_conn->socks_request->port); - payload_len = (int)strlen(payload)+1; - if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) { - set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags)); - payload_len += 4; - } - - log_info(LD_APP, - "Sending relay cell %d to begin stream %d.", - (int)ap_conn->use_begindir, - edge_conn->stream_id); - - begin_type = ap_conn->use_begindir ? - RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN; - if (begin_type == RELAY_COMMAND_BEGIN) { -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(circ->build_state->onehop_tunnel == 0); -#endif - } - - if (connection_edge_send_command(edge_conn, begin_type, - begin_type == RELAY_COMMAND_BEGIN ? payload : NULL, - begin_type == RELAY_COMMAND_BEGIN ? payload_len : 0) < 0) - return -1; /* circuit is closed, don't continue */ - - edge_conn->package_window = STREAMWINDOW_START; - edge_conn->deliver_window = STREAMWINDOW_START; - base_conn->state = AP_CONN_STATE_CONNECT_WAIT; - log_info(LD_APP,"Address/port sent, ap socket "TOR_SOCKET_T_FORMAT - ", n_circ_id %u", - base_conn->s, (unsigned)circ->base_.n_circ_id); - control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0); - - /* If there's queued-up data, send it now */ - if ((connection_get_inbuf_len(base_conn) || - ap_conn->sending_optimistic_data) && - connection_ap_supports_optimistic_data(ap_conn)) { - log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data", - (long)connection_get_inbuf_len(base_conn), - ap_conn->sending_optimistic_data ? - (long)generic_buffer_len(ap_conn->sending_optimistic_data) : 0); - if (connection_edge_package_raw_inbuf(edge_conn, 1, NULL) < 0) { - connection_mark_for_close(base_conn); - } - } - - return 0; -} - -/** Write a relay resolve cell, using destaddr and destport from ap_conn's - * socks_request field, and send it down circ. - * - * If ap_conn is broken, mark it for close and return -1. Else return 0. - */ -int -connection_ap_handshake_send_resolve(entry_connection_t *ap_conn) -{ - int payload_len, command; - const char *string_addr; - char inaddr_buf[REVERSE_LOOKUP_NAME_BUF_LEN]; - origin_circuit_t *circ; - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); - connection_t *base_conn = TO_CONN(edge_conn); - tor_assert(edge_conn->on_circuit); - circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit); - - tor_assert(base_conn->type == CONN_TYPE_AP); - tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - tor_assert(ap_conn->socks_request); - tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL); - - command = ap_conn->socks_request->command; - tor_assert(SOCKS_COMMAND_IS_RESOLVE(command)); - - edge_conn->stream_id = get_unique_stream_id_by_circ(circ); - if (edge_conn->stream_id==0) { - /* XXXX024 Instead of closing this stream, we should make it get - * retried on another circuit. */ - connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - - /* Mark this circuit "unusable for new streams". */ - mark_circuit_unusable_for_new_conns(circ); - return -1; - } - - if (command == SOCKS_COMMAND_RESOLVE) { - string_addr = ap_conn->socks_request->address; - payload_len = (int)strlen(string_addr)+1; - } else { - /* command == SOCKS_COMMAND_RESOLVE_PTR */ - const char *a = ap_conn->socks_request->address; - tor_addr_t addr; - int r; - - /* We're doing a reverse lookup. The input could be an IP address, or - * could be an .in-addr.arpa or .ip6.arpa address */ - r = tor_addr_parse_PTR_name(&addr, a, AF_UNSPEC, 1); - if (r <= 0) { - log_warn(LD_APP, "Rejecting ill-formed reverse lookup of %s", - safe_str_client(a)); - connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - return -1; - } - - r = tor_addr_to_PTR_name(inaddr_buf, sizeof(inaddr_buf), &addr); - if (r < 0) { - log_warn(LD_BUG, "Couldn't generate reverse lookup hostname of %s", - safe_str_client(a)); - connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - return -1; - } - - string_addr = inaddr_buf; - payload_len = (int)strlen(inaddr_buf)+1; - tor_assert(payload_len <= (int)sizeof(inaddr_buf)); - } - - log_debug(LD_APP, - "Sending relay cell to begin stream %d.", edge_conn->stream_id); - - if (connection_edge_send_command(edge_conn, - RELAY_COMMAND_RESOLVE, - string_addr, payload_len) < 0) - return -1; /* circuit is closed, don't continue */ - - if (!base_conn->address) { - /* This might be unnecessary. XXXX */ - base_conn->address = tor_dup_addr(&base_conn->addr); - } - base_conn->state = AP_CONN_STATE_RESOLVE_WAIT; - log_info(LD_APP,"Address sent for resolve, ap socket "TOR_SOCKET_T_FORMAT - ", n_circ_id %u", - base_conn->s, (unsigned)circ->base_.n_circ_id); - control_event_stream_status(ap_conn, STREAM_EVENT_SENT_RESOLVE, 0); - return 0; -} - -/** Make an AP connection_t linked to the connection_t partner. make a - * new linked connection pair, and attach one side to the conn, connection_add - * it, initialize it to circuit_wait, and call - * connection_ap_handshake_attach_circuit(conn) on it. - * - * Return the newly created end of the linked connection pair, or -1 if error. - */ -entry_connection_t * -connection_ap_make_link(connection_t *partner, - char *address, uint16_t port, - const char *digest, - int session_group, int isolation_flags, - int use_begindir, int want_onehop) -{ - entry_connection_t *conn; - connection_t *base_conn; - - log_info(LD_APP,"Making internal %s tunnel to %s:%d ...", - want_onehop ? "direct" : "anonymized", - safe_str_client(address), port); - - conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr)); - base_conn = ENTRY_TO_CONN(conn); - base_conn->linked = 1; /* so that we can add it safely below. */ - - /* populate conn->socks_request */ - - /* leave version at zero, so the socks_reply is empty */ - conn->socks_request->socks_version = 0; - conn->socks_request->has_finished = 0; /* waiting for 'connected' */ - strlcpy(conn->socks_request->address, address, - sizeof(conn->socks_request->address)); - conn->socks_request->port = port; - conn->socks_request->command = SOCKS_COMMAND_CONNECT; - conn->want_onehop = want_onehop; - conn->use_begindir = use_begindir; - if (use_begindir) { - conn->chosen_exit_name = tor_malloc(HEX_DIGEST_LEN+2); - conn->chosen_exit_name[0] = '$'; - tor_assert(digest); - base16_encode(conn->chosen_exit_name+1,HEX_DIGEST_LEN+1, - digest, DIGEST_LEN); - } - - /* Populate isolation fields. */ - conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER; - conn->original_dest_address = tor_strdup(address); - conn->session_group = session_group; - conn->isolation_flags = isolation_flags; - - base_conn->address = tor_strdup("(Tor_internal)"); - tor_addr_make_unspec(&base_conn->addr); - base_conn->port = 0; - - connection_link_connections(partner, base_conn); - - if (connection_add(base_conn) < 0) { /* no space, forget it */ - connection_free(base_conn); - return NULL; - } - - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - - control_event_stream_status(conn, STREAM_EVENT_NEW, 0); - - /* attaching to a dirty circuit is fine */ - if (connection_ap_handshake_attach_circuit(conn) < 0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return NULL; - } - - log_info(LD_APP,"... application connection created and linked."); - return conn; -} - -/** Notify any interested controller connections about a new hostname resolve - * or resolve error. Takes the same arguments as does - * connection_ap_handshake_socks_resolved(). */ -static void -tell_controller_about_resolved_result(entry_connection_t *conn, - int answer_type, - size_t answer_len, - const char *answer, - int ttl, - time_t expires) -{ - expires = time(NULL) + ttl; - if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) { - char *cp = tor_dup_ip(ntohl(get_uint32(answer))); - control_event_address_mapped(conn->socks_request->address, - cp, expires, NULL, 0); - tor_free(cp); - } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) { - char *cp = tor_strndup(answer, answer_len); - control_event_address_mapped(conn->socks_request->address, - cp, expires, NULL, 0); - tor_free(cp); - } else { - control_event_address_mapped(conn->socks_request->address, - "", time(NULL)+ttl, - "error=yes", 0); - } -} - -/** - * As connection_ap_handshake_socks_resolved, but take a tor_addr_t to send - * as the answer. - */ -static void -connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn, - const tor_addr_t *answer, - int ttl, - time_t expires) -{ - if (tor_addr_family(answer) == AF_INET) { - uint32_t a = tor_addr_to_ipv4n(answer); /* network order */ - connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV4,4, - (uint8_t*)&a, - ttl, expires); - } else if (tor_addr_family(answer) == AF_INET6) { - const uint8_t *a = tor_addr_to_in6_addr8(answer); - connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV6,16, - a, - ttl, expires); - } else { - log_warn(LD_BUG, "Got called with address of unexpected family %d", - tor_addr_family(answer)); - connection_ap_handshake_socks_resolved(conn, - RESOLVED_TYPE_ERROR,0,NULL,-1,-1); - } -} - -/** Send an answer to an AP connection that has requested a DNS lookup via - * SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6|HOSTNAME) or -1 - * for unreachable; the answer should be in the format specified in the socks - * extensions document. ttl is the ttl for the answer, or -1 on - * certain errors or for values that didn't come via DNS. expires is - * a time when the answer expires, or -1 or TIME_MAX if there's a good TTL. - **/ -/* XXXX the use of the ttl and expires fields is nutty. Let's make this - * interface and those that use it less ugly. */ -void -connection_ap_handshake_socks_resolved(entry_connection_t *conn, - int answer_type, - size_t answer_len, - const uint8_t *answer, - int ttl, - time_t expires) -{ - char buf[384]; - size_t replylen; - - if (ttl >= 0) { - if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { - tor_addr_t a; - tor_addr_from_ipv4n(&a, get_uint32(answer)); - if (! tor_addr_is_null(&a)) { - client_dns_set_addressmap(conn, - conn->socks_request->address, &a, - conn->chosen_exit_name, ttl); - } - } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { - tor_addr_t a; - tor_addr_from_ipv6_bytes(&a, (char*)answer); - if (! tor_addr_is_null(&a)) { - client_dns_set_addressmap(conn, - conn->socks_request->address, &a, - conn->chosen_exit_name, ttl); - } - } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) { - char *cp = tor_strndup((char*)answer, answer_len); - client_dns_set_reverse_addressmap(conn, - conn->socks_request->address, - cp, - conn->chosen_exit_name, ttl); - tor_free(cp); - } - } - - if (ENTRY_TO_EDGE_CONN(conn)->is_dns_request) { - if (conn->dns_server_request) { - /* We had a request on our DNS port: answer it. */ - dnsserv_resolved(conn, answer_type, answer_len, (char*)answer, ttl); - conn->socks_request->has_finished = 1; - return; - } else { - /* This must be a request from the controller. Since answers to those - * requests are not cached, they do not generate an ADDRMAP event on - * their own. */ - tell_controller_about_resolved_result(conn, answer_type, answer_len, - (char*)answer, ttl, expires); - conn->socks_request->has_finished = 1; - return; - } - /* We shouldn't need to free conn here; it gets marked by the caller. */ - } - - if (conn->socks_request->socks_version == 4) { - buf[0] = 0x00; /* version */ - if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { - buf[1] = SOCKS4_GRANTED; - set_uint16(buf+2, 0); - memcpy(buf+4, answer, 4); /* address */ - replylen = SOCKS4_NETWORK_LEN; - } else { /* "error" */ - buf[1] = SOCKS4_REJECT; - memset(buf+2, 0, 6); - replylen = SOCKS4_NETWORK_LEN; - } - } else if (conn->socks_request->socks_version == 5) { - /* SOCKS5 */ - buf[0] = 0x05; /* version */ - if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { - buf[1] = SOCKS5_SUCCEEDED; - buf[2] = 0; /* reserved */ - buf[3] = 0x01; /* IPv4 address type */ - memcpy(buf+4, answer, 4); /* address */ - set_uint16(buf+8, 0); /* port == 0. */ - replylen = 10; - } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { - buf[1] = SOCKS5_SUCCEEDED; - buf[2] = 0; /* reserved */ - buf[3] = 0x04; /* IPv6 address type */ - memcpy(buf+4, answer, 16); /* address */ - set_uint16(buf+20, 0); /* port == 0. */ - replylen = 22; - } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) { - buf[1] = SOCKS5_SUCCEEDED; - buf[2] = 0; /* reserved */ - buf[3] = 0x03; /* Domainname address type */ - buf[4] = (char)answer_len; - memcpy(buf+5, answer, answer_len); /* address */ - set_uint16(buf+5+answer_len, 0); /* port == 0. */ - replylen = 5+answer_len+2; - } else { - buf[1] = SOCKS5_HOST_UNREACHABLE; - memset(buf+2, 0, 8); - replylen = 10; - } - } else { - /* no socks version info; don't send anything back */ - return; - } - connection_ap_handshake_socks_reply(conn, buf, replylen, - (answer_type == RESOLVED_TYPE_IPV4 || - answer_type == RESOLVED_TYPE_IPV6 || - answer_type == RESOLVED_TYPE_HOSTNAME) ? - 0 : END_STREAM_REASON_RESOLVEFAILED); -} - -/** Send a socks reply to stream conn, using the appropriate - * socks version, etc, and mark conn as completed with SOCKS - * handshaking. - * - * If reply is defined, then write replylen bytes of it to conn - * and return, else reply based on endreason (one of - * END_STREAM_REASON_*). If reply is undefined, endreason can't - * be 0 or REASON_DONE. Send endreason to the controller, if appropriate. - */ -void -connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, - size_t replylen, int endreason) -{ - char buf[256]; - socks5_reply_status_t status = - stream_end_reason_to_socks5_response(endreason); - - tor_assert(conn->socks_request); /* make sure it's an AP stream */ - - if (!SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) { - control_event_stream_status(conn, status==SOCKS5_SUCCEEDED ? - STREAM_EVENT_SUCCEEDED : STREAM_EVENT_FAILED, - endreason); - } - - /* Flag this stream's circuit as having completed a stream successfully - * (for path bias) */ - if (status == SOCKS5_SUCCEEDED || - endreason == END_STREAM_REASON_RESOLVEFAILED || - endreason == END_STREAM_REASON_CONNECTREFUSED || - endreason == END_STREAM_REASON_CONNRESET || - endreason == END_STREAM_REASON_NOROUTE || - endreason == END_STREAM_REASON_RESOURCELIMIT) { - if (!conn->edge_.on_circuit || - !CIRCUIT_IS_ORIGIN(conn->edge_.on_circuit)) { - // DNS remaps can trigger this. So can failed hidden service - // lookups. - log_info(LD_BUG, - "No origin circuit for successful SOCKS stream "U64_FORMAT - ". Reason: %d", - U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier), - endreason); - } else { - // XXX: Hrmm. It looks like optimistic data can't go through this - // codepath, but someone should probably test it and make sure. - // We don't want to mark optimistically opened streams as successful. - pathbias_mark_use_success(TO_ORIGIN_CIRCUIT(conn->edge_.on_circuit)); - } - } - - if (conn->socks_request->has_finished) { - log_warn(LD_BUG, "(Harmless.) duplicate calls to " - "connection_ap_handshake_socks_reply."); - return; - } - if (replylen) { /* we already have a reply in mind */ - connection_write_to_buf(reply, replylen, ENTRY_TO_CONN(conn)); - conn->socks_request->has_finished = 1; - return; - } - if (conn->socks_request->socks_version == 4) { - memset(buf,0,SOCKS4_NETWORK_LEN); - buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT); - /* leave version, destport, destip zero */ - connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn)); - } else if (conn->socks_request->socks_version == 5) { - buf[0] = 5; /* version 5 */ - buf[1] = (char)status; - buf[2] = 0; - buf[3] = 1; /* ipv4 addr */ - memset(buf+4,0,6); /* Set external addr/port to 0. - The spec doesn't seem to say what to do here. -RD */ - connection_write_to_buf(buf,10,ENTRY_TO_CONN(conn)); - } - /* If socks_version isn't 4 or 5, don't send anything. - * This can happen in the case of AP bridges. */ - conn->socks_request->has_finished = 1; - return; -} - -/** Read a RELAY_BEGIN or RELAY_BEGINDIR cell from cell, decode it, and - * place the result in bcell. On success return 0; on failure return - * <0 and set *end_reason_out to the end reason we should send back to - * the client. - * - * Return -1 in the case where want to send a RELAY_END cell, and < -1 when - * we don't. - **/ -STATIC int -begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, - uint8_t *end_reason_out) -{ - relay_header_t rh; - const uint8_t *body, *nul; - - memset(bcell, 0, sizeof(*bcell)); - *end_reason_out = END_STREAM_REASON_MISC; - - relay_header_unpack(&rh, cell->payload); - if (rh.length > RELAY_PAYLOAD_SIZE) { - return -2; /*XXXX why not TORPROTOCOL? */ - } - - bcell->stream_id = rh.stream_id; - - if (rh.command == RELAY_COMMAND_BEGIN_DIR) { - bcell->is_begindir = 1; - return 0; - } else if (rh.command != RELAY_COMMAND_BEGIN) { - log_warn(LD_BUG, "Got an unexpected command %d", (int)rh.command); - *end_reason_out = END_STREAM_REASON_INTERNAL; - return -1; - } - - body = cell->payload + RELAY_HEADER_SIZE; - nul = memchr(body, 0, rh.length); - if (! nul) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Relay begin cell has no \\0. Closing."); - *end_reason_out = END_STREAM_REASON_TORPROTOCOL; - return -1; - } - - if (tor_addr_port_split(LOG_PROTOCOL_WARN, - (char*)(body), - &bcell->address,&bcell->port)<0) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Unable to parse addr:port in relay begin cell. Closing."); - *end_reason_out = END_STREAM_REASON_TORPROTOCOL; - return -1; - } - if (bcell->port == 0) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Missing port in relay begin cell. Closing."); - tor_free(bcell->address); - *end_reason_out = END_STREAM_REASON_TORPROTOCOL; - return -1; - } - if (body + rh.length >= nul + 4) - bcell->flags = ntohl(get_uint32(nul+1)); - - return 0; -} - -/** A relay 'begin' or 'begin_dir' cell has arrived, and either we are - * an exit hop for the circuit, or we are the origin and it is a - * rendezvous begin. - * - * Launch a new exit connection and initialize things appropriately. - * - * If it's a rendezvous stream, call connection_exit_connect() on - * it. - * - * For general streams, call dns_resolve() on it first, and only call - * connection_exit_connect() if the dns answer is already known. - * - * Note that we don't call connection_add() on the new stream! We wait - * for connection_exit_connect() to do that. - * - * Return -(some circuit end reason) if we want to tear down circ. - * Else return 0. - */ -int -connection_exit_begin_conn(cell_t *cell, circuit_t *circ) -{ - edge_connection_t *n_stream; - relay_header_t rh; - char *address = NULL; - uint16_t port = 0; - or_circuit_t *or_circ = NULL; - const or_options_t *options = get_options(); - begin_cell_t bcell; - int r; - uint8_t end_reason=0; - - assert_circuit_ok(circ); - if (!CIRCUIT_IS_ORIGIN(circ)) - or_circ = TO_OR_CIRCUIT(circ); - - relay_header_unpack(&rh, cell->payload); - if (rh.length > RELAY_PAYLOAD_SIZE) - return -1; - - /* Note: we have to use relay_send_command_from_edge here, not - * connection_edge_end or connection_edge_send_command, since those require - * that we have a stream connected to a circuit, and we don't connect to a - * circuit until we have a pending/successful resolve. */ - - if (!server_mode(options) && - circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Relay begin cell at non-server. Closing."); - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_EXITPOLICY, NULL); - return 0; - } - - r = begin_cell_parse(cell, &bcell, &end_reason); - if (r < -1) { - return -1; - } else if (r == -1) { - tor_free(bcell.address); - relay_send_end_cell_from_edge(rh.stream_id, circ, end_reason, NULL); - return 0; - } - - if (! bcell.is_begindir) { - /* Steal reference */ - address = bcell.address; - port = bcell.port; - - if (or_circ && or_circ->p_chan) { - if (!options->AllowSingleHopExits && - (or_circ->is_first_hop || - (!connection_or_digest_is_known_relay( - or_circ->p_chan->identity_digest) && - should_refuse_unknown_exits(options)))) { - /* Don't let clients use us as a single-hop proxy, unless the user - * has explicitly allowed that in the config. It attracts attackers - * and users who'd be better off with, well, single-hop proxies. - */ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Attempt by %s to open a stream %s. Closing.", - safe_str(channel_get_canonical_remote_descr(or_circ->p_chan)), - or_circ->is_first_hop ? "on first hop of circuit" : - "from unknown relay"); - relay_send_end_cell_from_edge(rh.stream_id, circ, - or_circ->is_first_hop ? - END_STREAM_REASON_TORPROTOCOL : - END_STREAM_REASON_MISC, - NULL); - tor_free(address); - return 0; - } - } - } else if (rh.command == RELAY_COMMAND_BEGIN_DIR) { - if (!directory_permits_begindir_requests(options) || - circ->purpose != CIRCUIT_PURPOSE_OR) { - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_NOTDIRECTORY, NULL); - return 0; - } - /* Make sure to get the 'real' address of the previous hop: the - * caller might want to know whether his IP address has changed, and - * we might already have corrected base_.addr[ess] for the relay's - * canonical IP address. */ - if (or_circ && or_circ->p_chan) - address = tor_strdup(channel_get_actual_remote_address(or_circ->p_chan)); - else - address = tor_strdup("127.0.0.1"); - port = 1; /* XXXX This value is never actually used anywhere, and there - * isn't "really" a connection here. But we - * need to set it to something nonzero. */ - } else { - log_warn(LD_BUG, "Got an unexpected command %d", (int)rh.command); - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_INTERNAL, NULL); - return 0; - } - - if (! options->IPv6Exit) { - /* I don't care if you prefer IPv6; I can't give you any. */ - bcell.flags &= ~BEGIN_FLAG_IPV6_PREFERRED; - /* If you don't want IPv4, I can't help. */ - if (bcell.flags & BEGIN_FLAG_IPV4_NOT_OK) { - tor_free(address); - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_EXITPOLICY, NULL); - return 0; - } - } - - log_debug(LD_EXIT,"Creating new exit connection."); - /* The 'AF_INET' here is temporary; we might need to change it later in - * connection_exit_connect(). */ - n_stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET); - - /* Remember the tunneled request ID in the new edge connection, so that - * we can measure download times. */ - n_stream->dirreq_id = circ->dirreq_id; - - n_stream->base_.purpose = EXIT_PURPOSE_CONNECT; - n_stream->begincell_flags = bcell.flags; - n_stream->stream_id = rh.stream_id; - n_stream->base_.port = port; - /* leave n_stream->s at -1, because it's not yet valid */ - n_stream->package_window = STREAMWINDOW_START; - n_stream->deliver_window = STREAMWINDOW_START; - - if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) { - origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); - log_info(LD_REND,"begin is for rendezvous. configuring stream."); - n_stream->base_.address = tor_strdup("(rendezvous)"); - n_stream->base_.state = EXIT_CONN_STATE_CONNECTING; - n_stream->rend_data = rend_data_dup(origin_circ->rend_data); - tor_assert(connection_edge_is_rendezvous_stream(n_stream)); - assert_circuit_ok(circ); - if (rend_service_set_connection_addr_port(n_stream, origin_circ) < 0) { - log_info(LD_REND,"Didn't find rendezvous service (port %d)", - n_stream->base_.port); - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_EXITPOLICY, - origin_circ->cpath->prev); - connection_free(TO_CONN(n_stream)); - tor_free(address); - return 0; - } - assert_circuit_ok(circ); - log_debug(LD_REND,"Finished assigning addr/port"); - n_stream->cpath_layer = origin_circ->cpath->prev; /* link it */ - - /* add it into the linked list of p_streams on this circuit */ - n_stream->next_stream = origin_circ->p_streams; - n_stream->on_circuit = circ; - origin_circ->p_streams = n_stream; - assert_circuit_ok(circ); - - connection_exit_connect(n_stream); - - /* For path bias: This circuit was used successfully */ - pathbias_mark_use_success(origin_circ); - - tor_free(address); - return 0; - } - tor_strlower(address); - n_stream->base_.address = address; - n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; - /* default to failed, change in dns_resolve if it turns out not to fail */ - - if (we_are_hibernating()) { - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_HIBERNATING, NULL); - connection_free(TO_CONN(n_stream)); - return 0; - } - - n_stream->on_circuit = circ; - - if (rh.command == RELAY_COMMAND_BEGIN_DIR) { - tor_addr_t tmp_addr; - tor_assert(or_circ); - if (or_circ->p_chan && - channel_get_addr_if_possible(or_circ->p_chan, &tmp_addr)) { - tor_addr_copy(&n_stream->base_.addr, &tmp_addr); - } - return connection_exit_connect_dir(n_stream); - } - - log_debug(LD_EXIT,"about to start the dns_resolve()."); - - /* send it off to the gethostbyname farm */ - switch (dns_resolve(n_stream)) { - case 1: /* resolve worked; now n_stream is attached to circ. */ - assert_circuit_ok(circ); - log_debug(LD_EXIT,"about to call connection_exit_connect()."); - connection_exit_connect(n_stream); - return 0; - case -1: /* resolve failed */ - relay_send_end_cell_from_edge(rh.stream_id, circ, - END_STREAM_REASON_RESOLVEFAILED, NULL); - /* n_stream got freed. don't touch it. */ - break; - case 0: /* resolve added to pending list */ - assert_circuit_ok(circ); - break; - } - return 0; -} - -/** - * Called when we receive a RELAY_COMMAND_RESOLVE cell 'cell' along the - * circuit circ; - * begin resolving the hostname, and (eventually) reply with a RESOLVED cell. - */ -int -connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) -{ - edge_connection_t *dummy_conn; - relay_header_t rh; - - assert_circuit_ok(TO_CIRCUIT(circ)); - relay_header_unpack(&rh, cell->payload); - if (rh.length > RELAY_PAYLOAD_SIZE) - return -1; - - /* This 'dummy_conn' only exists to remember the stream ID - * associated with the resolve request; and to make the - * implementation of dns.c more uniform. (We really only need to - * remember the circuit, the stream ID, and the hostname to be - * resolved; but if we didn't store them in a connection like this, - * the housekeeping in dns.c would get way more complicated.) - */ - dummy_conn = edge_connection_new(CONN_TYPE_EXIT, AF_INET); - dummy_conn->stream_id = rh.stream_id; - dummy_conn->base_.address = tor_strndup( - (char*)cell->payload+RELAY_HEADER_SIZE, - rh.length); - dummy_conn->base_.port = 0; - dummy_conn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; - dummy_conn->base_.purpose = EXIT_PURPOSE_RESOLVE; - - dummy_conn->on_circuit = TO_CIRCUIT(circ); - - /* send it off to the gethostbyname farm */ - switch (dns_resolve(dummy_conn)) { - case -1: /* Impossible to resolve; a resolved cell was sent. */ - /* Connection freed; don't touch it. */ - return 0; - case 1: /* The result was cached; a resolved cell was sent. */ - if (!dummy_conn->base_.marked_for_close) - connection_free(TO_CONN(dummy_conn)); - return 0; - case 0: /* resolve added to pending list */ - assert_circuit_ok(TO_CIRCUIT(circ)); - break; - } - return 0; -} - -/** Connect to conn's specified addr and port. If it worked, conn - * has now been added to the connection_array. - * - * Send back a connected cell. Include the resolved IP of the destination - * address, but only if it's a general exit stream. (Rendezvous - * streams must not reveal what IP they connected to.) - */ -void -connection_exit_connect(edge_connection_t *edge_conn) -{ - const tor_addr_t *addr; - uint16_t port; - connection_t *conn = TO_CONN(edge_conn); - int socket_error = 0; - - if ( (!connection_edge_is_rendezvous_stream(edge_conn) && - router_compare_to_my_exit_policy(&edge_conn->base_.addr, - edge_conn->base_.port)) || - (tor_addr_family(&conn->addr) == AF_INET6 && - ! get_options()->IPv6Exit)) { - log_info(LD_EXIT,"%s:%d failed exit policy. Closing.", - escaped_safe_str_client(conn->address), conn->port); - connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY); - circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); - connection_free(conn); - return; - } - - addr = &conn->addr; - port = conn->port; - - if (tor_addr_family(addr) == AF_INET6) - conn->socket_family = AF_INET6; - - log_debug(LD_EXIT,"about to try connecting"); - switch (connection_connect(conn, conn->address, addr, port, &socket_error)) { - case -1: { - int reason = errno_to_stream_end_reason(socket_error); - connection_edge_end(edge_conn, reason); - circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); - connection_free(conn); - return; - } - case 0: - conn->state = EXIT_CONN_STATE_CONNECTING; - - connection_watch_events(conn, READ_EVENT | WRITE_EVENT); - /* writable indicates finish; - * readable/error indicates broken link in windows-land. */ - return; - /* case 1: fall through */ - } - - conn->state = EXIT_CONN_STATE_OPEN; - if (connection_get_outbuf_len(conn)) { - /* in case there are any queued data cells, from e.g. optimistic data */ - IF_HAS_NO_BUFFEREVENT(conn) - connection_watch_events(conn, READ_EVENT|WRITE_EVENT); - } else { - IF_HAS_NO_BUFFEREVENT(conn) - connection_watch_events(conn, READ_EVENT); - } - - /* also, deliver a 'connected' cell back through the circuit. */ - if (connection_edge_is_rendezvous_stream(edge_conn)) { - /* rendezvous stream */ - /* don't send an address back! */ - connection_edge_send_command(edge_conn, - RELAY_COMMAND_CONNECTED, - NULL, 0); - } else { /* normal stream */ - uint8_t connected_payload[MAX_CONNECTED_CELL_PAYLOAD_LEN]; - int connected_payload_len = - connected_cell_format_payload(connected_payload, &conn->addr, - edge_conn->address_ttl); - if (connected_payload_len < 0) { - connection_edge_end(edge_conn, END_STREAM_REASON_INTERNAL); - circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); - connection_free(conn); - return; - } - - connection_edge_send_command(edge_conn, - RELAY_COMMAND_CONNECTED, - (char*)connected_payload, - connected_payload_len); - } -} - -/** Given an exit conn that should attach to us as a directory server, open a - * bridge connection with a linked connection pair, create a new directory - * conn, and join them together. Return 0 on success (or if there was an - * error we could send back an end cell for). Return -(some circuit end - * reason) if the circuit needs to be torn down. Either connects - * exitconn, frees it, or marks it, as appropriate. - */ -static int -connection_exit_connect_dir(edge_connection_t *exitconn) -{ - dir_connection_t *dirconn = NULL; - or_circuit_t *circ = TO_OR_CIRCUIT(exitconn->on_circuit); - - log_info(LD_EXIT, "Opening local connection for anonymized directory exit"); - - exitconn->base_.state = EXIT_CONN_STATE_OPEN; - - dirconn = dir_connection_new(tor_addr_family(&exitconn->base_.addr)); - - tor_addr_copy(&dirconn->base_.addr, &exitconn->base_.addr); - dirconn->base_.port = 0; - dirconn->base_.address = tor_strdup(exitconn->base_.address); - dirconn->base_.type = CONN_TYPE_DIR; - dirconn->base_.purpose = DIR_PURPOSE_SERVER; - dirconn->base_.state = DIR_CONN_STATE_SERVER_COMMAND_WAIT; - - /* Note that the new dir conn belongs to the same tunneled request as - * the edge conn, so that we can measure download times. */ - dirconn->dirreq_id = exitconn->dirreq_id; - - connection_link_connections(TO_CONN(dirconn), TO_CONN(exitconn)); - - if (connection_add(TO_CONN(exitconn))<0) { - connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); - connection_free(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); - return 0; - } - - /* link exitconn to circ, now that we know we can use it. */ - exitconn->next_stream = circ->n_streams; - circ->n_streams = exitconn; - - if (connection_add(TO_CONN(dirconn))<0) { - connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); - connection_close_immediate(TO_CONN(exitconn)); - connection_mark_for_close(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); - return 0; - } - - connection_start_reading(TO_CONN(dirconn)); - connection_start_reading(TO_CONN(exitconn)); - - if (connection_edge_send_command(exitconn, - RELAY_COMMAND_CONNECTED, NULL, 0) < 0) { - connection_mark_for_close(TO_CONN(exitconn)); - connection_mark_for_close(TO_CONN(dirconn)); - return 0; - } - - return 0; -} - -/** Return 1 if conn is a rendezvous stream, or 0 if - * it is a general stream. - */ -int -connection_edge_is_rendezvous_stream(edge_connection_t *conn) -{ - tor_assert(conn); - if (conn->rend_data) - return 1; - return 0; -} - -/** Return 1 if router exit is likely to allow stream conn - * to exit from it, or 0 if it probably will not allow it. - * (We might be uncertain if conn's destination address has not yet been - * resolved.) - */ -int -connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit) -{ - const or_options_t *options = get_options(); - - tor_assert(conn); - tor_assert(conn->socks_request); - tor_assert(exit); - - /* If a particular exit node has been requested for the new connection, - * make sure the exit node of the existing circuit matches exactly. - */ - if (conn->chosen_exit_name) { - const node_t *chosen_exit = - node_get_by_nickname(conn->chosen_exit_name, 1); - if (!chosen_exit || tor_memneq(chosen_exit->identity, - exit->identity, DIGEST_LEN)) { - /* doesn't match */ -// log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.", -// conn->chosen_exit_name, exit->nickname); - return 0; - } - } - - if (conn->use_begindir) { - /* Internal directory fetches do not count as exiting. */ - return 1; - } - - if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) { - tor_addr_t addr, *addrp = NULL; - addr_policy_result_t r; - if (0 == tor_addr_parse(&addr, conn->socks_request->address)) { - addrp = &addr; - } else if (!conn->ipv4_traffic_ok && conn->ipv6_traffic_ok) { - tor_addr_make_null(&addr, AF_INET6); - addrp = &addr; - } else if (conn->ipv4_traffic_ok && !conn->ipv6_traffic_ok) { - tor_addr_make_null(&addr, AF_INET); - addrp = &addr; - } - r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,exit); - if (r == ADDR_POLICY_REJECTED) - return 0; /* We know the address, and the exit policy rejects it. */ - if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name) - return 0; /* We don't know the addr, but the exit policy rejects most - * addresses with this port. Since the user didn't ask for - * this node, err on the side of caution. */ - } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) { - /* Don't send DNS requests to non-exit servers by default. */ - if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit)) - return 0; - } - if (routerset_contains_node(options->ExcludeExitNodesUnion_, exit)) { - /* Not a suitable exit. Refuse it. */ - return 0; - } - - return 1; -} - -/** If address is of the form "y.onion" with a well-formed handle y: - * Put a NUL after y, lower-case it, and return ONION_HOSTNAME. - * - * If address is of the form "x.y.onion" with a well-formed handle x: - * Drop "x.", put a NUL after y, lower-case it, and return ONION_HOSTNAME. - * - * If address is of the form "y.onion" with a badly-formed handle y: - * Return BAD_HOSTNAME and log a message. - * - * If address is of the form "y.exit": - * Put a NUL after y and return EXIT_HOSTNAME. - * - * Otherwise: - * Return NORMAL_HOSTNAME and change nothing. - */ -hostname_type_t -parse_extended_hostname(char *address) -{ - char *s; - char *q; - char query[REND_SERVICE_ID_LEN_BASE32+1]; - - s = strrchr(address,'.'); - if (!s) - return NORMAL_HOSTNAME; /* no dot, thus normal */ - if (!strcmp(s+1,"exit")) { - *s = 0; /* NUL-terminate it */ - return EXIT_HOSTNAME; /* .exit */ - } - if (strcmp(s+1,"onion")) - return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */ - - /* so it is .onion */ - *s = 0; /* NUL-terminate it */ - /* locate a 'sub-domain' component, in order to remove it */ - q = strrchr(address, '.'); - if (q == address) { - goto failed; /* reject sub-domain, as DNS does */ - } - q = (NULL == q) ? address : q + 1; - if (strlcpy(query, q, REND_SERVICE_ID_LEN_BASE32+1) >= - REND_SERVICE_ID_LEN_BASE32+1) - goto failed; - if (q != address) { - memmove(address, q, strlen(q) + 1 /* also get \0 */); - } - if (rend_valid_service_id(query)) { - return ONION_HOSTNAME; /* success */ - } - failed: - /* otherwise, return to previous state and return 0 */ - *s = '.'; - log_warn(LD_APP, "Invalid onion hostname %s; rejecting", - safe_str_client(address)); - return BAD_HOSTNAME; -} - -/** Return true iff the (possibly NULL) alen-byte chunk of memory at - * a is equal to the (possibly NULL) blen-byte chunk of memory - * at b. */ -static int -memeq_opt(const char *a, size_t alen, const char *b, size_t blen) -{ - if (a == NULL) { - return (b == NULL); - } else if (b == NULL) { - return 0; - } else if (alen != blen) { - return 0; - } else { - return tor_memeq(a, b, alen); - } -} - -/** - * Return true iff none of the isolation flags and fields in conn - * should prevent it from being attached to circ. - */ -int -connection_edge_compatible_with_circuit(const entry_connection_t *conn, - const origin_circuit_t *circ) -{ - const uint8_t iso = conn->isolation_flags; - const socks_request_t *sr = conn->socks_request; - - /* If circ has never been used for an isolated connection, we can - * totally use it for this one. */ - if (!circ->isolation_values_set) - return 1; - - /* If circ has been used for connections having more than one value - * for some field f, it will have the corresponding bit set in - * isolation_flags_mixed. If isolation_flags_mixed has any bits - * in common with iso, then conn must be isolated from at least - * one stream that has been attached to circ. */ - if ((iso & circ->isolation_flags_mixed) != 0) { - /* For at least one field where conn is isolated, the circuit - * already has mixed streams. */ - return 0; - } - - if (! conn->original_dest_address) { - log_warn(LD_BUG, "Reached connection_edge_compatible_with_circuit without " - "having set conn->original_dest_address"); - ((entry_connection_t*)conn)->original_dest_address = - tor_strdup(conn->socks_request->address); - } - - if ((iso & ISO_STREAM) && - (circ->associated_isolated_stream_global_id != - ENTRY_TO_CONN(conn)->global_identifier)) - return 0; - - if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port) - return 0; - if ((iso & ISO_DESTADDR) && - strcasecmp(conn->original_dest_address, circ->dest_address)) - return 0; - if ((iso & ISO_SOCKSAUTH) && - (! memeq_opt(sr->username, sr->usernamelen, - circ->socks_username, circ->socks_username_len) || - ! memeq_opt(sr->password, sr->passwordlen, - circ->socks_password, circ->socks_password_len))) - return 0; - if ((iso & ISO_CLIENTPROTO) && - (conn->socks_request->listener_type != circ->client_proto_type || - conn->socks_request->socks_version != circ->client_proto_socksver)) - return 0; - if ((iso & ISO_CLIENTADDR) && - !tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr)) - return 0; - if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group) - return 0; - if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch) - return 0; - - return 1; -} - -/** - * If dry_run is false, update circ's isolation flags and fields - * to reflect having had conn attached to it, and return 0. Otherwise, - * if dry_run is true, then make no changes to circ, and return - * a bitfield of isolation flags that we would have to set in - * isolation_flags_mixed to add conn to circ, or -1 if - * circ has had no streams attached to it. - */ -int -connection_edge_update_circuit_isolation(const entry_connection_t *conn, - origin_circuit_t *circ, - int dry_run) -{ - const socks_request_t *sr = conn->socks_request; - if (! conn->original_dest_address) { - log_warn(LD_BUG, "Reached connection_update_circuit_isolation without " - "having set conn->original_dest_address"); - ((entry_connection_t*)conn)->original_dest_address = - tor_strdup(conn->socks_request->address); - } - - if (!circ->isolation_values_set) { - if (dry_run) - return -1; - circ->associated_isolated_stream_global_id = - ENTRY_TO_CONN(conn)->global_identifier; - circ->dest_port = conn->socks_request->port; - circ->dest_address = tor_strdup(conn->original_dest_address); - circ->client_proto_type = conn->socks_request->listener_type; - circ->client_proto_socksver = conn->socks_request->socks_version; - tor_addr_copy(&circ->client_addr, &ENTRY_TO_CONN(conn)->addr); - circ->session_group = conn->session_group; - circ->nym_epoch = conn->nym_epoch; - circ->socks_username = sr->username ? - tor_memdup(sr->username, sr->usernamelen) : NULL; - circ->socks_password = sr->password ? - tor_memdup(sr->password, sr->passwordlen) : NULL; - circ->socks_username_len = sr->usernamelen; - circ->socks_password_len = sr->passwordlen; - - circ->isolation_values_set = 1; - return 0; - } else { - uint8_t mixed = 0; - if (conn->socks_request->port != circ->dest_port) - mixed |= ISO_DESTPORT; - if (strcasecmp(conn->original_dest_address, circ->dest_address)) - mixed |= ISO_DESTADDR; - if (!memeq_opt(sr->username, sr->usernamelen, - circ->socks_username, circ->socks_username_len) || - !memeq_opt(sr->password, sr->passwordlen, - circ->socks_password, circ->socks_password_len)) - mixed |= ISO_SOCKSAUTH; - if ((conn->socks_request->listener_type != circ->client_proto_type || - conn->socks_request->socks_version != circ->client_proto_socksver)) - mixed |= ISO_CLIENTPROTO; - if (!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr)) - mixed |= ISO_CLIENTADDR; - if (conn->session_group != circ->session_group) - mixed |= ISO_SESSIONGRP; - if (conn->nym_epoch != circ->nym_epoch) - mixed |= ISO_NYM_EPOCH; - - if (dry_run) - return mixed; - - if ((mixed & conn->isolation_flags) != 0) { - log_warn(LD_BUG, "Updating a circuit with seemingly incompatible " - "isolation flags."); - } - circ->isolation_flags_mixed |= mixed; - return 0; - } -} - -/** - * Clear the isolation settings on circ. - * - * This only works on an open circuit that has never had a stream attached to - * it, and whose isolation settings are hypothetical. (We set hypothetical - * isolation settings on circuits as we're launching them, so that we - * know whether they can handle more streams or whether we need to launch - * even more circuits. Once the circuit is open, if it turns out that - * we no longer have any streams to attach to it, we clear the isolation flags - * and data so that other streams can have a chance.) - */ -void -circuit_clear_isolation(origin_circuit_t *circ) -{ - if (circ->isolation_any_streams_attached) { - log_warn(LD_BUG, "Tried to clear the isolation status of a dirty circuit"); - return; - } - if (TO_CIRCUIT(circ)->state != CIRCUIT_STATE_OPEN) { - log_warn(LD_BUG, "Tried to clear the isolation status of a non-open " - "circuit"); - return; - } - - circ->isolation_values_set = 0; - circ->isolation_flags_mixed = 0; - circ->associated_isolated_stream_global_id = 0; - circ->client_proto_type = 0; - circ->client_proto_socksver = 0; - circ->dest_port = 0; - tor_addr_make_unspec(&circ->client_addr); - tor_free(circ->dest_address); - circ->session_group = -1; - circ->nym_epoch = 0; - if (circ->socks_username) { - memwipe(circ->socks_username, 0x11, circ->socks_username_len); - tor_free(circ->socks_username); - } - if (circ->socks_password) { - memwipe(circ->socks_password, 0x05, circ->socks_password_len); - tor_free(circ->socks_password); - } - circ->socks_username_len = circ->socks_password_len = 0; -} - diff --git a/src/tor/connection_edge.h b/src/tor/connection_edge.h deleted file mode 100644 index e3a95ad..0000000 --- a/src/tor/connection_edge.h +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection_edge.h - * \brief Header file for connection_edge.c. - **/ - -#ifndef TOR_CONNECTION_EDGE_H -#define TOR_CONNECTION_EDGE_H - -#include "testsupport.h" - -#define connection_mark_unattached_ap(conn, endreason) \ - connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__) - -void connection_mark_unattached_ap_(entry_connection_t *conn, int endreason, - int line, const char *file); -int connection_edge_reached_eof(edge_connection_t *conn); -int connection_edge_process_inbuf(edge_connection_t *conn, - int package_partial); -int connection_edge_destroy(circid_t circ_id, edge_connection_t *conn); -int connection_edge_end(edge_connection_t *conn, uint8_t reason); -int connection_edge_end_errno(edge_connection_t *conn); -int connection_edge_flushed_some(edge_connection_t *conn); -int connection_edge_finished_flushing(edge_connection_t *conn); -int connection_edge_finished_connecting(edge_connection_t *conn); - -void connection_ap_about_to_close(entry_connection_t *edge_conn); -void connection_exit_about_to_close(edge_connection_t *edge_conn); - -int connection_ap_handshake_send_begin(entry_connection_t *ap_conn); -int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn); - -entry_connection_t *connection_ap_make_link(connection_t *partner, - char *address, uint16_t port, - const char *digest, - int session_group, - int isolation_flags, - int use_begindir, int want_onehop); -void connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, - size_t replylen, - int endreason); -void connection_ap_handshake_socks_resolved(entry_connection_t *conn, - int answer_type, - size_t answer_len, - const uint8_t *answer, - int ttl, - time_t expires); - -int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); -int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ); -void connection_exit_connect(edge_connection_t *conn); -int connection_edge_is_rendezvous_stream(edge_connection_t *conn); -int connection_ap_can_use_exit(const entry_connection_t *conn, - const node_t *exit); -void connection_ap_expire_beginning(void); -void connection_ap_attach_pending(void); -void connection_ap_fail_onehop(const char *failed_digest, - cpath_build_state_t *build_state); -void circuit_discard_optional_exit_enclaves(extend_info_t *info); -int connection_ap_detach_retriable(entry_connection_t *conn, - origin_circuit_t *circ, - int reason); -int connection_ap_process_transparent(entry_connection_t *conn); - -int address_is_invalid_destination(const char *address, int client); - -int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath); -int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, - origin_circuit_t *circ, - crypt_path_t *cpath); - -/** Possible return values for parse_extended_hostname. */ -typedef enum hostname_type_t { - NORMAL_HOSTNAME, ONION_HOSTNAME, EXIT_HOSTNAME, BAD_HOSTNAME -} hostname_type_t; -hostname_type_t parse_extended_hostname(char *address); - -#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) -int get_pf_socket(void); -#endif - -int connection_edge_compatible_with_circuit(const entry_connection_t *conn, - const origin_circuit_t *circ); -int connection_edge_update_circuit_isolation(const entry_connection_t *conn, - origin_circuit_t *circ, - int dry_run); -void circuit_clear_isolation(origin_circuit_t *circ); -streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ); - -/** @name Begin-cell flags - * - * These flags are used in RELAY_BEGIN cells to change the default behavior - * of the cell. - * - * @{ - **/ -/** When this flag is set, the client is willing to get connected to IPv6 - * addresses */ -#define BEGIN_FLAG_IPV6_OK (1u<<0) -/** When this flag is set, the client DOES NOT support connecting to IPv4 - * addresses. (The sense of this flag is inverted from IPV6_OK, so that the - * old default behavior of Tor is equivalent to having all flags set to 0.) - **/ -#define BEGIN_FLAG_IPV4_NOT_OK (1u<<1) -/** When this flag is set, if we find both an IPv4 and an IPv6 address, - * we use the IPv6 address. Otherwise we use the IPv4 address. */ -#define BEGIN_FLAG_IPV6_PREFERRED (1u<<2) -/**@}*/ - -#ifdef CONNECTION_EDGE_PRIVATE - -/** A parsed BEGIN or BEGIN_DIR cell */ -typedef struct begin_cell_t { - /** The address the client has asked us to connect to, or NULL if this is - * a BEGIN_DIR cell*/ - char *address; - /** The flags specified in the BEGIN cell's body. One or more of - * BEGIN_FLAG_*. */ - uint32_t flags; - /** The client's requested port. */ - uint16_t port; - /** The client's requested Stream ID */ - uint16_t stream_id; - /** True iff this is a BEGIN_DIR cell. */ - unsigned is_begindir : 1; -} begin_cell_t; - -STATIC int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, - uint8_t *end_reason_out); -STATIC int connected_cell_format_payload(uint8_t *payload_out, - const tor_addr_t *addr, - uint32_t ttl); -#endif - -#endif - diff --git a/src/tor/connection_or.c b/src/tor/connection_or.c deleted file mode 100644 index b89f7bf..0000000 --- a/src/tor/connection_or.c +++ /dev/null @@ -1,2452 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection_or.c - * \brief Functions to handle OR connections, TLS handshaking, and - * cells on the network. - **/ -#include "or.h" -#include "buffers.h" -/* - * Define this so we get channel internal functions, since we're implementing - * part of a subclass (channel_tls_t). - */ -#define TOR_CHANNEL_INTERNAL_ -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitstats.h" -#include "command.h" -#include "config.h" -#include "connection.h" -#include "connection_or.h" -#include "control.h" -#include "dirserv.h" -#include "entrynodes.h" -#include "geoip.h" -#include "onion_main.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "reasons.h" -#include "relay.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "ext_orport.h" -#ifdef USE_BUFFEREVENTS -#include -#endif - -static int connection_tls_finish_handshake(or_connection_t *conn); -static int connection_or_launch_v3_or_handshake(or_connection_t *conn); -static int connection_or_process_cells_from_inbuf(or_connection_t *conn); -static int connection_or_check_valid_tls_handshake(or_connection_t *conn, - int started_here, - char *digest_rcvd_out); - -static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn); - -static unsigned int -connection_or_is_bad_for_new_circs(or_connection_t *or_conn); -static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn); - -/* - * Call this when changing connection state, so notifications to the owning - * channel can be handled. - */ - -static void connection_or_change_state(or_connection_t *conn, uint8_t state); - -#ifdef USE_BUFFEREVENTS -static void connection_or_handle_event_cb(struct bufferevent *bufev, - short event, void *arg); -#include /*XXXX REMOVE */ -#endif - -/**************************************************************/ - -/** Map from identity digest of connected OR or desired OR to a connection_t - * with that identity digest. If there is more than one such connection_t, - * they form a linked list, with next_with_same_id as the next pointer. */ -static digestmap_t *orconn_identity_map = NULL; - -/** Global map between Extended ORPort identifiers and OR - * connections. */ -static digestmap_t *orconn_ext_or_id_map = NULL; - -/** If conn is listed in orconn_identity_map, remove it, and clear - * conn->identity_digest. Otherwise do nothing. */ -void -connection_or_remove_from_identity_map(or_connection_t *conn) -{ - or_connection_t *tmp; - tor_assert(conn); - if (!orconn_identity_map) - return; - tmp = digestmap_get(orconn_identity_map, conn->identity_digest); - if (!tmp) { - if (!tor_digest_is_zero(conn->identity_digest)) { - log_warn(LD_BUG, "Didn't find connection '%s' on identity map when " - "trying to remove it.", - conn->nickname ? conn->nickname : "NULL"); - } - return; - } - if (conn == tmp) { - if (conn->next_with_same_id) - digestmap_set(orconn_identity_map, conn->identity_digest, - conn->next_with_same_id); - else - digestmap_remove(orconn_identity_map, conn->identity_digest); - } else { - while (tmp->next_with_same_id) { - if (tmp->next_with_same_id == conn) { - tmp->next_with_same_id = conn->next_with_same_id; - break; - } - tmp = tmp->next_with_same_id; - } - } - memset(conn->identity_digest, 0, DIGEST_LEN); - conn->next_with_same_id = NULL; -} - -/** Remove all entries from the identity-to-orconn map, and clear - * all identities in OR conns.*/ -void -connection_or_clear_identity_map(void) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == CONN_TYPE_OR) { - or_connection_t *or_conn = TO_OR_CONN(conn); - memset(or_conn->identity_digest, 0, DIGEST_LEN); - or_conn->next_with_same_id = NULL; - } - }); - - digestmap_free(orconn_identity_map, NULL); - orconn_identity_map = NULL; -} - -/** Change conn->identity_digest to digest, and add conn into - * orconn_digest_map. */ -static void -connection_or_set_identity_digest(or_connection_t *conn, const char *digest) -{ - or_connection_t *tmp; - tor_assert(conn); - tor_assert(digest); - - if (!orconn_identity_map) - orconn_identity_map = digestmap_new(); - if (tor_memeq(conn->identity_digest, digest, DIGEST_LEN)) - return; - - /* If the identity was set previously, remove the old mapping. */ - if (! tor_digest_is_zero(conn->identity_digest)) { - connection_or_remove_from_identity_map(conn); - if (conn->chan) - channel_clear_identity_digest(TLS_CHAN_TO_BASE(conn->chan)); - } - - memcpy(conn->identity_digest, digest, DIGEST_LEN); - - /* If we're setting the ID to zero, don't add a mapping. */ - if (tor_digest_is_zero(digest)) - return; - - tmp = digestmap_set(orconn_identity_map, digest, conn); - conn->next_with_same_id = tmp; - - /* Deal with channels */ - if (conn->chan) - channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), digest); - -#if 1 - /* Testing code to check for bugs in representation. */ - for (; tmp; tmp = tmp->next_with_same_id) { - tor_assert(tor_memeq(tmp->identity_digest, digest, DIGEST_LEN)); - tor_assert(tmp != conn); - } -#endif -} - -/** Remove the Extended ORPort identifier of conn from the - * global identifier list. Also, clear the identifier from the - * connection itself. */ -void -connection_or_remove_from_ext_or_id_map(or_connection_t *conn) -{ - or_connection_t *tmp; - if (!orconn_ext_or_id_map) - return; - if (!conn->ext_or_conn_id) - return; - - tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); - if (!tor_digest_is_zero(conn->ext_or_conn_id)) - tor_assert(tmp == conn); - - memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); -} - -/** Return the connection whose ext_or_id is id. Return NULL if no such - * connection is found. */ -or_connection_t * -connection_or_get_by_ext_or_id(const char *id) -{ - if (!orconn_ext_or_id_map) - return NULL; - return digestmap_get(orconn_ext_or_id_map, id); -} - -/** Deallocate the global Extended ORPort identifier list */ -void -connection_or_clear_ext_or_id_map(void) -{ - digestmap_free(orconn_ext_or_id_map, NULL); - orconn_ext_or_id_map = NULL; -} - -/** Creates an Extended ORPort identifier for conn and deposits - * it into the global list of identifiers. */ -void -connection_or_set_ext_or_identifier(or_connection_t *conn) -{ - char random_id[EXT_OR_CONN_ID_LEN]; - or_connection_t *tmp; - - if (!orconn_ext_or_id_map) - orconn_ext_or_id_map = digestmap_new(); - - /* Remove any previous identifiers: */ - if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) - connection_or_remove_from_ext_or_id_map(conn); - - do { - crypto_rand(random_id, sizeof(random_id)); - } while (digestmap_get(orconn_ext_or_id_map, random_id)); - - if (!conn->ext_or_conn_id) - conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); - - memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); - - tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); - tor_assert(!tmp); -} - -/**************************************************************/ - -/** Map from a string describing what a non-open OR connection was doing when - * failed, to an intptr_t describing the count of connections that failed that - * way. Note that the count is stored _as_ the pointer. - */ -static strmap_t *broken_connection_counts; - -/** If true, do not record information in broken_connection_counts. */ -static int disable_broken_connection_counts = 0; - -/** Record that an OR connection failed in state. */ -static void -note_broken_connection(const char *state) -{ - void *ptr; - intptr_t val; - if (disable_broken_connection_counts) - return; - - if (!broken_connection_counts) - broken_connection_counts = strmap_new(); - - ptr = strmap_get(broken_connection_counts, state); - val = (intptr_t)ptr; - val++; - ptr = (void*)val; - strmap_set(broken_connection_counts, state, ptr); -} - -/** Forget all recorded states for failed connections. If - * stop_recording is true, don't record any more. */ -void -clear_broken_connection_map(int stop_recording) -{ - if (broken_connection_counts) - strmap_free(broken_connection_counts, NULL); - broken_connection_counts = NULL; - if (stop_recording) - disable_broken_connection_counts = 1; -} - -/** Write a detailed description the state of orconn into the - * buflen-byte buffer at buf. This description includes not - * only the OR-conn level state but also the TLS state. It's useful for - * diagnosing broken handshakes. */ -static void -connection_or_get_state_description(or_connection_t *orconn, - char *buf, size_t buflen) -{ - connection_t *conn = TO_CONN(orconn); - const char *conn_state; - char tls_state[256]; - - tor_assert(conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR); - - conn_state = conn_state_to_string(conn->type, conn->state); - tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state)); - - tor_snprintf(buf, buflen, "%s with SSL state %s", conn_state, tls_state); -} - -/** Record the current state of orconn as the state of a broken - * connection. */ -static void -connection_or_note_state_when_broken(or_connection_t *orconn) -{ - char buf[256]; - if (disable_broken_connection_counts) - return; - connection_or_get_state_description(orconn, buf, sizeof(buf)); - log_info(LD_HANDSHAKE,"Connection died in state '%s'", buf); - note_broken_connection(buf); -} - -/** Helper type used to sort connection states and find the most frequent. */ -typedef struct broken_state_count_t { - intptr_t count; - const char *state; -} broken_state_count_t; - -/** Helper function used to sort broken_state_count_t by frequency. */ -static int -broken_state_count_compare(const void **a_ptr, const void **b_ptr) -{ - const broken_state_count_t *a = *a_ptr, *b = *b_ptr; - if (b->count < a->count) - return -1; - else if (b->count == a->count) - return 0; - else - return 1; -} - -/** Upper limit on the number of different states to report for connection - * failure. */ -#define MAX_REASONS_TO_REPORT 10 - -/** Report a list of the top states for failed OR connections at log level - * severity, in log domain domain. */ -void -connection_or_report_broken_states(int severity, int domain) -{ - int total = 0; - smartlist_t *items; - - if (!broken_connection_counts || disable_broken_connection_counts) - return; - - items = smartlist_new(); - STRMAP_FOREACH(broken_connection_counts, state, void *, countptr) { - broken_state_count_t *c = tor_malloc(sizeof(broken_state_count_t)); - c->count = (intptr_t)countptr; - total += (int)c->count; - c->state = state; - smartlist_add(items, c); - } STRMAP_FOREACH_END; - - smartlist_sort(items, broken_state_count_compare); - - tor_log(severity, domain, "%d connections have failed%s", total, - smartlist_len(items) > MAX_REASONS_TO_REPORT ? ". Top reasons:" : ":"); - - SMARTLIST_FOREACH_BEGIN(items, const broken_state_count_t *, c) { - if (c_sl_idx > MAX_REASONS_TO_REPORT) - break; - tor_log(severity, domain, - " %d connections died in state %s", (int)c->count, c->state); - } SMARTLIST_FOREACH_END(c); - - SMARTLIST_FOREACH(items, broken_state_count_t *, c, tor_free(c)); - smartlist_free(items); -} - -/** Call this to change or_connection_t states, so the owning channel_tls_t can - * be notified. - */ - -static void -connection_or_change_state(or_connection_t *conn, uint8_t state) -{ - uint8_t old_state; - - tor_assert(conn); - - old_state = conn->base_.state; - conn->base_.state = state; - - if (conn->chan) - channel_tls_handle_state_change_on_orconn(conn->chan, conn, - old_state, state); -} - -/** Return the number of circuits using an or_connection_t; this used to - * be an or_connection_t field, but it got moved to channel_t and we - * shouldn't maintain two copies. */ - -int -connection_or_get_num_circuits(or_connection_t *conn) -{ - tor_assert(conn); - - if (conn->chan) { - return channel_num_circuits(TLS_CHAN_TO_BASE(conn->chan)); - } else return 0; -} - -/**************************************************************/ - -/** Pack the cell_t host-order structure src into network-order - * in the buffer dest. See tor-spec.txt for details about the - * wire format. - * - * Note that this function doesn't touch dst-\>next: the caller - * should set it or clear it as appropriate. - */ -void -cell_pack(packed_cell_t *dst, const cell_t *src, int wide_circ_ids) -{ - char *dest = dst->body; - if (wide_circ_ids) { - set_uint32(dest, htonl(src->circ_id)); - dest += 4; - } else { - set_uint16(dest, htons(src->circ_id)); - dest += 2; - memset(dest+CELL_MAX_NETWORK_SIZE-2, 0, 2); /*make sure it's clear */ - } - set_uint8(dest, src->command); - memcpy(dest+1, src->payload, CELL_PAYLOAD_SIZE); -} - -/** Unpack the network-order buffer src into a host-order - * cell_t structure dest. - */ -static void -cell_unpack(cell_t *dest, const char *src, int wide_circ_ids) -{ - if (wide_circ_ids) { - dest->circ_id = ntohl(get_uint32(src)); - src += 4; - } else { - dest->circ_id = ntohs(get_uint16(src)); - src += 2; - } - dest->command = get_uint8(src); - memcpy(dest->payload, src+1, CELL_PAYLOAD_SIZE); -} - -/** Write the header of cell into the first VAR_CELL_MAX_HEADER_SIZE - * bytes of hdr_out. Returns number of bytes used. */ -int -var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids) -{ - int r; - if (wide_circ_ids) { - set_uint32(hdr_out, htonl(cell->circ_id)); - hdr_out += 4; - r = VAR_CELL_MAX_HEADER_SIZE; - } else { - set_uint16(hdr_out, htons(cell->circ_id)); - hdr_out += 2; - r = VAR_CELL_MAX_HEADER_SIZE - 2; - } - set_uint8(hdr_out, cell->command); - set_uint16(hdr_out+1, htons(cell->payload_len)); - return r; -} - -/** Allocate and return a new var_cell_t with payload_len bytes of - * payload space. */ -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_zero(size); - cell->payload_len = payload_len; - cell->command = 0; - cell->circ_id = 0; - return cell; -} - -/** Release all space held by cell. */ -void -var_cell_free(var_cell_t *cell) -{ - tor_free(cell); -} - -/** We've received an EOF from conn. Mark it for close and return. */ -int -connection_or_reached_eof(or_connection_t *conn) -{ - tor_assert(conn); - - log_info(LD_OR,"OR connection reached EOF. Closing."); - connection_or_close_normally(conn, 1); - - return 0; -} - -/** Handle any new bytes that have come in on connection conn. - * If conn is in 'open' state, hand it to - * connection_or_process_cells_from_inbuf() - * (else do nothing). - */ -int -connection_or_process_inbuf(or_connection_t *conn) -{ - /** Don't let the inbuf of a nonopen OR connection grow beyond this many - * bytes: it's either a broken client, a non-Tor client, or a DOS - * attempt. */ -#define MAX_OR_INBUF_WHEN_NONOPEN 0 - - int ret = 0; - tor_assert(conn); - - switch (conn->base_.state) { - case OR_CONN_STATE_PROXY_HANDSHAKING: - ret = connection_read_proxy_handshake(TO_CONN(conn)); - - /* start TLS after handshake completion, or deal with error */ - if (ret == 1) { - tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED); - if (connection_tls_start_handshake(conn, 0) < 0) - ret = -1; - /* Touch the channel's active timestamp if there is one */ - if (conn->chan) - channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); - } - if (ret < 0) { - connection_or_close_for_error(conn, 0); - } - - return ret; - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: -#ifdef USE_BUFFEREVENTS - if (tor_tls_server_got_renegotiate(conn->tls)) - connection_or_tls_renegotiated_cb(conn->tls, conn); - if (conn->base_.marked_for_close) - return 0; - /* fall through. */ -#endif - case OR_CONN_STATE_OPEN: - case OR_CONN_STATE_OR_HANDSHAKING_V2: - case OR_CONN_STATE_OR_HANDSHAKING_V3: - return connection_or_process_cells_from_inbuf(conn); - default: - break; /* don't do anything */ - } - - /* This check was necessary with 0.2.2, when the TLS_SERVER_RENEGOTIATING - * check would otherwise just let data accumulate. It serves no purpose - * in 0.2.3. - * - * XXX024 Remove this check once we verify that the above paragraph is - * 100% true. */ - if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) { - log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) " - "on nonopen OR connection %s %s:%u in state %s; closing.", - (int)buf_datalen(conn->base_.inbuf), - connection_or_nonopen_was_started_here(conn) ? "to" : "from", - conn->base_.address, conn->base_.port, - conn_state_to_string(conn->base_.type, conn->base_.state)); - connection_or_close_for_error(conn, 0); - ret = -1; - } - - return ret; -} - -/** When adding cells to an OR connection's outbuf, keep adding until the - * outbuf is at least this long, or we run out of cells. */ -#define OR_CONN_HIGHWATER (32*1024) - -/** Add cells to an OR connection's outbuf whenever the outbuf's data length - * drops below this size. */ -#define OR_CONN_LOWWATER (16*1024) - -/** Called whenever we have flushed some data on an or_conn: add more data - * from active circuits. */ -int -connection_or_flushed_some(or_connection_t *conn) -{ - size_t datalen, temp; - ssize_t n, flushed; - size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); - - /* If we're under the low water mark, add cells until we're just over the - * high water mark. */ - datalen = connection_get_outbuf_len(TO_CONN(conn)); - if (datalen < OR_CONN_LOWWATER) { - while ((conn->chan) && channel_tls_more_to_flush(conn->chan)) { - /* Compute how many more cells we want at most */ - n = CEIL_DIV(OR_CONN_HIGHWATER - datalen, cell_network_size); - /* Bail out if we don't want any more */ - if (n <= 0) break; - /* We're still here; try to flush some more cells */ - flushed = channel_tls_flush_some_cells(conn->chan, n); - /* Bail out if it says it didn't flush anything */ - if (flushed <= 0) break; - /* How much in the outbuf now? */ - temp = connection_get_outbuf_len(TO_CONN(conn)); - /* Bail out if we didn't actually increase the outbuf size */ - if (temp <= datalen) break; - /* Update datalen for the next iteration */ - datalen = temp; - } - } - - return 0; -} - -/** Connection conn has finished writing and has no bytes left on - * its outbuf. - * - * Otherwise it's in state "open": stop writing and return. - * - * If conn is broken, mark it for close and return -1, else - * return 0. - */ -int -connection_or_finished_flushing(or_connection_t *conn) -{ - tor_assert(conn); - assert_connection_ok(TO_CONN(conn),0); - - switch (conn->base_.state) { - case OR_CONN_STATE_PROXY_HANDSHAKING: - case OR_CONN_STATE_OPEN: - case OR_CONN_STATE_OR_HANDSHAKING_V2: - case OR_CONN_STATE_OR_HANDSHAKING_V3: - break; - default: - log_err(LD_BUG,"Called in unexpected state %d.", conn->base_.state); - tor_fragile_assert(); - return -1; - } - return 0; -} - -/** Connected handler for OR connections: begin the TLS handshake. - */ -int -connection_or_finished_connecting(or_connection_t *or_conn) -{ - const int proxy_type = or_conn->proxy_type; - connection_t *conn; - - tor_assert(or_conn); - conn = TO_CONN(or_conn); - tor_assert(conn->state == OR_CONN_STATE_CONNECTING); - - log_debug(LD_HANDSHAKE,"OR connect() to router at %s:%u finished.", - conn->address,conn->port); - control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0); - - if (proxy_type != PROXY_NONE) { - /* start proxy handshake */ - if (connection_proxy_connect(conn, proxy_type) < 0) { - connection_or_close_for_error(or_conn, 0); - return -1; - } - - connection_start_reading(conn); - connection_or_change_state(or_conn, OR_CONN_STATE_PROXY_HANDSHAKING); - return 0; - } - - if (connection_tls_start_handshake(or_conn, 0) < 0) { - /* TLS handshaking error of some kind. */ - connection_or_close_for_error(or_conn, 0); - return -1; - } - return 0; -} - -/** Called when we're about to finally unlink and free an OR connection: - * perform necessary accounting and cleanup */ -void -connection_or_about_to_close(or_connection_t *or_conn) -{ - time_t now = time(NULL); - connection_t *conn = TO_CONN(or_conn); - - /* Tell the controlling channel we're closed */ - if (or_conn->chan) { - channel_closed(TLS_CHAN_TO_BASE(or_conn->chan)); - /* - * NULL this out because the channel might hang around a little - * longer before channel_run_cleanup() gets it. - */ - or_conn->chan->conn = NULL; - or_conn->chan = NULL; - } - - /* Remember why we're closing this connection. */ - if (conn->state != OR_CONN_STATE_OPEN) { - /* now mark things down as needed */ - if (connection_or_nonopen_was_started_here(or_conn)) { - const or_options_t *options = get_options(); - connection_or_note_state_when_broken(or_conn); - rep_hist_note_connect_failed(or_conn->identity_digest, now); - entry_guard_register_connect_status(or_conn->identity_digest,0, - !options->HTTPSProxy, now); - if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) { - int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); - control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, - reason); - if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem( - orconn_end_reason_to_control_string(reason), reason); - } - } - } else if (conn->hold_open_until_flushed) { - /* We only set hold_open_until_flushed when we're intentionally - * closing a connection. */ - rep_hist_note_disconnect(or_conn->identity_digest, now); - control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, - tls_error_to_orconn_end_reason(or_conn->tls_error)); - } else if (!tor_digest_is_zero(or_conn->identity_digest)) { - rep_hist_note_connection_died(or_conn->identity_digest, now); - control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, - tls_error_to_orconn_end_reason(or_conn->tls_error)); - } -} - -/** Return 1 if identity digest id_digest is known to be a - * currently or recently running relay. Otherwise return 0. */ -int -connection_or_digest_is_known_relay(const char *id_digest) -{ - if (router_get_consensus_status_by_id(id_digest)) - return 1; /* It's in the consensus: "yes" */ - if (router_get_by_id_digest(id_digest)) - return 1; /* Not in the consensus, but we have a descriptor for - * it. Probably it was in a recent consensus. "Yes". */ - return 0; -} - -/** Set the per-conn read and write limits for conn. If it's a known - * relay, we will rely on the global read and write buckets, so give it - * per-conn limits that are big enough they'll never matter. But if it's - * not a known relay, first check if we set PerConnBwRate/Burst, then - * check if the consensus sets them, else default to 'big enough'. - * - * If reset is true, set the bucket to be full. Otherwise, just - * clip the bucket if it happens to be too full. - */ -static void -connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, - const or_options_t *options) -{ - int rate, burst; /* per-connection rate limiting params */ - if (connection_or_digest_is_known_relay(conn->identity_digest)) { - /* It's in the consensus, or we have a descriptor for it meaning it - * was probably in a recent consensus. It's a recognized relay: - * give it full bandwidth. */ - rate = (int)options->BandwidthRate; - burst = (int)options->BandwidthBurst; - } else { - /* Not a recognized relay. Squeeze it down based on the suggested - * bandwidth parameters in the consensus, but allow local config - * options to override. */ - rate = options->PerConnBWRate ? (int)options->PerConnBWRate : - networkstatus_get_param(NULL, "perconnbwrate", - (int)options->BandwidthRate, 1, INT32_MAX); - burst = options->PerConnBWBurst ? (int)options->PerConnBWBurst : - networkstatus_get_param(NULL, "perconnbwburst", - (int)options->BandwidthBurst, 1, INT32_MAX); - } - - conn->bandwidthrate = rate; - conn->bandwidthburst = burst; -#ifdef USE_BUFFEREVENTS - { - const struct timeval *tick = tor_libevent_get_one_tick_timeout(); - struct ev_token_bucket_cfg *cfg, *old_cfg; - int64_t rate64 = (((int64_t)rate) * options->TokenBucketRefillInterval) - / 1000; - /* This can't overflow, since TokenBucketRefillInterval <= 1000, - * and rate started out less than INT_MAX. */ - int rate_per_tick = (int) rate64; - - cfg = ev_token_bucket_cfg_new(rate_per_tick, burst, rate_per_tick, - burst, tick); - old_cfg = conn->bucket_cfg; - if (conn->base_.bufev) - tor_set_bufferevent_rate_limit(conn->base_.bufev, cfg); - if (old_cfg) - ev_token_bucket_cfg_free(old_cfg); - conn->bucket_cfg = cfg; - (void) reset; /* No way to do this with libevent yet. */ - } -#else - if (reset) { /* set up the token buckets to be full */ - conn->read_bucket = conn->write_bucket = burst; - return; - } - /* If the new token bucket is smaller, take out the extra tokens. - * (If it's larger, don't -- the buckets can grow to reach the cap.) */ - if (conn->read_bucket > burst) - conn->read_bucket = burst; - if (conn->write_bucket > burst) - conn->write_bucket = burst; -#endif -} - -/** Either our set of relays or our per-conn rate limits have changed. - * Go through all the OR connections and update their token buckets to make - * sure they don't exceed their maximum values. */ -void -connection_or_update_token_buckets(smartlist_t *conns, - const or_options_t *options) -{ - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (connection_speaks_cells(conn)) - connection_or_update_token_buckets_helper(TO_OR_CONN(conn), 0, options); - }); -} - -/** If we don't necessarily know the router we're connecting to, but we - * have an addr/port/id_digest, then fill in as much as we can. Start - * by checking to see if this describes a router we know. - * started_here is 1 if we are the initiator of conn and - * 0 if it's an incoming connection. */ -void -connection_or_init_conn_from_address(or_connection_t *conn, - const tor_addr_t *addr, uint16_t port, - const char *id_digest, - int started_here) -{ - const node_t *r = node_get_by_id(id_digest); - connection_or_set_identity_digest(conn, id_digest); - connection_or_update_token_buckets_helper(conn, 1, get_options()); - - conn->base_.port = port; - tor_addr_copy(&conn->base_.addr, addr); - tor_addr_copy(&conn->real_addr, addr); - if (r) { - tor_addr_port_t node_ap; - node_get_pref_orport(r, &node_ap); - /* XXXX proposal 186 is making this more complex. For now, a conn - is canonical when it uses the _preferred_ address. */ - if (tor_addr_eq(&conn->base_.addr, &node_ap.addr)) - conn->is_canonical = 1; - if (!started_here) { - /* Override the addr/port, so our log messages will make sense. - * This is dangerous, since if we ever try looking up a conn by - * its actual addr/port, we won't remember. Careful! */ - /* XXXX arma: this is stupid, and it's the reason we need real_addr - * to track is_canonical properly. What requires it? */ - /* XXXX i believe the reason we did this, originally, is because - * we wanted to log what OR a connection was to, and if we logged the - * right IP address and port 56244, that wouldn't be as helpful. now we - * log the "right" port too, so we know if it's moria1 or moria2. - */ - tor_addr_copy(&conn->base_.addr, &node_ap.addr); - conn->base_.port = node_ap.port; - } - conn->nickname = tor_strdup(node_get_nickname(r)); - tor_free(conn->base_.address); - conn->base_.address = tor_dup_addr(&node_ap.addr); - } else { - const char *n; - /* If we're an authoritative directory server, we may know a - * nickname for this router. */ - n = dirserv_get_nickname_by_digest(id_digest); - if (n) { - conn->nickname = tor_strdup(n); - } else { - conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); - conn->nickname[0] = '$'; - base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, - conn->identity_digest, DIGEST_LEN); - } - tor_free(conn->base_.address); - conn->base_.address = tor_dup_addr(addr); - } -} - -/** These just pass all the is_bad_for_new_circs manipulation on to - * channel_t */ - -static unsigned int -connection_or_is_bad_for_new_circs(or_connection_t *or_conn) -{ - tor_assert(or_conn); - - if (or_conn->chan) - return channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); - else return 0; -} - -static void -connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) -{ - tor_assert(or_conn); - - if (or_conn->chan) - channel_mark_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); -} - -/** How old do we let a connection to an OR get before deciding it's - * too old for new circuits? */ -#define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) - -/** Given the head of the linked list for all the or_connections with a given - * identity, set elements of that list as is_bad_for_new_circs as - * appropriate. Helper for connection_or_set_bad_connections(). - * - * Specifically, we set the is_bad_for_new_circs flag on: - * - all connections if force is true. - * - all connections that are too old. - * - all open non-canonical connections for which a canonical connection - * exists to the same router. - * - all open canonical connections for which a 'better' canonical - * connection exists to the same router. - * - all open non-canonical connections for which a 'better' non-canonical - * connection exists to the same router at the same address. - * - * See channel_is_better() in channel.c for our idea of what makes one OR - * connection better than another. - */ -static void -connection_or_group_set_badness(or_connection_t *head, int force) -{ - or_connection_t *or_conn = NULL, *best = NULL; - int n_old = 0, n_inprogress = 0, n_canonical = 0, n_other = 0; - time_t now = time(NULL); - - /* Pass 1: expire everything that's old, and see what the status of - * everything else is. */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) - continue; - if (force || - or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD - < now) { - log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } - - if (connection_or_is_bad_for_new_circs(or_conn)) { - ++n_old; - } else if (or_conn->base_.state != OR_CONN_STATE_OPEN) { - ++n_inprogress; - } else if (or_conn->is_canonical) { - ++n_canonical; - } else { - ++n_other; - } - } - - /* Pass 2: We know how about how good the best connection is. - * expire everything that's worse, and find the very best if we can. */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) - continue; /* This one doesn't need to be marked bad. */ - if (or_conn->base_.state != OR_CONN_STATE_OPEN) - continue; /* Don't mark anything bad until we have seen what happens - * when the connection finishes. */ - if (n_canonical && !or_conn->is_canonical) { - /* We have at least one open canonical connection to this router, - * and this one is open but not canonical. Mark it bad. */ - log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old). It is not " - "canonical, and we have another connection to that OR that is.", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - continue; - } - - if (!best || - channel_is_better(now, - TLS_CHAN_TO_BASE(or_conn->chan), - TLS_CHAN_TO_BASE(best->chan), - 0)) { - best = or_conn; - } - } - - if (!best) - return; - - /* Pass 3: One connection to OR is best. If it's canonical, mark as bad - * every other open connection. If it's non-canonical, mark as bad - * every other open connection to the same address. - * - * XXXX This isn't optimal; if we have connections to an OR at multiple - * addresses, we'd like to pick the best _for each address_, and mark as - * bad every open connection that isn't best for its address. But this - * can only occur in cases where the other OR is old (so we have no - * canonical connection to it), or where all the connections to the OR are - * at noncanonical addresses and we have no good direct connection (which - * means we aren't at risk of attaching circuits to it anyway). As - * 0.1.2.x dies out, the first case will go away, and the second one is - * "mostly harmless", so a fix can wait until somebody is bored. - */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn) || - or_conn->base_.state != OR_CONN_STATE_OPEN) - continue; - if (or_conn != best && - channel_is_better(now, - TLS_CHAN_TO_BASE(best->chan), - TLS_CHAN_TO_BASE(or_conn->chan), 1)) { - /* This isn't the best conn, _and_ the best conn is better than it, - even when we're being forgiving. */ - if (best->is_canonical) { - log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old). " - "We have a better canonical one " - "(fd "TOR_SOCKET_T_FORMAT"; %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created), - best->base_.s, (int)(now - best->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } else if (!tor_addr_compare(&or_conn->real_addr, - &best->real_addr, CMP_EXACT)) { - log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old). We have a better " - "one with the " - "same address (fd "TOR_SOCKET_T_FORMAT"; %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created), - best->base_.s, (int)(now - best->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } - } - } -} - -/** Go through all the OR connections (or if digest is non-NULL, just - * the OR connections with that digest), and set the is_bad_for_new_circs - * flag based on the rules in connection_or_group_set_badness() (or just - * always set it if force is true). - */ -void -connection_or_set_bad_connections(const char *digest, int force) -{ - if (!orconn_identity_map) - return; - - DIGESTMAP_FOREACH(orconn_identity_map, identity, or_connection_t *, conn) { - if (!digest || tor_memeq(digest, conn->identity_digest, DIGEST_LEN)) - connection_or_group_set_badness(conn, force); - } DIGESTMAP_FOREACH_END; -} - -/** conn is in the 'connecting' state, and it failed to complete - * a TCP connection. Send notifications appropriately. - * - * reason specifies the or_conn_end_reason for the failure; - * msg specifies the strerror-style error message. - */ -void -connection_or_connect_failed(or_connection_t *conn, - int reason, const char *msg) -{ - control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason); - if (!authdir_mode_tests_reachability(get_options())) - control_event_bootstrap_problem(msg, reason); -} - -/** conn got an error in connection_handle_read_impl() or - * connection_handle_write_impl() and is going to die soon. - * - * reason specifies the or_conn_end_reason for the failure; - * msg specifies the strerror-style error message. - */ -void -connection_or_notify_error(or_connection_t *conn, - int reason, const char *msg) -{ - channel_t *chan; - - tor_assert(conn); - - /* If we're connecting, call connect_failed() too */ - if (TO_CONN(conn)->state == OR_CONN_STATE_CONNECTING) - connection_or_connect_failed(conn, reason, msg); - - /* Tell the controlling channel if we have one */ - if (conn->chan) { - chan = TLS_CHAN_TO_BASE(conn->chan); - /* Don't transition if we're already in closing, closed or error */ - if (!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - channel_close_for_error(chan); - } - } - - /* No need to mark for error because connection.c is about to do that */ -} - -/** Launch a new OR connection to addr:port and expect to - * handshake with an OR with identity digest id_digest. Optionally, - * pass in a pointer to a channel using this connection. - * - * If id_digest is me, do nothing. If we're already connected to it, - * return that connection. If the connect() is in progress, set the - * new conn's state to 'connecting' and return it. If connect() succeeds, - * call connection_tls_start_handshake() on it. - * - * This function is called from router_retry_connections(), for - * ORs connecting to ORs, and circuit_establish_circuit(), for - * OPs connecting to ORs. - * - * Return the launched conn, or NULL if it failed. - */ -or_connection_t * -connection_or_connect(const tor_addr_t *_addr, uint16_t port, - const char *id_digest, - channel_tls_t *chan) -{ - or_connection_t *conn; - const or_options_t *options = get_options(); - int socket_error = 0; - tor_addr_t addr; - - int r; - tor_addr_t proxy_addr; - uint16_t proxy_port; - int proxy_type; - - tor_assert(_addr); - tor_assert(id_digest); - tor_addr_copy(&addr, _addr); - - if (server_mode(options) && router_digest_is_me(id_digest)) { - log_info(LD_PROTOCOL,"Client asked me to connect to myself. Refusing."); - return NULL; - } - - conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr)); - - /* - * Set up conn so it's got all the data we need to remember for channels - * - * This stuff needs to happen before connection_or_init_conn_from_address() - * so connection_or_set_identity_digest() and such know where to look to - * keep the channel up to date. - */ - conn->chan = chan; - chan->conn = conn; - connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); - connection_or_change_state(conn, OR_CONN_STATE_CONNECTING); - control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0); - - conn->is_outgoing = 1; - - /* If we are using a proxy server, find it and use it. */ - r = get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, TO_CONN(conn)); - if (r == 0) { - conn->proxy_type = proxy_type; - if (proxy_type != PROXY_NONE) { - tor_addr_copy(&addr, &proxy_addr); - port = proxy_port; - conn->base_.proxy_state = PROXY_INFANT; - } - } else { - /* get_proxy_addrport() might fail if we have a Bridge line that - references a transport, but no ClientTransportPlugin lines - defining its transport proxy. If this is the case, let's try to - output a useful log message to the user. */ - const char *transport_name = - find_transport_name_by_bridge_addrport(&TO_CONN(conn)->addr, - TO_CONN(conn)->port); - - if (transport_name) { - log_warn(LD_GENERAL, "We were supposed to connect to bridge '%s' " - "using pluggable transport '%s', but we can't find a pluggable " - "transport proxy supporting '%s'. This can happen if you " - "haven't provided a ClientTransportPlugin line, or if " - "your pluggable transport proxy stopped running.", - fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port), - transport_name, transport_name); - } else { - log_warn(LD_GENERAL, "Tried to connect to '%s' through a proxy, but " - "the proxy address could not be found.", - fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); - } - - connection_free(TO_CONN(conn)); - return NULL; - } - - switch (connection_connect(TO_CONN(conn), conn->base_.address, - &addr, port, &socket_error)) { - case -1: - /* If the connection failed immediately, and we're using - * a proxy, our proxy is down. Don't blame the Tor server. */ - if (conn->base_.proxy_state == PROXY_INFANT) - entry_guard_register_connect_status(conn->identity_digest, - 0, 1, time(NULL)); - connection_or_connect_failed(conn, - errno_to_orconn_end_reason(socket_error), - tor_socket_strerror(socket_error)); - connection_free(TO_CONN(conn)); - return NULL; - case 0: - connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); - /* writable indicates finish, readable indicates broken link, - error indicates broken link on windows */ - return conn; - /* case 1: fall through */ - } - - if (connection_or_finished_connecting(conn) < 0) { - /* already marked for close */ - return NULL; - } - return conn; -} - -/** Mark orconn for close and transition the associated channel, if any, to - * the closing state. - * - * It's safe to call this and connection_or_close_for_error() any time, and - * channel layer will treat it as a connection closing for reasons outside - * its control, like the remote end closing it. It can also be a local - * reason that's specific to connection_t/or_connection_t rather than - * the channel mechanism, such as expiration of old connections in - * run_connection_housekeeping(). If you want to close a channel_t - * from somewhere that logically works in terms of generic channels - * rather than connections, use channel_mark_for_close(); see also - * the comment on that function in channel.c. - */ - -void -connection_or_close_normally(or_connection_t *orconn, int flush) -{ - channel_t *chan = NULL; - - tor_assert(orconn); - if (flush) connection_mark_and_flush_internal(TO_CONN(orconn)); - else connection_mark_for_close_internal(TO_CONN(orconn)); - if (orconn->chan) { - chan = TLS_CHAN_TO_BASE(orconn->chan); - /* Don't transition if we're already in closing, closed or error */ - if (!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - channel_close_from_lower_layer(chan); - } - } -} - -/** Mark orconn for close and transition the associated channel, if any, to - * the error state. - */ - -void -connection_or_close_for_error(or_connection_t *orconn, int flush) -{ - channel_t *chan = NULL; - - tor_assert(orconn); - if (flush) connection_mark_and_flush_internal(TO_CONN(orconn)); - else connection_mark_for_close_internal(TO_CONN(orconn)); - if (orconn->chan) { - chan = TLS_CHAN_TO_BASE(orconn->chan); - /* Don't transition if we're already in closing, closed or error */ - if (!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - channel_close_for_error(chan); - } - } -} - -/** Begin the tls handshake with conn. receiving is 0 if - * we initiated the connection, else it's 1. - * - * Assign a new tls object to conn->tls, begin reading on conn, and - * pass conn to connection_tls_continue_handshake(). - * - * Return -1 if conn is broken, else return 0. - */ -MOCK_IMPL(int, -connection_tls_start_handshake,(or_connection_t *conn, int receiving)) -{ - channel_listener_t *chan_listener; - channel_t *chan; - - /* Incoming connections will need a new channel passed to the - * channel_tls_listener */ - if (receiving) { - /* It shouldn't already be set */ - tor_assert(!(conn->chan)); - chan_listener = channel_tls_get_listener(); - if (!chan_listener) { - chan_listener = channel_tls_start_listener(); - command_setup_listener(chan_listener); - } - chan = channel_tls_handle_incoming(conn); - channel_listener_queue_incoming(chan_listener, chan); - } - - connection_or_change_state(conn, OR_CONN_STATE_TLS_HANDSHAKING); - tor_assert(!conn->tls); - conn->tls = tor_tls_new(conn->base_.s, receiving); - if (!conn->tls) { - log_warn(LD_BUG,"tor_tls_new failed. Closing."); - return -1; - } - tor_tls_set_logged_address(conn->tls, // XXX client and relay? - escaped_safe_str(conn->base_.address)); - -#ifdef USE_BUFFEREVENTS - if (connection_type_uses_bufferevent(TO_CONN(conn))) { - const int filtering = get_options()->UseFilteringSSLBufferevents; - struct bufferevent *b = - tor_tls_init_bufferevent(conn->tls, conn->base_.bufev, conn->base_.s, - receiving, filtering); - if (!b) { - log_warn(LD_BUG,"tor_tls_init_bufferevent failed. Closing."); - return -1; - } - conn->base_.bufev = b; - if (conn->bucket_cfg) - tor_set_bufferevent_rate_limit(conn->base_.bufev, conn->bucket_cfg); - connection_enable_rate_limiting(TO_CONN(conn)); - - connection_configure_bufferevent_callbacks(TO_CONN(conn)); - bufferevent_setcb(b, - connection_handle_read_cb, - connection_handle_write_cb, - connection_or_handle_event_cb,/* overriding this one*/ - TO_CONN(conn)); - } -#endif - connection_start_reading(TO_CONN(conn)); - log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT, - conn->base_.s); - note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C); - - IF_HAS_BUFFEREVENT(TO_CONN(conn), { - /* ???? */; - }) ELSE_IF_NO_BUFFEREVENT { - if (connection_tls_continue_handshake(conn) < 0) - return -1; - } - return 0; -} - -/** Block all future attempts to renegotiate on 'conn' */ -void -connection_or_block_renegotiation(or_connection_t *conn) -{ - tor_tls_t *tls = conn->tls; - if (!tls) - return; - tor_tls_set_renegotiate_callback(tls, NULL, NULL); - tor_tls_block_renegotiation(tls); -} - -/** Invoked on the server side from inside tor_tls_read() when the server - * gets a successful TLS renegotiation from the client. */ -static void -connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) -{ - or_connection_t *conn = _conn; - (void)tls; - - /* Don't invoke this again. */ - connection_or_block_renegotiation(conn); - - if (connection_tls_finish_handshake(conn) < 0) { - /* XXXX_TLS double-check that it's ok to do this from inside read. */ - /* XXXX_TLS double-check that this verifies certificates. */ - connection_or_close_for_error(conn, 0); - } -} - -/** Move forward with the tls handshake. If it finishes, hand - * conn to connection_tls_finish_handshake(). - * - * Return -1 if conn is broken, else return 0. - */ -int -connection_tls_continue_handshake(or_connection_t *conn) -{ - int result; - check_no_tls_errors(); - again: - if (conn->base_.state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { - // log_notice(LD_OR, "Renegotiate with %p", conn->tls); - result = tor_tls_renegotiate(conn->tls); - // log_notice(LD_OR, "Result: %d", result); - } else { - tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); - // log_notice(LD_OR, "Continue handshake with %p", conn->tls); - result = tor_tls_handshake(conn->tls); - // log_notice(LD_OR, "Result: %d", result); - } - switch (result) { - CASE_TOR_TLS_ERROR_ANY: - log_info(LD_OR,"tls error [%s]. breaking connection.", - tor_tls_err_to_string(result)); - return -1; - case TOR_TLS_DONE: - if (! tor_tls_used_v1_handshake(conn->tls)) { - if (!tor_tls_is_server(conn->tls)) { - if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) { - if (tor_tls_received_v3_certificate(conn->tls)) { - log_info(LD_OR, "Client got a v3 cert! Moving on to v3 " - "handshake with ciphersuite %s", - tor_tls_get_ciphersuite_name(conn->tls)); - return connection_or_launch_v3_or_handshake(conn); - } else { - log_debug(LD_OR, "Done with initial SSL handshake (client-side)." - " Requesting renegotiation."); - connection_or_change_state(conn, - OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); - goto again; - } - } - // log_notice(LD_OR,"Done. state was %d.", conn->base_.state); - } else { - /* v2/v3 handshake, but not a client. */ - log_debug(LD_OR, "Done with initial SSL handshake (server-side). " - "Expecting renegotiation or VERSIONS cell"); - tor_tls_set_renegotiate_callback(conn->tls, - connection_or_tls_renegotiated_cb, - conn); - connection_or_change_state(conn, - OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); - connection_stop_writing(TO_CONN(conn)); - connection_start_reading(TO_CONN(conn)); - return 0; - } - } - return connection_tls_finish_handshake(conn); - case TOR_TLS_WANTWRITE: - connection_start_writing(TO_CONN(conn)); - log_debug(LD_OR,"wanted write"); - return 0; - case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */ - log_debug(LD_OR,"wanted read"); - return 0; - case TOR_TLS_CLOSE: - log_info(LD_OR,"tls closed. breaking connection."); - return -1; - } - return 0; -} - -#ifdef USE_BUFFEREVENTS -static void -connection_or_handle_event_cb(struct bufferevent *bufev, short event, - void *arg) -{ - struct or_connection_t *conn = TO_OR_CONN(arg); - - /* XXXX cut-and-paste code; should become a function. */ - if (event & BEV_EVENT_CONNECTED) { - if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) { - if (tor_tls_finish_handshake(conn->tls) < 0) { - log_warn(LD_OR, "Problem finishing handshake"); - connection_or_close_for_error(conn, 0); - return; - } - } - - if (! tor_tls_used_v1_handshake(conn->tls)) { - if (!tor_tls_is_server(conn->tls)) { - if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) { - if (tor_tls_received_v3_certificate(conn->tls)) { - log_info(LD_OR, "Client got a v3 cert!"); - if (connection_or_launch_v3_or_handshake(conn) < 0) - connection_or_close_for_error(conn, 0); - return; - } else { - connection_or_change_state(conn, - OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); - tor_tls_unblock_renegotiation(conn->tls); - if (bufferevent_ssl_renegotiate(conn->base_.bufev)<0) { - log_warn(LD_OR, "Start_renegotiating went badly."); - connection_or_close_for_error(conn, 0); - } - tor_tls_unblock_renegotiation(conn->tls); - return; /* ???? */ - } - } - } else { - const int handshakes = tor_tls_get_num_server_handshakes(conn->tls); - - if (handshakes == 1) { - /* v2 or v3 handshake, as a server. Only got one handshake, so - * wait for the next one. */ - tor_tls_set_renegotiate_callback(conn->tls, - connection_or_tls_renegotiated_cb, - conn); - connection_or_change_state(conn, - OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); - } else if (handshakes == 2) { - /* v2 handshake, as a server. Two handshakes happened already, - * so we treat renegotiation as done. - */ - connection_or_tls_renegotiated_cb(conn->tls, conn); - } else if (handshakes > 2) { - log_warn(LD_OR, "More than two handshakes done on connection. " - "Closing."); - connection_or_close_for_error(conn, 0); - } else { - log_warn(LD_BUG, "We were unexpectedly told that a connection " - "got %d handshakes. Closing.", handshakes); - connection_or_close_for_error(conn, 0); - } - return; - } - } - connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); - if (connection_tls_finish_handshake(conn) < 0) - connection_or_close_for_error(conn, 0); /* ???? */ - return; - } - - if (event & BEV_EVENT_ERROR) { - unsigned long err; - while ((err = bufferevent_get_openssl_error(bufev))) { - tor_tls_log_one_error(conn->tls, err, LOG_WARN, LD_OR, - "handshaking (with bufferevent)"); - } - } - - connection_handle_event_cb(bufev, event, arg); -} -#endif - -/** Return 1 if we initiated this connection, or 0 if it started - * out as an incoming connection. - */ -int -connection_or_nonopen_was_started_here(or_connection_t *conn) -{ - tor_assert(conn->base_.type == CONN_TYPE_OR || - conn->base_.type == CONN_TYPE_EXT_OR); - if (!conn->tls) - return 1; /* it's still in proxy states or something */ - if (conn->handshake_state) - return conn->handshake_state->started_here; - return !tor_tls_is_server(conn->tls); -} - -/** Conn just completed its handshake. Return 0 if all is well, and - * return -1 if he is lying, broken, or otherwise something is wrong. - * - * If we initiated this connection (started_here is true), make sure - * the other side sent a correctly formed certificate. If I initiated the - * connection, make sure it's the right guy. - * - * Otherwise (if we _didn't_ initiate this connection), it's okay for - * the certificate to be weird or absent. - * - * If we return 0, and the certificate is as expected, write a hash of the - * identity key into digest_rcvd_out, which must have DIGEST_LEN - * space in it. - * If the certificate is invalid or missing on an incoming connection, - * we return 0 and set digest_rcvd_out to DIGEST_LEN NUL bytes. - * (If we return -1, the contents of this buffer are undefined.) - * - * As side effects, - * 1) Set conn->circ_id_type according to tor-spec.txt. - * 2) If we're an authdirserver and we initiated the connection: drop all - * descriptors that claim to be on that IP/port but that aren't - * this guy; and note that this guy is reachable. - * 3) If this is a bridge and we didn't configure its identity - * fingerprint, remember the keyid we just learned. - */ -static int -connection_or_check_valid_tls_handshake(or_connection_t *conn, - int started_here, - char *digest_rcvd_out) -{ - crypto_pk_t *identity_rcvd=NULL; - const or_options_t *options = get_options(); - int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; - const char *safe_address = - started_here ? conn->base_.address : - safe_str_client(conn->base_.address); - const char *conn_type = started_here ? "outgoing" : "incoming"; - int has_cert = 0; - - check_no_tls_errors(); - has_cert = tor_tls_peer_has_cert(conn->tls); - if (started_here && !has_cert) { - log_info(LD_HANDSHAKE,"Tried connecting to router at %s:%d, but it didn't " - "send a cert! Closing.", - safe_address, conn->base_.port); - return -1; - } else if (!has_cert) { - log_debug(LD_HANDSHAKE,"Got incoming connection with no certificate. " - "That's ok."); - } - check_no_tls_errors(); - - if (has_cert) { - int v = tor_tls_verify(started_here?severity:LOG_INFO, - conn->tls, &identity_rcvd); - if (started_here && v<0) { - log_fn(severity,LD_HANDSHAKE,"Tried connecting to router at %s:%d: It" - " has a cert but it's invalid. Closing.", - safe_address, conn->base_.port); - return -1; - } else if (v<0) { - log_info(LD_HANDSHAKE,"Incoming connection gave us an invalid cert " - "chain; ignoring."); - } else { - log_debug(LD_HANDSHAKE, - "The certificate seems to be valid on %s connection " - "with %s:%d", conn_type, safe_address, conn->base_.port); - } - check_no_tls_errors(); - } - - if (identity_rcvd) { - crypto_pk_get_digest(identity_rcvd, digest_rcvd_out); - } else { - memset(digest_rcvd_out, 0, DIGEST_LEN); - } - - tor_assert(conn->chan); - channel_set_circid_type(TLS_CHAN_TO_BASE(conn->chan), identity_rcvd, 1); - - crypto_pk_free(identity_rcvd); - - if (started_here) - return connection_or_client_learned_peer_id(conn, - (const uint8_t*)digest_rcvd_out); - - return 0; -} - -/** Called when we (as a connection initiator) have definitively, - * authenticatedly, learned that ID of the Tor instance on the other - * side of conn is peer_id. For v1 and v2 handshakes, - * this is right after we get a certificate chain in a TLS handshake - * or renegotiation. For v3 handshakes, this is right after we get a - * certificate chain in a CERTS cell. - * - * If we want any particular ID before, record the one we got. - * - * If we wanted an ID, but we didn't get it, log a warning and return -1. - * - * If we're testing reachability, remember what we learned. - * - * Return 0 on success, -1 on failure. - */ -int -connection_or_client_learned_peer_id(or_connection_t *conn, - const uint8_t *peer_id) -{ - const or_options_t *options = get_options(); - int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; - - if (tor_digest_is_zero(conn->identity_digest)) { - connection_or_set_identity_digest(conn, (const char*)peer_id); - tor_free(conn->nickname); - conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); - conn->nickname[0] = '$'; - base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, - conn->identity_digest, DIGEST_LEN); - log_info(LD_HANDSHAKE, "Connected to router %s at %s:%d without knowing " - "its key. Hoping for the best.", - conn->nickname, conn->base_.address, conn->base_.port); - /* if it's a bridge and we didn't know its identity fingerprint, now - * we do -- remember it for future attempts. */ - learned_router_identity(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); - } - - if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) { - /* I was aiming for a particular digest. I didn't get it! */ - char seen[HEX_DIGEST_LEN+1]; - char expected[HEX_DIGEST_LEN+1]; - base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN); - base16_encode(expected, sizeof(expected), conn->identity_digest, - DIGEST_LEN); - log_fn(severity, LD_HANDSHAKE, - "Tried connecting to router at %s:%d, but identity key was not " - "as expected: wanted %s but got %s.", - conn->base_.address, conn->base_.port, expected, seen); - entry_guard_register_connect_status(conn->identity_digest, 0, 1, - time(NULL)); - control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, - END_OR_CONN_REASON_OR_IDENTITY); - if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem( - "Unexpected identity in router certificate", - END_OR_CONN_REASON_OR_IDENTITY); - return -1; - } - if (authdir_mode_tests_reachability(options)) { - dirserv_orconn_tls_done(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); - } - - return 0; -} - -/** Return when a client used this, for connection.c, since client_used - * is now one of the timestamps of channel_t */ - -time_t -connection_or_client_used(or_connection_t *conn) -{ - tor_assert(conn); - - if (conn->chan) { - return channel_when_last_client(TLS_CHAN_TO_BASE(conn->chan)); - } else return 0; -} - -/** The v1/v2 TLS handshake is finished. - * - * Make sure we are happy with the person we just handshaked with. - * - * If he initiated the connection, make sure he's not already connected, - * then initialize conn from the information in router. - * - * If all is successful, call circuit_n_conn_done() to handle events - * that have been pending on the base_.address), - tor_tls_get_ciphersuite_name(conn->tls)); - - directory_set_dirty(); - - if (connection_or_check_valid_tls_handshake(conn, started_here, - digest_rcvd) < 0) - return -1; - - circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - - if (tor_tls_used_v1_handshake(conn->tls)) { - conn->link_proto = 1; - if (!started_here) { - connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); - } - tor_tls_block_renegotiation(conn->tls); - return connection_or_set_state_open(conn); - } else { - connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2); - if (connection_init_or_handshake_state(conn, started_here) < 0) - return -1; - if (!started_here) { - connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); - } - return connection_or_send_versions(conn, 0); - } -} - -/** - * Called as client when initial TLS handshake is done, and we notice - * that we got a v3-handshake signalling certificate from the server. - * Set up structures, do bookkeeping, and send the versions cell. - * Return 0 on success and -1 on failure. - */ -static int -connection_or_launch_v3_or_handshake(or_connection_t *conn) -{ - tor_assert(connection_or_nonopen_was_started_here(conn)); - tor_assert(tor_tls_received_v3_certificate(conn->tls)); - - circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - - connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V3); - if (connection_init_or_handshake_state(conn, 1) < 0) - return -1; - - return connection_or_send_versions(conn, 1); -} - -/** Allocate a new connection handshake state for the connection - * conn. Return 0 on success, -1 on failure. */ -int -connection_init_or_handshake_state(or_connection_t *conn, int started_here) -{ - or_handshake_state_t *s; - if (conn->handshake_state) { - log_warn(LD_BUG, "Duplicate call to connection_init_or_handshake_state!"); - return 0; - } - s = conn->handshake_state = tor_malloc_zero(sizeof(or_handshake_state_t)); - s->started_here = started_here ? 1 : 0; - s->digest_sent_data = 1; - s->digest_received_data = 1; - return 0; -} - -/** Free all storage held by state. */ -void -or_handshake_state_free(or_handshake_state_t *state) -{ - if (!state) - return; - crypto_digest_free(state->digest_sent); - crypto_digest_free(state->digest_received); - tor_cert_free(state->auth_cert); - tor_cert_free(state->id_cert); - memwipe(state, 0xBE, sizeof(or_handshake_state_t)); - tor_free(state); -} - -/** - * Remember that cell has been transmitted (if incoming is - * false) or received (if incoming is true) during a V3 handshake using - * state. - * - * (We don't record the cell, but we keep a digest of everything sent or - * received during the v3 handshake, and the client signs it in an - * authenticate cell.) - */ -void -or_handshake_state_record_cell(or_connection_t *conn, - or_handshake_state_t *state, - const cell_t *cell, - int incoming) -{ - size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); - crypto_digest_t *d, **dptr; - packed_cell_t packed; - if (incoming) { - if (!state->digest_received_data) - return; - } else { - if (!state->digest_sent_data) - return; - } - if (!incoming) { - log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells " - "while making a handshake digest. But we think we are sending " - "one with type %d.", (int)cell->command); - } - dptr = incoming ? &state->digest_received : &state->digest_sent; - if (! *dptr) - *dptr = crypto_digest256_new(DIGEST_SHA256); - - d = *dptr; - /* Re-packing like this is a little inefficient, but we don't have to do - this very often at all. */ - cell_pack(&packed, cell, conn->wide_circ_ids); - crypto_digest_add_bytes(d, packed.body, cell_network_size); - memwipe(&packed, 0, sizeof(packed)); -} - -/** Remember that a variable-length cell has been transmitted (if - * incoming is false) or received (if incoming is true) during a - * V3 handshake using state. - * - * (We don't record the cell, but we keep a digest of everything sent or - * received during the v3 handshake, and the client signs it in an - * authenticate cell.) - */ -void -or_handshake_state_record_var_cell(or_connection_t *conn, - or_handshake_state_t *state, - const var_cell_t *cell, - int incoming) -{ - crypto_digest_t *d, **dptr; - int n; - char buf[VAR_CELL_MAX_HEADER_SIZE]; - if (incoming) { - if (!state->digest_received_data) - return; - } else { - if (!state->digest_sent_data) - return; - } - dptr = incoming ? &state->digest_received : &state->digest_sent; - if (! *dptr) - *dptr = crypto_digest256_new(DIGEST_SHA256); - - d = *dptr; - - n = var_cell_pack_header(cell, buf, conn->wide_circ_ids); - crypto_digest_add_bytes(d, buf, n); - crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len); - - memwipe(buf, 0, sizeof(buf)); -} - -/** Set conn's state to OR_CONN_STATE_OPEN, and tell other subsystems - * as appropriate. Called when we are done with all TLS and OR handshaking. - */ -int -connection_or_set_state_open(or_connection_t *conn) -{ - connection_or_change_state(conn, OR_CONN_STATE_OPEN); - control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0); - - or_handshake_state_free(conn->handshake_state); - conn->handshake_state = NULL; - IF_HAS_BUFFEREVENT(TO_CONN(conn), { - connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); - }) ELSE_IF_NO_BUFFEREVENT { - connection_start_reading(TO_CONN(conn)); - } - - return 0; -} - -/** Pack cell into wire-format, and write it onto conn's outbuf. - * For cells that use or affect a circuit, this should only be called by - * connection_or_flush_from_first_active_circuit(). - */ -void -connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) -{ - packed_cell_t networkcell; - size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); - - tor_assert(cell); - tor_assert(conn); - - cell_pack(&networkcell, cell, conn->wide_circ_ids); - - connection_write_to_buf(networkcell.body, cell_network_size, TO_CONN(conn)); - - /* Touch the channel's active timestamp if there is one */ - if (conn->chan) - channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); - - if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) - or_handshake_state_record_cell(conn, conn->handshake_state, cell, 0); - - if (cell->command != CELL_PADDING) - conn->timestamp_last_added_nonpadding = approx_time(); -} - -/** Pack a variable-length cell into wire-format, and write it onto - * conn's outbuf. Right now, this DOES NOT support cells that - * affect a circuit. - */ -void -connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn) -{ - int n; - char hdr[VAR_CELL_MAX_HEADER_SIZE]; - tor_assert(cell); - tor_assert(conn); - n = var_cell_pack_header(cell, hdr, conn->wide_circ_ids); - connection_write_to_buf(hdr, n, TO_CONN(conn)); - connection_write_to_buf((char*)cell->payload, - cell->payload_len, TO_CONN(conn)); - if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) - or_handshake_state_record_var_cell(conn, conn->handshake_state, cell, 0); - if (cell->command != CELL_PADDING) - conn->timestamp_last_added_nonpadding = approx_time(); - - /* Touch the channel's active timestamp if there is one */ - if (conn->chan) - channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); -} - -/** See whether there's a variable-length cell waiting on or_conn's - * inbuf. Return values as for fetch_var_cell_from_buf(). */ -static int -connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out) -{ - connection_t *conn = TO_CONN(or_conn); - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return fetch_var_cell_from_evbuffer(input, out, or_conn->link_proto); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto); - } -} - -/** Process cells from conn's inbuf. - * - * Loop: while inbuf contains a cell, pull it off the inbuf, unpack it, - * and hand it to command_process_cell(). - * - * Always return 0. - */ -static int -connection_or_process_cells_from_inbuf(or_connection_t *conn) -{ - var_cell_t *var_cell; - - while (1) { - log_debug(LD_OR, - TOR_SOCKET_T_FORMAT": starting, inbuf_datalen %d " - "(%d pending in tls object).", - conn->base_.s,(int)connection_get_inbuf_len(TO_CONN(conn)), - tor_tls_get_pending_bytes(conn->tls)); - if (connection_fetch_var_cell_from_buf(conn, &var_cell)) { - if (!var_cell) - return 0; /* not yet. */ - - /* Touch the channel's active timestamp if there is one */ - if (conn->chan) - channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); - - circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - channel_tls_handle_var_cell(var_cell, conn); - var_cell_free(var_cell); - } else { - const int wide_circ_ids = conn->wide_circ_ids; - size_t cell_network_size = get_cell_network_size(conn->wide_circ_ids); - char buf[CELL_MAX_NETWORK_SIZE]; - cell_t cell; - if (connection_get_inbuf_len(TO_CONN(conn)) - < cell_network_size) /* whole response available? */ - return 0; /* not yet */ - - /* Touch the channel's active timestamp if there is one */ - if (conn->chan) - channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); - - circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - connection_fetch_from_buf(buf, cell_network_size, TO_CONN(conn)); - - /* retrieve cell info from buf (create the host-order struct from the - * network-order string) */ - cell_unpack(&cell, buf, wide_circ_ids); - - channel_tls_handle_cell(&cell, conn); - } - } -} - -/** Array of recognized link protocol versions. */ -static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4 }; -/** Number of versions in or_protocol_versions. */ -static const int n_or_protocol_versions = - (int)( sizeof(or_protocol_versions)/sizeof(uint16_t) ); - -/** Return true iff v is a link protocol version that this Tor - * implementation believes it can support. */ -int -is_or_protocol_version_known(uint16_t v) -{ - int i; - for (i = 0; i < n_or_protocol_versions; ++i) { - if (or_protocol_versions[i] == v) - return 1; - } - return 0; -} - -/** Send a VERSIONS cell on conn, telling the other host about the - * link protocol versions that this Tor can support. - * - * If v3_plus, this is part of a V3 protocol handshake, so only - * allow protocol version v3 or later. If not v3_plus, this is - * not part of a v3 protocol handshake, so don't allow protocol v3 or - * later. - **/ -int -connection_or_send_versions(or_connection_t *conn, int v3_plus) -{ - var_cell_t *cell; - int i; - int n_versions = 0; - const int min_version = v3_plus ? 3 : 0; - const int max_version = v3_plus ? UINT16_MAX : 2; - tor_assert(conn->handshake_state && - !conn->handshake_state->sent_versions_at); - cell = var_cell_new(n_or_protocol_versions * 2); - cell->command = CELL_VERSIONS; - for (i = 0; i < n_or_protocol_versions; ++i) { - uint16_t v = or_protocol_versions[i]; - if (v < min_version || v > max_version) - continue; - set_uint16(cell->payload+(2*n_versions), htons(v)); - ++n_versions; - } - cell->payload_len = n_versions * 2; - - connection_or_write_var_cell_to_buf(cell, conn); - conn->handshake_state->sent_versions_at = time(NULL); - - var_cell_free(cell); - return 0; -} - -/** Send a NETINFO cell on conn, telling the other server what we know - * about their address, our address, and the current time. */ -int -connection_or_send_netinfo(or_connection_t *conn) -{ - cell_t cell; - time_t now = time(NULL); - const routerinfo_t *me; - int len; - uint8_t *out; - - tor_assert(conn->handshake_state); - - if (conn->handshake_state->sent_netinfo) { - log_warn(LD_BUG, "Attempted to send an extra netinfo cell on a connection " - "where we already sent one."); - return 0; - } - - memset(&cell, 0, sizeof(cell_t)); - cell.command = CELL_NETINFO; - - /* Timestamp, if we're a relay. */ - if (public_server_mode(get_options()) || ! conn->is_outgoing) - set_uint32(cell.payload, htonl((uint32_t)now)); - - /* Their address. */ - out = cell.payload + 4; - /* We use &conn->real_addr below, unless it hasn't yet been set. If it - * hasn't yet been set, we know that base_.addr hasn't been tampered with - * yet either. */ - len = append_address_to_payload(out, !tor_addr_is_null(&conn->real_addr) - ? &conn->real_addr : &conn->base_.addr); - if (len<0) - return -1; - out += len; - - /* My address -- only include it if I'm a public relay, or if I'm a - * bridge and this is an incoming connection. If I'm a bridge and this - * is an outgoing connection, act like a normal client and omit it. */ - if ((public_server_mode(get_options()) || !conn->is_outgoing) && - (me = router_get_my_routerinfo())) { - tor_addr_t my_addr; - *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; - } - - conn->handshake_state->digest_sent_data = 0; - conn->handshake_state->sent_netinfo = 1; - connection_or_write_cell_to_buf(&cell, conn); - - return 0; -} - -/** Send a CERTS cell on the connection conn. Return 0 on success, -1 - * on failure. */ -int -connection_or_send_certs_cell(or_connection_t *conn) -{ - const tor_cert_t *link_cert = NULL, *id_cert = NULL; - const uint8_t *link_encoded = NULL, *id_encoded = NULL; - size_t link_len, id_len; - var_cell_t *cell; - size_t cell_len; - ssize_t pos; - int server_mode; - - tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); - - if (! conn->handshake_state) - return -1; - server_mode = ! conn->handshake_state->started_here; - if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0) - return -1; - tor_cert_get_der(link_cert, &link_encoded, &link_len); - tor_cert_get_der(id_cert, &id_encoded, &id_len); - - cell_len = 1 /* 1 byte: num certs in cell */ + - 2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ + - link_len + id_len; - cell = var_cell_new(cell_len); - cell->command = CELL_CERTS; - cell->payload[0] = 2; - pos = 1; - - if (server_mode) - cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ - else - cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */ - set_uint16(&cell->payload[pos+1], htons(link_len)); - memcpy(&cell->payload[pos+3], link_encoded, link_len); - pos += 3 + link_len; - - cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */ - set_uint16(&cell->payload[pos+1], htons(id_len)); - memcpy(&cell->payload[pos+3], id_encoded, id_len); - pos += 3 + id_len; - - tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */ - - connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - - return 0; -} - -/** Send an AUTH_CHALLENGE cell on the connection conn. Return 0 - * on success, -1 on failure. */ -int -connection_or_send_auth_challenge_cell(or_connection_t *conn) -{ - var_cell_t *cell; - uint8_t *cp; - uint8_t challenge[OR_AUTH_CHALLENGE_LEN]; - tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); - - if (! conn->handshake_state) - return -1; - - if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0) - return -1; - cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4); - cell->command = CELL_AUTH_CHALLENGE; - memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN); - cp = cell->payload + OR_AUTH_CHALLENGE_LEN; - set_uint16(cp, htons(1)); /* We recognize one authentication type. */ - set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); - - connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - memwipe(challenge, 0, sizeof(challenge)); - - return 0; -} - -/** Compute the main body of an AUTHENTICATE cell that a client can use - * to authenticate itself on a v3 handshake for conn. Write it to the - * outlen-byte buffer at out. - * - * If server is true, only calculate the first - * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's - * determined by the rest of the handshake, and which match the provided value - * exactly. - * - * If server is false and signing_key is NULL, calculate the - * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything - * that should be signed), but don't actually sign it. - * - * If server is false and signing_key is provided, calculate the - * entire authenticator, signed with signing_key. - * - * Return the length of the cell body on success, and -1 on failure. - */ -int -connection_or_compute_authenticate_cell_body(or_connection_t *conn, - uint8_t *out, size_t outlen, - crypto_pk_t *signing_key, - int server) -{ - uint8_t *ptr; - - /* assert state is reasonable XXXX */ - - if (outlen < V3_AUTH_FIXED_PART_LEN || - (!server && outlen < V3_AUTH_BODY_LEN)) - return -1; - - ptr = out; - - /* Type: 8 bytes. */ - memcpy(ptr, "AUTH0001", 8); - ptr += 8; - - { - const tor_cert_t *id_cert=NULL, *link_cert=NULL; - const digests_t *my_digests, *their_digests; - const uint8_t *my_id, *their_id, *client_id, *server_id; - if (tor_tls_get_my_certs(server, &link_cert, &id_cert)) - return -1; - my_digests = tor_cert_get_id_digests(id_cert); - their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); - tor_assert(my_digests); - tor_assert(their_digests); - my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; - their_id = (uint8_t*)their_digests->d[DIGEST_SHA256]; - - client_id = server ? their_id : my_id; - server_id = server ? my_id : their_id; - - /* Client ID digest: 32 octets. */ - memcpy(ptr, client_id, 32); - ptr += 32; - - /* Server ID digest: 32 octets. */ - memcpy(ptr, server_id, 32); - ptr += 32; - } - - { - crypto_digest_t *server_d, *client_d; - if (server) { - server_d = conn->handshake_state->digest_sent; - client_d = conn->handshake_state->digest_received; - } else { - client_d = conn->handshake_state->digest_sent; - server_d = conn->handshake_state->digest_received; - } - - /* Server log digest : 32 octets */ - crypto_digest_get_digest(server_d, (char*)ptr, 32); - ptr += 32; - - /* Client log digest : 32 octets */ - crypto_digest_get_digest(client_d, (char*)ptr, 32); - ptr += 32; - } - - { - /* Digest of cert used on TLS link : 32 octets. */ - const tor_cert_t *cert = NULL; - tor_cert_t *freecert = NULL; - if (server) { - tor_tls_get_my_certs(1, &cert, NULL); - } else { - freecert = tor_tls_get_peer_cert(conn->tls); - cert = freecert; - } - if (!cert) - return -1; - memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); - - if (freecert) - tor_cert_free(freecert); - ptr += 32; - } - - /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - tor_tls_get_tlssecrets(conn->tls, ptr); - ptr += 32; - - tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN); - - if (server) - return V3_AUTH_FIXED_PART_LEN; // ptr-out - - /* 8 octets were reserved for the current time, but we're trying to get out - * of the habit of sending time around willynilly. Fortunately, nothing - * checks it. That's followed by 16 bytes of nonce. */ - crypto_rand((char*)ptr, 24); - ptr += 24; - - tor_assert(ptr - out == V3_AUTH_BODY_LEN); - - if (!signing_key) - return V3_AUTH_BODY_LEN; // ptr - out - - { - int siglen; - char d[32]; - crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256); - siglen = crypto_pk_private_sign(signing_key, - (char*)ptr, outlen - (ptr-out), - d, 32); - if (siglen < 0) - return -1; - - ptr += siglen; - tor_assert(ptr <= out+outlen); - return (int)(ptr - out); - } -} - -/** Send an AUTHENTICATE cell on the connection conn. Return 0 on - * success, -1 on failure */ -int -connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) -{ - var_cell_t *cell; - crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); - int authlen; - size_t cell_maxlen; - /* XXXX make sure we're actually supposed to send this! */ - - if (!pk) { - log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); - return -1; - } - if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) { - log_warn(LD_BUG, "Tried to send authenticate cell with unknown " - "authentication type %d", authtype); - return -1; - } - - cell_maxlen = 4 + /* overhead */ - V3_AUTH_BODY_LEN + /* Authentication body */ - crypto_pk_keysize(pk) + /* Max signature length */ - 16 /* add a few extra bytes just in case. */; - - cell = var_cell_new(cell_maxlen); - cell->command = CELL_AUTHENTICATE; - set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); - /* skip over length ; we don't know that yet. */ - - authlen = connection_or_compute_authenticate_cell_body(conn, - cell->payload+4, - cell_maxlen-4, - pk, - 0 /* not server */); - if (authlen < 0) { - log_warn(LD_BUG, "Unable to compute authenticate cell!"); - var_cell_free(cell); - return -1; - } - tor_assert(authlen + 4 <= cell->payload_len); - set_uint16(cell->payload+2, htons(authlen)); - cell->payload_len = authlen + 4; - - connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - - return 0; -} - diff --git a/src/tor/connection_or.h b/src/tor/connection_or.h deleted file mode 100644 index 8d93028..0000000 --- a/src/tor/connection_or.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file connection_or.h - * \brief Header file for connection_or.c. - **/ - -#ifndef TOR_CONNECTION_OR_H -#define TOR_CONNECTION_OR_H - -void connection_or_remove_from_identity_map(or_connection_t *conn); -void connection_or_clear_identity_map(void); -void clear_broken_connection_map(int disable); -or_connection_t *connection_or_get_for_extend(const char *digest, - const tor_addr_t *target_addr, - const char **msg_out, - int *launch_out); -void connection_or_set_bad_connections(const char *digest, int force); - -void connection_or_block_renegotiation(or_connection_t *conn); -int connection_or_reached_eof(or_connection_t *conn); -int connection_or_process_inbuf(or_connection_t *conn); -int connection_or_flushed_some(or_connection_t *conn); -int connection_or_finished_flushing(or_connection_t *conn); -int connection_or_finished_connecting(or_connection_t *conn); -void connection_or_about_to_close(or_connection_t *conn); -int connection_or_digest_is_known_relay(const char *id_digest); -void connection_or_update_token_buckets(smartlist_t *conns, - const or_options_t *options); - -void connection_or_connect_failed(or_connection_t *conn, - int reason, const char *msg); -void connection_or_notify_error(or_connection_t *conn, - int reason, const char *msg); -or_connection_t *connection_or_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest, - channel_tls_t *chan); - -void connection_or_close_normally(or_connection_t *orconn, int flush); -void connection_or_close_for_error(or_connection_t *orconn, int flush); - -void connection_or_report_broken_states(int severity, int domain); - -MOCK_DECL(int,connection_tls_start_handshake,(or_connection_t *conn, - int receiving)); -int connection_tls_continue_handshake(or_connection_t *conn); - -int connection_init_or_handshake_state(or_connection_t *conn, - int started_here); -void connection_or_init_conn_from_address(or_connection_t *conn, - const tor_addr_t *addr, - uint16_t port, - const char *id_digest, - int started_here); -int connection_or_client_learned_peer_id(or_connection_t *conn, - const uint8_t *peer_id); -time_t connection_or_client_used(or_connection_t *conn); -int connection_or_get_num_circuits(or_connection_t *conn); -void or_handshake_state_free(or_handshake_state_t *state); -void or_handshake_state_record_cell(or_connection_t *conn, - or_handshake_state_t *state, - const cell_t *cell, - int incoming); -void or_handshake_state_record_var_cell(or_connection_t *conn, - or_handshake_state_t *state, - const var_cell_t *cell, - int incoming); - -int connection_or_set_state_open(or_connection_t *conn); -void connection_or_write_cell_to_buf(const cell_t *cell, - or_connection_t *conn); -void connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn); -int connection_or_send_versions(or_connection_t *conn, int v3_plus); -int connection_or_send_netinfo(or_connection_t *conn); -int connection_or_send_certs_cell(or_connection_t *conn); -int connection_or_send_auth_challenge_cell(or_connection_t *conn); -int connection_or_compute_authenticate_cell_body(or_connection_t *conn, - uint8_t *out, size_t outlen, - crypto_pk_t *signing_key, - int server); -int connection_or_send_authenticate_cell(or_connection_t *conn, int type); - -int is_or_protocol_version_known(uint16_t version); - -void cell_pack(packed_cell_t *dest, const cell_t *src, int wide_circ_ids); -int var_cell_pack_header(const var_cell_t *cell, char *hdr_out, - int wide_circ_ids); -var_cell_t *var_cell_new(uint16_t payload_len); -void var_cell_free(var_cell_t *cell); - -/** DOCDOC */ -#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 - -#endif - diff --git a/src/tor/container.c b/src/tor/container.c deleted file mode 100644 index eee8591..0000000 --- a/src/tor/container.c +++ /dev/null @@ -1,1557 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file container.c - * \brief Implements a smartlist (a resizable array) along - * with helper functions to use smartlists. Also includes - * hash table implementations of a string-to-void* map, and of - * a digest-to-void* map. - **/ - -#include "tor_compat.h" -#include "tor_util.h" -#include "torlog.h" -#include "container.h" -#include "crypto.h" - -#include -#include -#include - -#include "ht.h" - -/** All newly allocated smartlists have this capacity. */ -#define SMARTLIST_DEFAULT_CAPACITY 16 - -/** Allocate and return an empty smartlist. - */ -smartlist_t * -smartlist_new(void) -{ - smartlist_t *sl = tor_malloc(sizeof(smartlist_t)); - sl->num_used = 0; - sl->capacity = SMARTLIST_DEFAULT_CAPACITY; - sl->list = tor_malloc(sizeof(void *) * sl->capacity); - return sl; -} - -/** Deallocate a smartlist. Does not release storage associated with the - * list's elements. - */ -void -smartlist_free(smartlist_t *sl) -{ - if (!sl) - return; - tor_free(sl->list); - tor_free(sl); -} - -/** Remove all elements from the list. - */ -void -smartlist_clear(smartlist_t *sl) -{ - sl->num_used = 0; -} - -/** Make sure that sl can hold at least size entries. */ -static INLINE void -smartlist_ensure_capacity(smartlist_t *sl, int size) -{ -#if SIZEOF_SIZE_T > SIZEOF_INT -#define MAX_CAPACITY (INT_MAX) -#else -#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*)))) -#endif - if (size > sl->capacity) { - int higher = sl->capacity; - if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) { - tor_assert(size <= MAX_CAPACITY); - higher = MAX_CAPACITY; - } else { - while (size > higher) - higher *= 2; - } - sl->capacity = higher; - sl->list = tor_realloc(sl->list, sizeof(void*)*((size_t)sl->capacity)); - } -} - -/** Append element to the end of the list. */ -void -smartlist_add(smartlist_t *sl, void *element) -{ - smartlist_ensure_capacity(sl, sl->num_used+1); - sl->list[sl->num_used++] = element; -} - -/** Append each element from S2 to the end of S1. */ -void -smartlist_add_all(smartlist_t *s1, const smartlist_t *s2) -{ - int new_size = s1->num_used + s2->num_used; - tor_assert(new_size >= s1->num_used); /* check for overflow. */ - smartlist_ensure_capacity(s1, new_size); - memcpy(s1->list + s1->num_used, s2->list, s2->num_used*sizeof(void*)); - s1->num_used = new_size; -} - -/** Remove all elements E from sl such that E==element. Preserve - * the order of any elements before E, but elements after E can be - * rearranged. - */ -void -smartlist_remove(smartlist_t *sl, const void *element) -{ - int i; - if (element == NULL) - return; - for (i=0; i < sl->num_used; i++) - if (sl->list[i] == element) { - sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ - i--; /* so we process the new i'th element */ - } -} - -/** If sl is nonempty, remove and return the final element. Otherwise, - * return NULL. */ -void * -smartlist_pop_last(smartlist_t *sl) -{ - tor_assert(sl); - if (sl->num_used) - return sl->list[--sl->num_used]; - else - return NULL; -} - -/** Reverse the order of the items in sl. */ -void -smartlist_reverse(smartlist_t *sl) -{ - int i, j; - void *tmp; - tor_assert(sl); - for (i = 0, j = sl->num_used-1; i < j; ++i, --j) { - tmp = sl->list[i]; - sl->list[i] = sl->list[j]; - sl->list[j] = tmp; - } -} - -/** If there are any strings in sl equal to element, remove and free them. - * Does not preserve order. */ -void -smartlist_string_remove(smartlist_t *sl, const char *element) -{ - int i; - tor_assert(sl); - tor_assert(element); - for (i = 0; i < sl->num_used; ++i) { - if (!strcmp(element, sl->list[i])) { - tor_free(sl->list[i]); - sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ - i--; /* so we process the new i'th element */ - } - } -} - -/** Return true iff some element E of sl has E==element. - */ -int -smartlist_contains(const smartlist_t *sl, const void *element) -{ - int i; - for (i=0; i < sl->num_used; i++) - if (sl->list[i] == element) - return 1; - return 0; -} - -/** Return true iff sl has some element E such that - * !strcmp(E,element) - */ -int -smartlist_contains_string(const smartlist_t *sl, const char *element) -{ - int i; - if (!sl) return 0; - for (i=0; i < sl->num_used; i++) - if (strcmp((const char*)sl->list[i],element)==0) - return 1; - return 0; -} - -/** If element is equal to an element of sl, return that - * element's index. Otherwise, return -1. */ -int -smartlist_string_pos(const smartlist_t *sl, const char *element) -{ - int i; - if (!sl) return -1; - for (i=0; i < sl->num_used; i++) - if (strcmp((const char*)sl->list[i],element)==0) - return i; - return -1; -} - -/** Return true iff sl has some element E such that - * !strcasecmp(E,element) - */ -int -smartlist_contains_string_case(const smartlist_t *sl, const char *element) -{ - int i; - if (!sl) return 0; - for (i=0; i < sl->num_used; i++) - if (strcasecmp((const char*)sl->list[i],element)==0) - return 1; - return 0; -} - -/** Return true iff sl has some element E such that E is equal - * to the decimal encoding of num. - */ -int -smartlist_contains_int_as_string(const smartlist_t *sl, int num) -{ - char buf[32]; /* long enough for 64-bit int, and then some. */ - tor_snprintf(buf,sizeof(buf),"%d", num); - return smartlist_contains_string(sl, buf); -} - -/** Return true iff the two lists contain the same strings in the same - * order, or if they are both NULL. */ -int -smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) -{ - if (sl1 == NULL) - return sl2 == NULL; - if (sl2 == NULL) - return 0; - if (smartlist_len(sl1) != smartlist_len(sl2)) - return 0; - SMARTLIST_FOREACH(sl1, const char *, cp1, { - const char *cp2 = smartlist_get(sl2, cp1_sl_idx); - if (strcmp(cp1, cp2)) - return 0; - }); - return 1; -} - -/** Return true iff the two lists contain the same int pointer values in - * the same order, or if they are both NULL. */ -int -smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2) -{ - if (sl1 == NULL) - return sl2 == NULL; - if (sl2 == NULL) - return 0; - if (smartlist_len(sl1) != smartlist_len(sl2)) - return 0; - SMARTLIST_FOREACH(sl1, int *, cp1, { - int *cp2 = smartlist_get(sl2, cp1_sl_idx); - if (*cp1 != *cp2) - return 0; - }); - return 1; -} - -/** Return true iff sl has some element E such that - * tor_memeq(E,element,DIGEST_LEN) - */ -int -smartlist_contains_digest(const smartlist_t *sl, const char *element) -{ - int i; - if (!sl) return 0; - for (i=0; i < sl->num_used; i++) - if (tor_memeq((const char*)sl->list[i],element,DIGEST_LEN)) - return 1; - return 0; -} - -/** Return true iff some element E of sl2 has smartlist_contains(sl1,E). - */ -int -smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) -{ - int i; - for (i=0; i < sl2->num_used; i++) - if (smartlist_contains(sl1, sl2->list[i])) - return 1; - return 0; -} - -/** Remove every element E of sl1 such that !smartlist_contains(sl2,E). - * Does not preserve the order of sl1. - */ -void -smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2) -{ - int i; - for (i=0; i < sl1->num_used; i++) - if (!smartlist_contains(sl2, sl1->list[i])) { - sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */ - i--; /* so we process the new i'th element */ - } -} - -/** Remove every element E of sl1 such that smartlist_contains(sl2,E). - * Does not preserve the order of sl1. - */ -void -smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2) -{ - int i; - for (i=0; i < sl2->num_used; i++) - smartlist_remove(sl1, sl2->list[i]); -} - -/** Remove the idxth element of sl; if idx is not the last - * element, swap the last element of sl into the idxth space. - */ -void -smartlist_del(smartlist_t *sl, int idx) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx < sl->num_used); - sl->list[idx] = sl->list[--sl->num_used]; -} - -/** Remove the idxth element of sl; if idx is not the last element, - * moving all subsequent elements back one space. Return the old value - * of the idxth element. - */ -void -smartlist_del_keeporder(smartlist_t *sl, int idx) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx < sl->num_used); - --sl->num_used; - if (idx < sl->num_used) - memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx)); -} - -/** Insert the value val as the new idxth element of - * sl, moving all items previously at idx or later - * forward one space. - */ -void -smartlist_insert(smartlist_t *sl, int idx, void *val) -{ - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(idx <= sl->num_used); - if (idx == sl->num_used) { - smartlist_add(sl, val); - } else { - smartlist_ensure_capacity(sl, sl->num_used+1); - /* Move other elements away */ - if (idx < sl->num_used) - memmove(sl->list + idx + 1, sl->list + idx, - sizeof(void*)*(sl->num_used-idx)); - sl->num_used++; - sl->list[idx] = val; - } -} - -/** - * Split a string str along all occurrences of sep, - * appending the (newly allocated) split strings, in order, to - * sl. Return the number of strings added to sl. - * - * If flags&SPLIT_SKIP_SPACE is true, remove initial and - * trailing space from each entry. - * If flags&SPLIT_IGNORE_BLANK is true, remove any entries - * of length 0. - * If flags&SPLIT_STRIP_SPACE is true, strip spaces from each - * split string. - * - * If max\>0, divide the string into no more than max pieces. If - * sep is NULL, split on any sequence of horizontal space. - */ -int -smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, - int flags, int max) -{ - const char *cp, *end, *next; - int n = 0; - - tor_assert(sl); - tor_assert(str); - - cp = str; - while (1) { - if (flags&SPLIT_SKIP_SPACE) { - while (TOR_ISSPACE(*cp)) ++cp; - } - - if (max>0 && n == max-1) { - end = strchr(cp,'\0'); - } else if (sep) { - end = strstr(cp,sep); - if (!end) - end = strchr(cp,'\0'); - } else { - for (end = cp; *end && *end != '\t' && *end != ' '; ++end) - ; - } - - tor_assert(end); - - if (!*end) { - next = NULL; - } else if (sep) { - next = end+strlen(sep); - } else { - next = end+1; - while (*next == '\t' || *next == ' ') - ++next; - } - - if (flags&SPLIT_SKIP_SPACE) { - while (end > cp && TOR_ISSPACE(*(end-1))) - --end; - } - if (end != cp || !(flags&SPLIT_IGNORE_BLANK)) { - char *string = tor_strndup(cp, end-cp); - if (flags&SPLIT_STRIP_SPACE) - tor_strstrip(string, " "); - smartlist_add(sl, string); - ++n; - } - if (!next) - break; - cp = next; - } - - return n; -} - -/** Allocate and return a new string containing the concatenation of - * the elements of sl, in order, separated by join. If - * terminate is true, also terminate the string with join. - * If len_out is not NULL, set len_out to the length of - * the returned string. Requires that every element of sl is - * NUL-terminated string. - */ -char * -smartlist_join_strings(smartlist_t *sl, const char *join, - int terminate, size_t *len_out) -{ - return smartlist_join_strings2(sl,join,strlen(join),terminate,len_out); -} - -/** As smartlist_join_strings, but instead of separating/terminated with a - * NUL-terminated string join, uses the join_len-byte sequence - * at join. (Useful for generating a sequence of NUL-terminated - * strings.) - */ -char * -smartlist_join_strings2(smartlist_t *sl, const char *join, - size_t join_len, int terminate, size_t *len_out) -{ - int i; - size_t n = 0; - char *r = NULL, *dst, *src; - - tor_assert(sl); - tor_assert(join); - - if (terminate) - n = join_len; - - for (i = 0; i < sl->num_used; ++i) { - n += strlen(sl->list[i]); - if (i+1 < sl->num_used) /* avoid double-counting the last one */ - n += join_len; - } - dst = r = tor_malloc(n+1); - for (i = 0; i < sl->num_used; ) { - for (src = sl->list[i]; *src; ) - *dst++ = *src++; - if (++i < sl->num_used) { - memcpy(dst, join, join_len); - dst += join_len; - } - } - if (terminate) { - memcpy(dst, join, join_len); - dst += join_len; - } - *dst = '\0'; - - if (len_out) - *len_out = dst-r; - return r; -} - -/** Sort the members of sl into an order defined by - * the ordering function compare, which returns less then 0 if a - * precedes b, greater than 0 if b precedes a, and 0 if a 'equals' b. - */ -void -smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b)) -{ - if (!sl->num_used) - return; - qsort(sl->list, sl->num_used, sizeof(void*), - (int (*)(const void *,const void*))compare); -} - -/** Given a smartlist sl sorted with the function compare, - * return the most frequent member in the list. Break ties in favor of - * later elements. If the list is empty, return NULL. - */ -void * -smartlist_get_most_frequent(const smartlist_t *sl, - int (*compare)(const void **a, const void **b)) -{ - const void *most_frequent = NULL; - int most_frequent_count = 0; - - const void *cur = NULL; - int i, count=0; - - if (!sl->num_used) - return NULL; - for (i = 0; i < sl->num_used; ++i) { - const void *item = sl->list[i]; - if (cur && 0 == compare(&cur, &item)) { - ++count; - } else { - if (cur && count >= most_frequent_count) { - most_frequent = cur; - most_frequent_count = count; - } - cur = item; - count = 1; - } - } - if (cur && count >= most_frequent_count) { - most_frequent = cur; - most_frequent_count = count; - } - return (void*)most_frequent; -} - -/** Given a sorted smartlist sl and the comparison function used to - * sort it, remove all duplicate members. If free_fn is provided, calls - * free_fn on each duplicate. Otherwise, just removes them. Preserves order. - */ -void -smartlist_uniq(smartlist_t *sl, - int (*compare)(const void **a, const void **b), - void (*free_fn)(void *a)) -{ - int i; - for (i=1; i < sl->num_used; ++i) { - if (compare((const void **)&(sl->list[i-1]), - (const void **)&(sl->list[i])) == 0) { - if (free_fn) - free_fn(sl->list[i]); - smartlist_del_keeporder(sl, i--); - } - } -} - -/** Assuming the members of sl are in order, return a pointer to the - * member that matches key. Ordering and matching are defined by a - * compare function that returns 0 on a match; less than 0 if key is - * less than member, and greater than 0 if key is greater then member. - */ -void * -smartlist_bsearch(smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member)) -{ - int found, idx; - idx = smartlist_bsearch_idx(sl, key, compare, &found); - return found ? smartlist_get(sl, idx) : NULL; -} - -/** Assuming the members of sl are in order, return the index of the - * member that matches key. If no member matches, return the index of - * the first member greater than key, or smartlist_len(sl) if no member - * is greater than key. Set found_out to true on a match, to - * false otherwise. Ordering and matching are defined by a compare - * function that returns 0 on a match; less than 0 if key is less than member, - * and greater than 0 if key is greater then member. - */ -int -smartlist_bsearch_idx(const smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member), - int *found_out) -{ - int hi, lo, cmp, mid, len, diff; - - tor_assert(sl); - tor_assert(compare); - tor_assert(found_out); - - len = smartlist_len(sl); - - /* Check for the trivial case of a zero-length list */ - if (len == 0) { - *found_out = 0; - /* We already know smartlist_len(sl) is 0 in this case */ - return 0; - } - - /* Okay, we have a real search to do */ - tor_assert(len > 0); - lo = 0; - hi = len - 1; - - /* - * These invariants are always true: - * - * For all i such that 0 <= i < lo, sl[i] < key - * For all i such that hi < i <= len, sl[i] > key - */ - - while (lo <= hi) { - diff = hi - lo; - /* - * We want mid = (lo + hi) / 2, but that could lead to overflow, so - * instead diff = hi - lo (non-negative because of loop condition), and - * then hi = lo + diff, mid = (lo + lo + diff) / 2 = lo + (diff / 2). - */ - mid = lo + (diff / 2); - cmp = compare(key, (const void**) &(sl->list[mid])); - if (cmp == 0) { - /* sl[mid] == key; we found it */ - *found_out = 1; - return mid; - } else if (cmp > 0) { - /* - * key > sl[mid] and an index i such that sl[i] == key must - * have i > mid if it exists. - */ - - /* - * Since lo <= mid <= hi, hi can only decrease on each iteration (by - * being set to mid - 1) and hi is initially len - 1, mid < len should - * always hold, and this is not symmetric with the left end of list - * mid > 0 test below. A key greater than the right end of the list - * should eventually lead to lo == hi == mid == len - 1, and then - * we set lo to len below and fall out to the same exit we hit for - * a key in the middle of the list but not matching. Thus, we just - * assert for consistency here rather than handle a mid == len case. - */ - tor_assert(mid < len); - /* Move lo to the element immediately after sl[mid] */ - lo = mid + 1; - } else { - /* This should always be true in this case */ - tor_assert(cmp < 0); - - /* - * key < sl[mid] and an index i such that sl[i] == key must - * have i < mid if it exists. - */ - - if (mid > 0) { - /* Normal case, move hi to the element immediately before sl[mid] */ - hi = mid - 1; - } else { - /* These should always be true in this case */ - tor_assert(mid == lo); - tor_assert(mid == 0); - /* - * We were at the beginning of the list and concluded that every - * element e compares e > key. - */ - *found_out = 0; - return 0; - } - } - } - - /* - * lo > hi; we have no element matching key but we have elements falling - * on both sides of it. The lo index points to the first element > key. - */ - tor_assert(lo == hi + 1); /* All other cases should have been handled */ - tor_assert(lo >= 0); - tor_assert(lo <= len); - tor_assert(hi >= 0); - tor_assert(hi <= len); - - if (lo < len) { - cmp = compare(key, (const void **) &(sl->list[lo])); - tor_assert(cmp < 0); - } else { - cmp = compare(key, (const void **) &(sl->list[len-1])); - tor_assert(cmp > 0); - } - - *found_out = 0; - return lo; -} - -/** Helper: compare two const char **s. */ -static int -compare_string_ptrs_(const void **_a, const void **_b) -{ - return strcmp((const char*)*_a, (const char*)*_b); -} - -/** Sort a smartlist sl containing strings into lexically ascending - * order. */ -void -smartlist_sort_strings(smartlist_t *sl) -{ - smartlist_sort(sl, compare_string_ptrs_); -} - -/** Return the most frequent string in the sorted list sl */ -char * -smartlist_get_most_frequent_string(smartlist_t *sl) -{ - return smartlist_get_most_frequent(sl, compare_string_ptrs_); -} - -/** Remove duplicate strings from a sorted list, and free them with tor_free(). - */ -void -smartlist_uniq_strings(smartlist_t *sl) -{ - smartlist_uniq(sl, compare_string_ptrs_, tor_free_); -} - -/* Heap-based priority queue implementation for O(lg N) insert and remove. - * Recall that the heap property is that, for every index I, h[I] < - * H[LEFT_CHILD[I]] and h[I] < H[RIGHT_CHILD[I]]. - * - * For us to remove items other than the topmost item, each item must store - * its own index within the heap. When calling the pqueue functions, tell - * them about the offset of the field that stores the index within the item. - * - * Example: - * - * typedef struct timer_t { - * struct timeval tv; - * int heap_index; - * } timer_t; - * - * static int compare(const void *p1, const void *p2) { - * const timer_t *t1 = p1, *t2 = p2; - * if (t1->tv.tv_sec < t2->tv.tv_sec) { - * return -1; - * } else if (t1->tv.tv_sec > t2->tv.tv_sec) { - * return 1; - * } else { - * return t1->tv.tv_usec - t2->tv_usec; - * } - * } - * - * void timer_heap_insert(smartlist_t *heap, timer_t *timer) { - * smartlist_pqueue_add(heap, compare, STRUCT_OFFSET(timer_t, heap_index), - * timer); - * } - * - * void timer_heap_pop(smartlist_t *heap) { - * return smartlist_pqueue_pop(heap, compare, - * STRUCT_OFFSET(timer_t, heap_index)); - * } - */ - -/** @{ */ -/** Functions to manipulate heap indices to find a node's parent and children. - * - * For a 1-indexed array, we would use LEFT_CHILD[x] = 2*x and RIGHT_CHILD[x] - * = 2*x + 1. But this is C, so we have to adjust a little. */ -//#define LEFT_CHILD(i) ( ((i)+1)*2 - 1) -//#define RIGHT_CHILD(i) ( ((i)+1)*2 ) -//#define PARENT(i) ( ((i)+1)/2 - 1) -#define LEFT_CHILD(i) ( 2*(i) + 1 ) -#define RIGHT_CHILD(i) ( 2*(i) + 2 ) -#define PARENT(i) ( ((i)-1) / 2 ) -/** }@ */ - -/** @{ */ -/** Helper macros for heaps: Given a local variable idx_field_offset - * set to the offset of an integer index within the heap element structure, - * IDX_OF_ITEM(p) gives you the index of p, and IDXP(p) gives you a pointer to - * where p's index is stored. Given additionally a local smartlist sl, - * UPDATE_IDX(i) sets the index of the element at i to the correct - * value (that is, to i). - */ -#define IDXP(p) ((int*)STRUCT_VAR_P(p, idx_field_offset)) - -#define UPDATE_IDX(i) do { \ - void *updated = sl->list[i]; \ - *IDXP(updated) = i; \ - } while (0) - -#define IDX_OF_ITEM(p) (*IDXP(p)) -/** @} */ - -/** Helper. sl may have at most one violation of the heap property: - * the item at idx may be greater than one or both of its children. - * Restore the heap property. */ -static INLINE void -smartlist_heapify(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset, - int idx) -{ - while (1) { - int left_idx = LEFT_CHILD(idx); - int best_idx; - - if (left_idx >= sl->num_used) - return; - if (compare(sl->list[idx],sl->list[left_idx]) < 0) - best_idx = idx; - else - best_idx = left_idx; - if (left_idx+1 < sl->num_used && - compare(sl->list[left_idx+1],sl->list[best_idx]) < 0) - best_idx = left_idx + 1; - - if (best_idx == idx) { - return; - } else { - void *tmp = sl->list[idx]; - sl->list[idx] = sl->list[best_idx]; - sl->list[best_idx] = tmp; - UPDATE_IDX(idx); - UPDATE_IDX(best_idx); - - idx = best_idx; - } - } -} - -/** Insert item into the heap stored in sl, where order is - * determined by compare and the offset of the item in the heap is - * stored in an int-typed field at position idx_field_offset within - * item. - */ -void -smartlist_pqueue_add(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset, - void *item) -{ - int idx; - smartlist_add(sl,item); - UPDATE_IDX(sl->num_used-1); - - for (idx = sl->num_used - 1; idx; ) { - int parent = PARENT(idx); - if (compare(sl->list[idx], sl->list[parent]) < 0) { - void *tmp = sl->list[parent]; - sl->list[parent] = sl->list[idx]; - sl->list[idx] = tmp; - UPDATE_IDX(parent); - UPDATE_IDX(idx); - idx = parent; - } else { - return; - } - } -} - -/** Remove and return the top-priority item from the heap stored in sl, - * where order is determined by compare and the item's position is - * stored at position idx_field_offset within the item. sl must - * not be empty. */ -void * -smartlist_pqueue_pop(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset) -{ - void *top; - tor_assert(sl->num_used); - - top = sl->list[0]; - *IDXP(top)=-1; - if (--sl->num_used) { - sl->list[0] = sl->list[sl->num_used]; - UPDATE_IDX(0); - smartlist_heapify(sl, compare, idx_field_offset, 0); - } - return top; -} - -/** Remove the item item from the heap stored in sl, - * where order is determined by compare and the item's position is - * stored at position idx_field_offset within the item. sl must - * not be empty. */ -void -smartlist_pqueue_remove(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset, - void *item) -{ - int idx = IDX_OF_ITEM(item); - tor_assert(idx >= 0); - tor_assert(sl->list[idx] == item); - --sl->num_used; - *IDXP(item) = -1; - if (idx == sl->num_used) { - return; - } else { - sl->list[idx] = sl->list[sl->num_used]; - UPDATE_IDX(idx); - smartlist_heapify(sl, compare, idx_field_offset, idx); - } -} - -/** Assert that the heap property is correctly maintained by the heap stored - * in sl, where order is determined by compare. */ -void -smartlist_pqueue_assert_ok(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset) -{ - int i; - for (i = sl->num_used - 1; i >= 0; --i) { - if (i>0) - tor_assert(compare(sl->list[PARENT(i)], sl->list[i]) <= 0); - tor_assert(IDX_OF_ITEM(sl->list[i]) == i); - } -} - -/** Helper: compare two DIGEST_LEN digests. */ -static int -compare_digests_(const void **_a, const void **_b) -{ - return tor_memcmp((const char*)*_a, (const char*)*_b, DIGEST_LEN); -} - -/** Sort the list of DIGEST_LEN-byte digests into ascending order. */ -void -smartlist_sort_digests(smartlist_t *sl) -{ - smartlist_sort(sl, compare_digests_); -} - -/** Remove duplicate digests from a sorted list, and free them with tor_free(). - */ -void -smartlist_uniq_digests(smartlist_t *sl) -{ - smartlist_uniq(sl, compare_digests_, tor_free_); -} - -/** Helper: compare two DIGEST256_LEN digests. */ -static int -compare_digests256_(const void **_a, const void **_b) -{ - return tor_memcmp((const char*)*_a, (const char*)*_b, DIGEST256_LEN); -} - -/** Sort the list of DIGEST256_LEN-byte digests into ascending order. */ -void -smartlist_sort_digests256(smartlist_t *sl) -{ - smartlist_sort(sl, compare_digests256_); -} - -/** Return the most frequent member of the sorted list of DIGEST256_LEN - * digests in sl */ -char * -smartlist_get_most_frequent_digest256(smartlist_t *sl) -{ - return smartlist_get_most_frequent(sl, compare_digests256_); -} - -/** Remove duplicate 256-bit digests from a sorted list, and free them with - * tor_free(). - */ -void -smartlist_uniq_digests256(smartlist_t *sl) -{ - smartlist_uniq(sl, compare_digests256_, tor_free_); -} - -/** Helper: Declare an entry type and a map type to implement a mapping using - * ht.h. The map type will be called maptype. The key part of each - * entry is declared using the C declaration keydecl. All functions - * and types associated with the map get prefixed with prefix */ -#define DEFINE_MAP_STRUCTS(maptype, keydecl, prefix) \ - typedef struct prefix ## entry_t { \ - HT_ENTRY(prefix ## entry_t) node; \ - void *val; \ - keydecl; \ - } prefix ## entry_t; \ - struct maptype { \ - HT_HEAD(prefix ## impl, prefix ## entry_t) head; \ - } - -DEFINE_MAP_STRUCTS(strmap_t, char *key, strmap_); -DEFINE_MAP_STRUCTS(digestmap_t, char key[DIGEST_LEN], digestmap_); - -/** Helper: compare strmap_entry_t objects by key value. */ -static INLINE int -strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b) -{ - return !strcmp(a->key, b->key); -} - -/** Helper: return a hash value for a strmap_entry_t. */ -static INLINE unsigned int -strmap_entry_hash(const strmap_entry_t *a) -{ - return ht_string_hash(a->key); -} - -/** Helper: compare digestmap_entry_t objects by key value. */ -static INLINE int -digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b) -{ - return tor_memeq(a->key, b->key, DIGEST_LEN); -} - -/** Helper: return a hash value for a digest_map_t. */ -static INLINE unsigned int -digestmap_entry_hash(const digestmap_entry_t *a) -{ -#if SIZEOF_INT != 8 - const uint32_t *p = (const uint32_t*)a->key; - return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; -#else - const uint64_t *p = (const uint64_t*)a->key; - return p[0] ^ p[1]; -#endif -} - -HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, - strmap_entries_eq) -HT_GENERATE(strmap_impl, strmap_entry_t, node, strmap_entry_hash, - strmap_entries_eq, 0.6, malloc, realloc, free) - -HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, - digestmap_entries_eq) -HT_GENERATE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash, - digestmap_entries_eq, 0.6, malloc, realloc, free) - -/** Constructor to create a new empty map from strings to void*'s. - */ -strmap_t * -strmap_new(void) -{ - strmap_t *result; - result = tor_malloc(sizeof(strmap_t)); - HT_INIT(strmap_impl, &result->head); - return result; -} - -/** Constructor to create a new empty map from digests to void*'s. - */ -digestmap_t * -digestmap_new(void) -{ - digestmap_t *result; - result = tor_malloc(sizeof(digestmap_t)); - HT_INIT(digestmap_impl, &result->head); - return result; -} - -/** Set the current value for key to val. Returns the previous - * value for key if one was set, or NULL if one was not. - * - * This function makes a copy of key if necessary, but not of - * val. - */ -void * -strmap_set(strmap_t *map, const char *key, void *val) -{ - strmap_entry_t *resolve; - strmap_entry_t search; - void *oldval; - tor_assert(map); - tor_assert(key); - tor_assert(val); - search.key = (char*)key; - resolve = HT_FIND(strmap_impl, &map->head, &search); - if (resolve) { - oldval = resolve->val; - resolve->val = val; - return oldval; - } else { - resolve = tor_malloc_zero(sizeof(strmap_entry_t)); - resolve->key = tor_strdup(key); - resolve->val = val; - tor_assert(!HT_FIND(strmap_impl, &map->head, resolve)); - HT_INSERT(strmap_impl, &map->head, resolve); - return NULL; - } -} - -#define OPTIMIZED_DIGESTMAP_SET - -/** Like strmap_set() above but for digestmaps. */ -void * -digestmap_set(digestmap_t *map, const char *key, void *val) -{ -#ifndef OPTIMIZED_DIGESTMAP_SET - digestmap_entry_t *resolve; -#endif - digestmap_entry_t search; - void *oldval; - tor_assert(map); - tor_assert(key); - tor_assert(val); - memcpy(&search.key, key, DIGEST_LEN); -#ifndef OPTIMIZED_DIGESTMAP_SET - resolve = HT_FIND(digestmap_impl, &map->head, &search); - if (resolve) { - oldval = resolve->val; - resolve->val = val; - return oldval; - } else { - resolve = tor_malloc_zero(sizeof(digestmap_entry_t)); - memcpy(resolve->key, key, DIGEST_LEN); - resolve->val = val; - HT_INSERT(digestmap_impl, &map->head, resolve); - return NULL; - } -#else - /* We spend up to 5% of our time in this function, so the code below is - * meant to optimize the check/alloc/set cycle by avoiding the two trips to - * the hash table that we do in the unoptimized code above. (Each of - * HT_INSERT and HT_FIND calls HT_SET_HASH and HT_FIND_P.) - */ - HT_FIND_OR_INSERT_(digestmap_impl, node, digestmap_entry_hash, &(map->head), - digestmap_entry_t, &search, ptr, - { - /* we found an entry. */ - oldval = (*ptr)->val; - (*ptr)->val = val; - return oldval; - }, - { - /* We didn't find the entry. */ - digestmap_entry_t *newent = - tor_malloc_zero(sizeof(digestmap_entry_t)); - memcpy(newent->key, key, DIGEST_LEN); - newent->val = val; - HT_FOI_INSERT_(node, &(map->head), &search, newent, ptr); - return NULL; - }); -#endif -} - -/** Return the current value associated with key, or NULL if no - * value is set. - */ -void * -strmap_get(const strmap_t *map, const char *key) -{ - strmap_entry_t *resolve; - strmap_entry_t search; - tor_assert(map); - tor_assert(key); - search.key = (char*)key; - resolve = HT_FIND(strmap_impl, &map->head, &search); - if (resolve) { - return resolve->val; - } else { - return NULL; - } -} - -/** Like strmap_get() above but for digestmaps. */ -void * -digestmap_get(const digestmap_t *map, const char *key) -{ - digestmap_entry_t *resolve; - digestmap_entry_t search; - tor_assert(map); - tor_assert(key); - memcpy(&search.key, key, DIGEST_LEN); - resolve = HT_FIND(digestmap_impl, &map->head, &search); - if (resolve) { - return resolve->val; - } else { - return NULL; - } -} - -/** Remove the value currently associated with key from the map. - * Return the value if one was set, or NULL if there was no entry for - * key. - * - * Note: you must free any storage associated with the returned value. - */ -void * -strmap_remove(strmap_t *map, const char *key) -{ - strmap_entry_t *resolve; - strmap_entry_t search; - void *oldval; - tor_assert(map); - tor_assert(key); - search.key = (char*)key; - resolve = HT_REMOVE(strmap_impl, &map->head, &search); - if (resolve) { - oldval = resolve->val; - tor_free(resolve->key); - tor_free(resolve); - return oldval; - } else { - return NULL; - } -} - -/** Like strmap_remove() above but for digestmaps. */ -void * -digestmap_remove(digestmap_t *map, const char *key) -{ - digestmap_entry_t *resolve; - digestmap_entry_t search; - void *oldval; - tor_assert(map); - tor_assert(key); - memcpy(&search.key, key, DIGEST_LEN); - resolve = HT_REMOVE(digestmap_impl, &map->head, &search); - if (resolve) { - oldval = resolve->val; - tor_free(resolve); - return oldval; - } else { - return NULL; - } -} - -/** Same as strmap_set, but first converts key to lowercase. */ -void * -strmap_set_lc(strmap_t *map, const char *key, void *val) -{ - /* We could be a little faster by using strcasecmp instead, and a separate - * type, but I don't think it matters. */ - void *v; - char *lc_key = tor_strdup(key); - tor_strlower(lc_key); - v = strmap_set(map,lc_key,val); - tor_free(lc_key); - return v; -} - -/** Same as strmap_get, but first converts key to lowercase. */ -void * -strmap_get_lc(const strmap_t *map, const char *key) -{ - void *v; - char *lc_key = tor_strdup(key); - tor_strlower(lc_key); - v = strmap_get(map,lc_key); - tor_free(lc_key); - return v; -} - -/** Same as strmap_remove, but first converts key to lowercase */ -void * -strmap_remove_lc(strmap_t *map, const char *key) -{ - void *v; - char *lc_key = tor_strdup(key); - tor_strlower(lc_key); - v = strmap_remove(map,lc_key); - tor_free(lc_key); - return v; -} - -/** return an iterator pointer to the front of a map. - * - * Iterator example: - * - * \code - * // uppercase values in "map", removing empty values. - * - * strmap_iter_t *iter; - * const char *key; - * void *val; - * char *cp; - * - * for (iter = strmap_iter_init(map); !strmap_iter_done(iter); ) { - * strmap_iter_get(iter, &key, &val); - * cp = (char*)val; - * if (!*cp) { - * iter = strmap_iter_next_rmv(map,iter); - * free(val); - * } else { - * for (;*cp;cp++) *cp = TOR_TOUPPER(*cp); - * iter = strmap_iter_next(map,iter); - * } - * } - * \endcode - * - */ -strmap_iter_t * -strmap_iter_init(strmap_t *map) -{ - tor_assert(map); - return HT_START(strmap_impl, &map->head); -} - -/** Start iterating through map. See strmap_iter_init() for example. */ -digestmap_iter_t * -digestmap_iter_init(digestmap_t *map) -{ - tor_assert(map); - return HT_START(digestmap_impl, &map->head); -} - -/** Advance the iterator iter for map a single step to the next - * entry, and return its new value. */ -strmap_iter_t * -strmap_iter_next(strmap_t *map, strmap_iter_t *iter) -{ - tor_assert(map); - tor_assert(iter); - return HT_NEXT(strmap_impl, &map->head, iter); -} - -/** Advance the iterator iter for map a single step to the next entry, - * and return its new value. */ -digestmap_iter_t * -digestmap_iter_next(digestmap_t *map, digestmap_iter_t *iter) -{ - tor_assert(map); - tor_assert(iter); - return HT_NEXT(digestmap_impl, &map->head, iter); -} - -/** Advance the iterator iter a single step to the next entry, removing - * the current entry, and return its new value. - */ -strmap_iter_t * -strmap_iter_next_rmv(strmap_t *map, strmap_iter_t *iter) -{ - strmap_entry_t *rmv; - tor_assert(map); - tor_assert(iter); - tor_assert(*iter); - rmv = *iter; - iter = HT_NEXT_RMV(strmap_impl, &map->head, iter); - tor_free(rmv->key); - tor_free(rmv); - return iter; -} - -/** Advance the iterator iter a single step to the next entry, removing - * the current entry, and return its new value. - */ -digestmap_iter_t * -digestmap_iter_next_rmv(digestmap_t *map, digestmap_iter_t *iter) -{ - digestmap_entry_t *rmv; - tor_assert(map); - tor_assert(iter); - tor_assert(*iter); - rmv = *iter; - iter = HT_NEXT_RMV(digestmap_impl, &map->head, iter); - tor_free(rmv); - return iter; -} - -/** Set *keyp and *valp to the current entry pointed to by - * iter. */ -void -strmap_iter_get(strmap_iter_t *iter, const char **keyp, void **valp) -{ - tor_assert(iter); - tor_assert(*iter); - tor_assert(keyp); - tor_assert(valp); - *keyp = (*iter)->key; - *valp = (*iter)->val; -} - -/** Set *keyp and *valp to the current entry pointed to by - * iter. */ -void -digestmap_iter_get(digestmap_iter_t *iter, const char **keyp, void **valp) -{ - tor_assert(iter); - tor_assert(*iter); - tor_assert(keyp); - tor_assert(valp); - *keyp = (*iter)->key; - *valp = (*iter)->val; -} - -/** Return true iff iter has advanced past the last entry of - * map. */ -int -strmap_iter_done(strmap_iter_t *iter) -{ - return iter == NULL; -} - -/** Return true iff iter has advanced past the last entry of - * map. */ -int -digestmap_iter_done(digestmap_iter_t *iter) -{ - return iter == NULL; -} - -/** Remove all entries from map, and deallocate storage for those - * entries. If free_val is provided, it is invoked on every value in - * map. - */ -void -strmap_free(strmap_t *map, void (*free_val)(void*)) -{ - strmap_entry_t **ent, **next, *this; - if (!map) - return; - - for (ent = HT_START(strmap_impl, &map->head); ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(strmap_impl, &map->head, ent); - tor_free(this->key); - if (free_val) - free_val(this->val); - tor_free(this); - } - tor_assert(HT_EMPTY(&map->head)); - HT_CLEAR(strmap_impl, &map->head); - tor_free(map); -} - -/** Remove all entries from map, and deallocate storage for those - * entries. If free_val is provided, it is invoked on every value in - * map. - */ -void -digestmap_free(digestmap_t *map, void (*free_val)(void*)) -{ - digestmap_entry_t **ent, **next, *this; - if (!map) - return; - for (ent = HT_START(digestmap_impl, &map->head); ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(digestmap_impl, &map->head, ent); - if (free_val) - free_val(this->val); - tor_free(this); - } - tor_assert(HT_EMPTY(&map->head)); - HT_CLEAR(digestmap_impl, &map->head); - tor_free(map); -} - -/** Fail with an assertion error if anything has gone wrong with the internal - * representation of map. */ -void -strmap_assert_ok(const strmap_t *map) -{ - tor_assert(!strmap_impl_HT_REP_IS_BAD_(&map->head)); -} -/** Fail with an assertion error if anything has gone wrong with the internal - * representation of map. */ -void -digestmap_assert_ok(const digestmap_t *map) -{ - tor_assert(!digestmap_impl_HT_REP_IS_BAD_(&map->head)); -} - -/** Return true iff map has no entries. */ -int -strmap_isempty(const strmap_t *map) -{ - return HT_EMPTY(&map->head); -} - -/** Return true iff map has no entries. */ -int -digestmap_isempty(const digestmap_t *map) -{ - return HT_EMPTY(&map->head); -} - -/** Return the number of items in map. */ -int -strmap_size(const strmap_t *map) -{ - return HT_SIZE(&map->head); -} - -/** Return the number of items in map. */ -int -digestmap_size(const digestmap_t *map) -{ - return HT_SIZE(&map->head); -} - -/** Declare a function called funcname that acts as a find_nth_FOO - * function for an array of type elt_t*. - * - * NOTE: The implementation kind of sucks: It's O(n log n), whereas finding - * the kth element of an n-element list can be done in O(n). Then again, this - * implementation is not in critical path, and it is obviously correct. */ -#define IMPLEMENT_ORDER_FUNC(funcname, elt_t) \ - static int \ - _cmp_ ## elt_t(const void *_a, const void *_b) \ - { \ - const elt_t *a = _a, *b = _b; \ - if (*a<*b) \ - return -1; \ - else if (*a>*b) \ - return 1; \ - else \ - return 0; \ - } \ - elt_t \ - funcname(elt_t *array, int n_elements, int nth) \ - { \ - tor_assert(nth >= 0); \ - tor_assert(nth < n_elements); \ - qsort(array, n_elements, sizeof(elt_t), _cmp_ ##elt_t); \ - return array[nth]; \ - } - -IMPLEMENT_ORDER_FUNC(find_nth_int, int) -IMPLEMENT_ORDER_FUNC(find_nth_time, time_t) -IMPLEMENT_ORDER_FUNC(find_nth_double, double) -IMPLEMENT_ORDER_FUNC(find_nth_uint32, uint32_t) -IMPLEMENT_ORDER_FUNC(find_nth_int32, int32_t) -IMPLEMENT_ORDER_FUNC(find_nth_long, long) - -/** Return a newly allocated digestset_t, optimized to hold a total of - * max_elements digests with a reasonably low false positive weight. */ -digestset_t * -digestset_new(int max_elements) -{ - /* The probability of false positives is about P=(1 - exp(-kn/m))^k, where k - * is the number of hash functions per entry, m is the bits in the array, - * and n is the number of elements inserted. For us, k==4, n<=max_elements, - * and m==n_bits= approximately max_elements*32. This gives - * P<(1-exp(-4*n/(32*n)))^4 == (1-exp(1/-8))^4 == .00019 - * - * It would be more optimal in space vs false positives to get this false - * positive rate by going for k==13, and m==18.5n, but we also want to - * conserve CPU, and k==13 is pretty big. - */ - int n_bits = 1u << (tor_log2(max_elements)+5); - digestset_t *r = tor_malloc(sizeof(digestset_t)); - r->mask = n_bits - 1; - r->ba = bitarray_init_zero(n_bits); - return r; -} - -/** Free all storage held in set. */ -void -digestset_free(digestset_t *set) -{ - if (!set) - return; - bitarray_free(set->ba); - tor_free(set); -} - diff --git a/src/tor/container.h b/src/tor/container.h deleted file mode 100644 index 9b92aaa..0000000 --- a/src/tor/container.h +++ /dev/null @@ -1,681 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_CONTAINER_H -#define TOR_CONTAINER_H - -#include "tor_util.h" - -/** A resizeable list of pointers, with associated helpful functionality. - * - * The members of this struct are exposed only so that macros and inlines can - * use them; all access to smartlist internals should go through the functions - * and macros defined here. - **/ -typedef struct smartlist_t { - /** @{ */ - /** list has enough capacity to store exactly capacity elements - * before it needs to be resized. Only the first num_used (\<= - * capacity) elements point to valid data. - */ - void **list; - int num_used; - int capacity; - /** @} */ -} smartlist_t; - -smartlist_t *smartlist_new(void); -void smartlist_free(smartlist_t *sl); -void smartlist_clear(smartlist_t *sl); -void smartlist_add(smartlist_t *sl, void *element); -void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2); -void smartlist_remove(smartlist_t *sl, const void *element); -void *smartlist_pop_last(smartlist_t *sl); -void smartlist_reverse(smartlist_t *sl); -void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_contains(const smartlist_t *sl, const void *element); -int smartlist_contains_string(const smartlist_t *sl, const char *element); -int smartlist_string_pos(const smartlist_t *, const char *elt); -int smartlist_contains_string_case(const smartlist_t *sl, const char *element); -int smartlist_contains_int_as_string(const smartlist_t *sl, int num); -int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); -int smartlist_contains_digest(const smartlist_t *sl, const char *element); -int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2); -int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); -void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); -void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); - -/* smartlist_choose() is defined in crypto.[ch] */ -#ifdef DEBUG_SMARTLIST -/** Return the number of items in sl. - */ -static INLINE int smartlist_len(const smartlist_t *sl); -static INLINE int smartlist_len(const smartlist_t *sl) { - tor_assert(sl); - return (sl)->num_used; -} -/** Return the idxth element of sl. - */ -static INLINE void *smartlist_get(const smartlist_t *sl, int idx); -static INLINE void *smartlist_get(const smartlist_t *sl, int idx) { - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(sl->num_used > idx); - return sl->list[idx]; -} -static INLINE void smartlist_set(smartlist_t *sl, int idx, void *val) { - tor_assert(sl); - tor_assert(idx>=0); - tor_assert(sl->num_used > idx); - sl->list[idx] = val; -} -#else -#define smartlist_len(sl) ((sl)->num_used) -#define smartlist_get(sl, idx) ((sl)->list[idx]) -#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val)) -#endif - -/** Exchange the elements at indices idx1 and idx2 of the - * smartlist sl. */ -static INLINE void smartlist_swap(smartlist_t *sl, int idx1, int idx2) -{ - if (idx1 != idx2) { - void *elt = smartlist_get(sl, idx1); - smartlist_set(sl, idx1, smartlist_get(sl, idx2)); - smartlist_set(sl, idx2, elt); - } -} - -void smartlist_del(smartlist_t *sl, int idx); -void smartlist_del_keeporder(smartlist_t *sl, int idx); -void smartlist_insert(smartlist_t *sl, int idx, void *val); -void smartlist_sort(smartlist_t *sl, - int (*compare)(const void **a, const void **b)); -void *smartlist_get_most_frequent(const smartlist_t *sl, - int (*compare)(const void **a, const void **b)); -void smartlist_uniq(smartlist_t *sl, - int (*compare)(const void **a, const void **b), - void (*free_fn)(void *elt)); - -void smartlist_sort_strings(smartlist_t *sl); -void smartlist_sort_digests(smartlist_t *sl); -void smartlist_sort_digests256(smartlist_t *sl); - -char *smartlist_get_most_frequent_string(smartlist_t *sl); -char *smartlist_get_most_frequent_digest256(smartlist_t *sl); - -void smartlist_uniq_strings(smartlist_t *sl); -void smartlist_uniq_digests(smartlist_t *sl); -void smartlist_uniq_digests256(smartlist_t *sl); -void *smartlist_bsearch(smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member)); -int smartlist_bsearch_idx(const smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member), - int *found_out); - -void smartlist_pqueue_add(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset, - void *item); -void *smartlist_pqueue_pop(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset); -void smartlist_pqueue_remove(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset, - void *item); -void smartlist_pqueue_assert_ok(smartlist_t *sl, - int (*compare)(const void *a, const void *b), - int idx_field_offset); - -#define SPLIT_SKIP_SPACE 0x01 -#define SPLIT_IGNORE_BLANK 0x02 -#define SPLIT_STRIP_SPACE 0x04 -int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, - int flags, int max); -char *smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, - size_t *len_out) ATTR_MALLOC; -char *smartlist_join_strings2(smartlist_t *sl, const char *join, - size_t join_len, int terminate, size_t *len_out) - ATTR_MALLOC; - -/** Iterate over the items in a smartlist sl, in order. For each item, - * assign it to a new local variable of type type named var, and - * execute the statements inside the loop body. Inside the loop, the loop - * index can be accessed as var_sl_idx and the length of the list can - * be accessed as var_sl_len. - * - * NOTE: Do not change the length of the list while the loop is in progress, - * unless you adjust the _sl_len variable correspondingly. See second example - * below. - * - * Example use: - *
- *   smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
- *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
- *     printf("%d: %s\n", cp_sl_idx, cp);
- *     tor_free(cp);
- *   } SMARTLIST_FOREACH_END(cp);
- *   smartlist_free(list);
- * 
- * - * Example use (advanced): - *
- *   SMARTLIST_FOREACH_BEGIN(list, char *, cp) {
- *     if (!strcmp(cp, "junk")) {
- *       tor_free(cp);
- *       SMARTLIST_DEL_CURRENT(list, cp);
- *     }
- *   } SMARTLIST_FOREACH_END(cp);
- * 
- */ -/* Note: these macros use token pasting, and reach into smartlist internals. - * This can make them a little daunting. Here's the approximate unpacking of - * the above examples, for entertainment value: - * - *
- * smartlist_t *list = smartlist_split("A:B:C", ":", 0, 0);
- * {
- *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
- *   char *cp;
- *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
- *     cp = smartlist_get(list, cp_sl_idx);
- *     printf("%d: %s\n", cp_sl_idx, cp);
- *     tor_free(cp);
- *   }
- * }
- * smartlist_free(list);
- * 
- * - *
- * {
- *   int cp_sl_idx, cp_sl_len = smartlist_len(list);
- *   char *cp;
- *   for (cp_sl_idx = 0; cp_sl_idx < cp_sl_len; ++cp_sl_idx) {
- *     cp = smartlist_get(list, cp_sl_idx);
- *     if (!strcmp(cp, "junk")) {
- *       tor_free(cp);
- *       smartlist_del(list, cp_sl_idx);
- *       --cp_sl_idx;
- *       --cp_sl_len;
- *     }
- *   }
- * }
- * 
- */ -#define SMARTLIST_FOREACH_BEGIN(sl, type, var) \ - STMT_BEGIN \ - int var ## _sl_idx, var ## _sl_len=(sl)->num_used; \ - type var; \ - for (var ## _sl_idx = 0; var ## _sl_idx < var ## _sl_len; \ - ++var ## _sl_idx) { \ - var = (sl)->list[var ## _sl_idx]; - -#define SMARTLIST_FOREACH_END(var) \ - var = NULL; \ - } STMT_END - -/** - * An alias for SMARTLIST_FOREACH_BEGIN and SMARTLIST_FOREACH_END, using - * cmd as the loop body. This wrapper is here for convenience with - * very short loops. - * - * By convention, we do not use this for loops which nest, or for loops over - * 10 lines or so. Use SMARTLIST_FOREACH_{BEGIN,END} for those. - */ -#define SMARTLIST_FOREACH(sl, type, var, cmd) \ - SMARTLIST_FOREACH_BEGIN(sl,type,var) { \ - cmd; \ - } SMARTLIST_FOREACH_END(var) - -/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed - * with the variable var, remove the current element in a way that - * won't confuse the loop. */ -#define SMARTLIST_DEL_CURRENT(sl, var) \ - STMT_BEGIN \ - smartlist_del(sl, var ## _sl_idx); \ - --var ## _sl_idx; \ - --var ## _sl_len; \ - STMT_END - -/** Helper: While in a SMARTLIST_FOREACH loop over the list sl indexed - * with the variable var, replace the current element with val. - * Does not deallocate the current value of var. - */ -#define SMARTLIST_REPLACE_CURRENT(sl, var, val) \ - STMT_BEGIN \ - smartlist_set(sl, var ## _sl_idx, val); \ - STMT_END - -/* Helper: Given two lists of items, possibly of different types, such that - * both lists are sorted on some common field (as determined by a comparison - * expression cmpexpr), and such that one list (sl1) has no - * duplicates on the common field, loop through the lists in lockstep, and - * execute unmatched_var2 on items in var2 that do not appear in - * var1. - * - * WARNING: It isn't safe to add remove elements from either list while the - * loop is in progress. - * - * Example use: - * SMARTLIST_FOREACH_JOIN(routerstatus_list, routerstatus_t *, rs, - * routerinfo_list, routerinfo_t *, ri, - * tor_memcmp(rs->identity_digest, ri->identity_digest, 20), - * log_info(LD_GENERAL,"No match for %s", ri->nickname)) { - * log_info(LD_GENERAL, "%s matches routerstatus %p", ri->nickname, rs); - * } SMARTLIST_FOREACH_JOIN_END(rs, ri); - **/ -/* The example above unpacks (approximately) to: - * int rs_sl_idx = 0, rs_sl_len = smartlist_len(routerstatus_list); - * int ri_sl_idx, ri_sl_len = smartlist_len(routerinfo_list); - * int rs_ri_cmp; - * routerstatus_t *rs; - * routerinfo_t *ri; - * for (; ri_sl_idx < ri_sl_len; ++ri_sl_idx) { - * ri = smartlist_get(routerinfo_list, ri_sl_idx); - * while (rs_sl_idx < rs_sl_len) { - * rs = smartlist_get(routerstatus_list, rs_sl_idx); - * rs_ri_cmp = tor_memcmp(rs->identity_digest, ri->identity_digest, 20); - * if (rs_ri_cmp > 0) { - * break; - * } else if (rs_ri_cmp == 0) { - * goto matched_ri; - * } else { - * ++rs_sl_idx; - * } - * } - * log_info(LD_GENERAL,"No match for %s", ri->nickname); - * continue; - * matched_ri: { - * log_info(LD_GENERAL,"%s matches with routerstatus %p",ri->nickname,rs); - * } - * } - */ -#define SMARTLIST_FOREACH_JOIN(sl1, type1, var1, sl2, type2, var2, \ - cmpexpr, unmatched_var2) \ - STMT_BEGIN \ - int var1 ## _sl_idx = 0, var1 ## _sl_len=(sl1)->num_used; \ - int var2 ## _sl_idx = 0, var2 ## _sl_len=(sl2)->num_used; \ - int var1 ## _ ## var2 ## _cmp; \ - type1 var1; \ - type2 var2; \ - for (; var2##_sl_idx < var2##_sl_len; ++var2##_sl_idx) { \ - var2 = (sl2)->list[var2##_sl_idx]; \ - while (var1##_sl_idx < var1##_sl_len) { \ - var1 = (sl1)->list[var1##_sl_idx]; \ - var1##_##var2##_cmp = (cmpexpr); \ - if (var1##_##var2##_cmp > 0) { \ - break; \ - } else if (var1##_##var2##_cmp == 0) { \ - goto matched_##var2; \ - } else { \ - ++var1##_sl_idx; \ - } \ - } \ - /* Ran out of v1, or no match for var2. */ \ - unmatched_var2; \ - continue; \ - matched_##var2: ; \ - -#define SMARTLIST_FOREACH_JOIN_END(var1, var2) \ - } \ - STMT_END - -#define DECLARE_MAP_FNS(maptype, keytype, prefix) \ - typedef struct maptype maptype; \ - typedef struct prefix##entry_t *prefix##iter_t; \ - maptype* prefix##new(void); \ - void* prefix##set(maptype *map, keytype key, void *val); \ - void* prefix##get(const maptype *map, keytype key); \ - void* prefix##remove(maptype *map, keytype key); \ - void prefix##free(maptype *map, void (*free_val)(void*)); \ - int prefix##isempty(const maptype *map); \ - int prefix##size(const maptype *map); \ - prefix##iter_t *prefix##iter_init(maptype *map); \ - prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter); \ - prefix##iter_t *prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter); \ - void prefix##iter_get(prefix##iter_t *iter, keytype *keyp, void **valp); \ - int prefix##iter_done(prefix##iter_t *iter); \ - void prefix##assert_ok(const maptype *map) - -/* Map from const char * to void *. Implemented with a hash table. */ -DECLARE_MAP_FNS(strmap_t, const char *, strmap_); -/* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */ -DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_); - -#undef DECLARE_MAP_FNS - -/** Iterates over the key-value pairs in a map map in order. - * prefix is as for DECLARE_MAP_FNS (i.e., strmap_ or digestmap_). - * The map's keys and values are of type keytype and valtype respectively; - * each iteration assigns them to keyvar and valvar. - * - * Example use: - * MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) { - * // use k and r - * } MAP_FOREACH_END. - */ -/* Unpacks to, approximately: - * { - * digestmap_iter_t *k_iter; - * for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter); - * k_iter = digestmap_iter_next(m, k_iter)) { - * const char *k; - * void *r_voidp; - * routerinfo_t *r; - * digestmap_iter_get(k_iter, &k, &r_voidp); - * r = r_voidp; - * // use k and r - * } - * } - */ -#define MAP_FOREACH(prefix, map, keytype, keyvar, valtype, valvar) \ - STMT_BEGIN \ - prefix##iter_t *keyvar##_iter; \ - for (keyvar##_iter = prefix##iter_init(map); \ - !prefix##iter_done(keyvar##_iter); \ - keyvar##_iter = prefix##iter_next(map, keyvar##_iter)) { \ - keytype keyvar; \ - void *valvar##_voidp; \ - valtype valvar; \ - prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ - valvar = valvar##_voidp; - -/** As MAP_FOREACH, except allows members to be removed from the map - * during the iteration via MAP_DEL_CURRENT. Example use: - * - * Example use: - * MAP_FOREACH(digestmap_, m, const char *, k, routerinfo_t *, r) { - * if (is_very_old(r)) - * MAP_DEL_CURRENT(k); - * } MAP_FOREACH_END. - **/ -/* Unpacks to, approximately: - * { - * digestmap_iter_t *k_iter; - * int k_del=0; - * for (k_iter = digestmap_iter_init(m); !digestmap_iter_done(k_iter); - * k_iter = k_del ? digestmap_iter_next(m, k_iter) - * : digestmap_iter_next_rmv(m, k_iter)) { - * const char *k; - * void *r_voidp; - * routerinfo_t *r; - * k_del=0; - * digestmap_iter_get(k_iter, &k, &r_voidp); - * r = r_voidp; - * if (is_very_old(r)) { - * k_del = 1; - * } - * } - * } - */ -#define MAP_FOREACH_MODIFY(prefix, map, keytype, keyvar, valtype, valvar) \ - STMT_BEGIN \ - prefix##iter_t *keyvar##_iter; \ - int keyvar##_del=0; \ - for (keyvar##_iter = prefix##iter_init(map); \ - !prefix##iter_done(keyvar##_iter); \ - keyvar##_iter = keyvar##_del ? \ - prefix##iter_next_rmv(map, keyvar##_iter) : \ - prefix##iter_next(map, keyvar##_iter)) { \ - keytype keyvar; \ - void *valvar##_voidp; \ - valtype valvar; \ - keyvar##_del=0; \ - prefix##iter_get(keyvar##_iter, &keyvar, &valvar##_voidp); \ - valvar = valvar##_voidp; - -/** Used with MAP_FOREACH_MODIFY to remove the currently-iterated-upon - * member of the map. */ -#define MAP_DEL_CURRENT(keyvar) \ - STMT_BEGIN \ - keyvar##_del = 1; \ - STMT_END - -/** Used to end a MAP_FOREACH() block. */ -#define MAP_FOREACH_END } STMT_END ; - -/** As MAP_FOREACH, but does not require declaration of prefix or keytype. - * Example use: - * DIGESTMAP_FOREACH(m, k, routerinfo_t *, r) { - * // use k and r - * } DIGESTMAP_FOREACH_END. - */ -#define DIGESTMAP_FOREACH(map, keyvar, valtype, valvar) \ - MAP_FOREACH(digestmap_, map, const char *, keyvar, valtype, valvar) - -/** As MAP_FOREACH_MODIFY, but does not require declaration of prefix or - * keytype. - * Example use: - * DIGESTMAP_FOREACH_MODIFY(m, k, routerinfo_t *, r) { - * if (is_very_old(r)) - * MAP_DEL_CURRENT(k); - * } DIGESTMAP_FOREACH_END. - */ -#define DIGESTMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \ - MAP_FOREACH_MODIFY(digestmap_, map, const char *, keyvar, valtype, valvar) -/** Used to end a DIGESTMAP_FOREACH() block. */ -#define DIGESTMAP_FOREACH_END MAP_FOREACH_END - -#define STRMAP_FOREACH(map, keyvar, valtype, valvar) \ - MAP_FOREACH(strmap_, map, const char *, keyvar, valtype, valvar) -#define STRMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \ - MAP_FOREACH_MODIFY(strmap_, map, const char *, keyvar, valtype, valvar) -#define STRMAP_FOREACH_END MAP_FOREACH_END - -void* strmap_set_lc(strmap_t *map, const char *key, void *val); -void* strmap_get_lc(const strmap_t *map, const char *key); -void* strmap_remove_lc(strmap_t *map, const char *key); - -#define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype) \ - typedef struct maptype maptype; \ - typedef struct prefix##iter_t prefix##iter_t; \ - static INLINE maptype* prefix##new(void) \ - { \ - return (maptype*)digestmap_new(); \ - } \ - static INLINE digestmap_t* prefix##to_digestmap(maptype *map) \ - { \ - return (digestmap_t*)map; \ - } \ - static INLINE valtype* prefix##get(maptype *map, const char *key) \ - { \ - return (valtype*)digestmap_get((digestmap_t*)map, key); \ - } \ - static INLINE valtype* prefix##set(maptype *map, const char *key, \ - valtype *val) \ - { \ - return (valtype*)digestmap_set((digestmap_t*)map, key, val); \ - } \ - static INLINE valtype* prefix##remove(maptype *map, const char *key) \ - { \ - return (valtype*)digestmap_remove((digestmap_t*)map, key); \ - } \ - static INLINE void prefix##free(maptype *map, void (*free_val)(void*)) \ - { \ - digestmap_free((digestmap_t*)map, free_val); \ - } \ - static INLINE int prefix##isempty(maptype *map) \ - { \ - return digestmap_isempty((digestmap_t*)map); \ - } \ - static INLINE int prefix##size(maptype *map) \ - { \ - return digestmap_size((digestmap_t*)map); \ - } \ - static INLINE prefix##iter_t *prefix##iter_init(maptype *map) \ - { \ - return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \ - } \ - static INLINE prefix##iter_t *prefix##iter_next(maptype *map, \ - prefix##iter_t *iter) \ - { \ - return (prefix##iter_t*) digestmap_iter_next( \ - (digestmap_t*)map, (digestmap_iter_t*)iter); \ - } \ - static INLINE prefix##iter_t *prefix##iter_next_rmv(maptype *map, \ - prefix##iter_t *iter) \ - { \ - return (prefix##iter_t*) digestmap_iter_next_rmv( \ - (digestmap_t*)map, (digestmap_iter_t*)iter); \ - } \ - static INLINE void prefix##iter_get(prefix##iter_t *iter, \ - const char **keyp, \ - valtype **valp) \ - { \ - void *v; \ - digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v); \ - *valp = v; \ - } \ - static INLINE int prefix##iter_done(prefix##iter_t *iter) \ - { \ - return digestmap_iter_done((digestmap_iter_t*)iter); \ - } - -#if SIZEOF_INT == 4 -#define BITARRAY_SHIFT 5 -#elif SIZEOF_INT == 8 -#define BITARRAY_SHIFT 6 -#else -#error "int is neither 4 nor 8 bytes. I can't deal with that." -#endif -#define BITARRAY_MASK ((1u<n_bits
bits. */ -static INLINE bitarray_t * -bitarray_init_zero(unsigned int n_bits) -{ - /* round up to the next int. */ - size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT; - return tor_malloc_zero(sz*sizeof(unsigned int)); -} -/** Expand ba from holding n_bits_old to n_bits_new, - * clearing all new bits. Returns a possibly changed pointer to the - * bitarray. */ -static INLINE bitarray_t * -bitarray_expand(bitarray_t *ba, - unsigned int n_bits_old, unsigned int n_bits_new) -{ - size_t sz_old = (n_bits_old+BITARRAY_MASK) >> BITARRAY_SHIFT; - size_t sz_new = (n_bits_new+BITARRAY_MASK) >> BITARRAY_SHIFT; - char *ptr; - if (sz_new <= sz_old) - return ba; - ptr = tor_realloc(ba, sz_new*sizeof(unsigned int)); - /* This memset does nothing to the older excess bytes. But they were - * already set to 0 by bitarry_init_zero. */ - memset(ptr+sz_old*sizeof(unsigned int), 0, - (sz_new-sz_old)*sizeof(unsigned int)); - return (bitarray_t*) ptr; -} -/** Free the bit array ba. */ -static INLINE void -bitarray_free(bitarray_t *ba) -{ - tor_free(ba); -} -/** Set the bitth bit in b to 1. */ -static INLINE void -bitarray_set(bitarray_t *b, int bit) -{ - b[bit >> BITARRAY_SHIFT] |= (1u << (bit & BITARRAY_MASK)); -} -/** Set the bitth bit in b to 0. */ -static INLINE void -bitarray_clear(bitarray_t *b, int bit) -{ - b[bit >> BITARRAY_SHIFT] &= ~ (1u << (bit & BITARRAY_MASK)); -} -/** Return true iff bitth bit in b is nonzero. NOTE: does - * not necessarily return 1 on true. */ -static INLINE unsigned int -bitarray_is_set(bitarray_t *b, int bit) -{ - return b[bit >> BITARRAY_SHIFT] & (1u << (bit & BITARRAY_MASK)); -} - -/** A set of digests, implemented as a Bloom filter. */ -typedef struct { - int mask; /**< One less than the number of bits in ba; always one less - * than a power of two. */ - bitarray_t *ba; /**< A bit array to implement the Bloom filter. */ -} digestset_t; - -#define BIT(n) ((n) & set->mask) -/** Add the digest digest to set. */ -static INLINE void -digestset_add(digestset_t *set, const char *digest) -{ - const uint32_t *p = (const uint32_t *)digest; - const uint32_t d1 = p[0] + (p[1]>>16); - const uint32_t d2 = p[1] + (p[2]>>16); - const uint32_t d3 = p[2] + (p[3]>>16); - const uint32_t d4 = p[3] + (p[0]>>16); - bitarray_set(set->ba, BIT(d1)); - bitarray_set(set->ba, BIT(d2)); - bitarray_set(set->ba, BIT(d3)); - bitarray_set(set->ba, BIT(d4)); -} - -/** If digest is in set, return nonzero. Otherwise, - * probably return zero. */ -static INLINE int -digestset_contains(const digestset_t *set, const char *digest) -{ - const uint32_t *p = (const uint32_t *)digest; - const uint32_t d1 = p[0] + (p[1]>>16); - const uint32_t d2 = p[1] + (p[2]>>16); - const uint32_t d3 = p[2] + (p[3]>>16); - const uint32_t d4 = p[3] + (p[0]>>16); - return bitarray_is_set(set->ba, BIT(d1)) && - bitarray_is_set(set->ba, BIT(d2)) && - bitarray_is_set(set->ba, BIT(d3)) && - bitarray_is_set(set->ba, BIT(d4)); -} -#undef BIT - -digestset_t *digestset_new(int max_elements); -void digestset_free(digestset_t* set); - -/* These functions, given an array of n_elements, return the - * nth lowest element. nth=0 gives the lowest element; - * n_elements-1 gives the highest; and (n_elements-1) / 2 gives - * the median. As a side effect, the elements of array are sorted. */ -int find_nth_int(int *array, int n_elements, int nth); -time_t find_nth_time(time_t *array, int n_elements, int nth); -double find_nth_double(double *array, int n_elements, int nth); -int32_t find_nth_int32(int32_t *array, int n_elements, int nth); -uint32_t find_nth_uint32(uint32_t *array, int n_elements, int nth); -long find_nth_long(long *array, int n_elements, int nth); -static INLINE int -median_int(int *array, int n_elements) -{ - return find_nth_int(array, n_elements, (n_elements-1)/2); -} -static INLINE time_t -median_time(time_t *array, int n_elements) -{ - return find_nth_time(array, n_elements, (n_elements-1)/2); -} -static INLINE double -median_double(double *array, int n_elements) -{ - return find_nth_double(array, n_elements, (n_elements-1)/2); -} -static INLINE uint32_t -median_uint32(uint32_t *array, int n_elements) -{ - return find_nth_uint32(array, n_elements, (n_elements-1)/2); -} -static INLINE int32_t -median_int32(int32_t *array, int n_elements) -{ - return find_nth_int32(array, n_elements, (n_elements-1)/2); -} - -#endif - diff --git a/src/tor/control.c b/src/tor/control.c deleted file mode 100644 index cba8a69..0000000 --- a/src/tor/control.c +++ /dev/null @@ -1,5110 +0,0 @@ -/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file control.c - * \brief Implementation for Tor's control-socket interface. - * See doc/spec/control-spec.txt for full details on protocol. - **/ - -#define CONTROL_PRIVATE - -#include "or.h" -#include "addressmap.h" -#include "buffers.h" -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuitstats.h" -#include "circuituse.h" -#include "command.h" -#include "config.h" -#include "confparse.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "directory.h" -#include "dirserv.h" -#include "dnsserv.h" -#include "entrynodes.h" -#include "geoip.h" -#include "hibernate.h" -#include "onion_main.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "reasons.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" - -#ifndef _WIN32 -#include -#include -#endif - -#include "procmon.h" - -/** Yield true iff s is the state of a control_connection_t that has - * finished authentication and is accepting commands. */ -#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN) - -/** Bitfield: The bit 1<<e is set if any open control - * connection is interested in events of type e. We use this - * so that we can decide to skip generating event messages that nobody - * has interest in without having to walk over the global connection - * list to find out. - **/ -typedef uint64_t event_mask_t; - -/** An event mask of all the events that any controller is interested in - * receiving. */ -static event_mask_t global_event_mask = 0; - -/** True iff we have disabled log messages from being sent to the controller */ -static int disable_log_messages = 0; - -/** Macro: true if any control connection is interested in events of type - * e. */ -#define EVENT_IS_INTERESTING(e) \ - (!! (global_event_mask & (((uint64_t)1)<<(e)))) - -/** If we're using cookie-type authentication, how long should our cookies be? - */ -#define AUTHENTICATION_COOKIE_LEN 32 - -/** If true, we've set authentication_cookie to a secret code and - * stored it to disk. */ -static int authentication_cookie_is_set = 0; -/** If authentication_cookie_is_set, a secret cookie that we've stored to disk - * and which we're using to authenticate controllers. (If the controller can - * read it off disk, it has permission to connect.) */ -static uint8_t *authentication_cookie = NULL; - -#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \ - "Tor safe cookie authentication server-to-controller hash" -#define SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT \ - "Tor safe cookie authentication controller-to-server hash" -#define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN - -/** A sufficiently large size to record the last bootstrap phase string. */ -#define BOOTSTRAP_MSG_LEN 1024 - -/** What was the last bootstrap phase message we sent? We keep track - * of this so we can respond to getinfo status/bootstrap-phase queries. */ -static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN]; - -static void connection_printf_to_buf(control_connection_t *conn, - const char *format, ...) - CHECK_PRINTF(2,3); -static void send_control_event_impl(uint16_t event, event_format_t which, - const char *format, va_list ap) - CHECK_PRINTF(3,0); -static int control_event_status(int type, int severity, const char *format, - va_list args) - CHECK_PRINTF(3,0); - -static void send_control_done(control_connection_t *conn); -static void send_control_event(uint16_t event, event_format_t which, - const char *format, ...) - CHECK_PRINTF(3,4); -static int handle_control_setconf(control_connection_t *conn, uint32_t len, - char *body); -static int handle_control_resetconf(control_connection_t *conn, uint32_t len, - char *body); -static int handle_control_getconf(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_loadconf(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_setevents(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_authenticate(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_signal(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_mapaddress(control_connection_t *conn, uint32_t len, - const char *body); -static char *list_getinfo_options(void); -static int handle_control_getinfo(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_extendcircuit(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_setcircuitpurpose(control_connection_t *conn, - uint32_t len, const char *body); -static int handle_control_attachstream(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_postdescriptor(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_redirectstream(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_closestream(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_closecircuit(control_connection_t *conn, - uint32_t len, - const char *body); -static int handle_control_resolve(control_connection_t *conn, uint32_t len, - const char *body); -static int handle_control_usefeature(control_connection_t *conn, - uint32_t len, - const char *body); -static int write_stream_target_to_buf(entry_connection_t *conn, char *buf, - size_t len); -static void orconn_target_get_name(char *buf, size_t len, - or_connection_t *conn); -static char *get_cookie_file(void); - -/** Given a control event code for a message event, return the corresponding - * log severity. */ -static INLINE int -event_to_log_severity(int event) -{ - switch (event) { - case EVENT_DEBUG_MSG: return LOG_DEBUG; - case EVENT_INFO_MSG: return LOG_INFO; - case EVENT_NOTICE_MSG: return LOG_NOTICE; - case EVENT_WARN_MSG: return LOG_WARN; - case EVENT_ERR_MSG: return LOG_ERR; - default: return -1; - } -} - -/** Given a log severity, return the corresponding control event code. */ -static INLINE int -log_severity_to_event(int severity) -{ - switch (severity) { - case LOG_DEBUG: return EVENT_DEBUG_MSG; - case LOG_INFO: return EVENT_INFO_MSG; - case LOG_NOTICE: return EVENT_NOTICE_MSG; - case LOG_WARN: return EVENT_WARN_MSG; - case LOG_ERR: return EVENT_ERR_MSG; - default: return -1; - } -} - -/** Helper: clear bandwidth counters of all origin circuits. */ -static void -clear_circ_bw_fields(void) -{ - circuit_t *circ; - origin_circuit_t *ocirc; - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - ocirc = TO_ORIGIN_CIRCUIT(circ); - ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0; - } -} - -/** Set global_event_mask* to the bitwise OR of each live control - * connection's event_mask field. */ -void -control_update_global_event_mask(void) -{ - smartlist_t *conns = get_connection_array(); - event_mask_t old_mask, new_mask; - old_mask = global_event_mask; - - global_event_mask = 0; - SMARTLIST_FOREACH(conns, connection_t *, _conn, - { - if (_conn->type == CONN_TYPE_CONTROL && - STATE_IS_OPEN(_conn->state)) { - control_connection_t *conn = TO_CONTROL_CONN(_conn); - global_event_mask |= conn->event_mask; - } - }); - - new_mask = global_event_mask; - - /* Handle the aftermath. Set up the log callback to tell us only what - * we want to hear...*/ - control_adjust_event_log_severity(); - - /* ...then, if we've started logging stream or circ bw, clear the - * appropriate fields. */ - if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) && - (new_mask & EVENT_STREAM_BANDWIDTH_USED)) { - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - edge_conn->n_written = edge_conn->n_read = 0; - } - }); - } - if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) && - (new_mask & EVENT_CIRC_BANDWIDTH_USED)) { - clear_circ_bw_fields(); - } -} - -/** Adjust the log severities that result in control_event_logmsg being called - * to match the severity of log messages that any controllers are interested - * in. */ -void -control_adjust_event_log_severity(void) -{ - int i; - int min_log_event=EVENT_ERR_MSG, max_log_event=EVENT_DEBUG_MSG; - - for (i = EVENT_DEBUG_MSG; i <= EVENT_ERR_MSG; ++i) { - if (EVENT_IS_INTERESTING(i)) { - min_log_event = i; - break; - } - } - for (i = EVENT_ERR_MSG; i >= EVENT_DEBUG_MSG; --i) { - if (EVENT_IS_INTERESTING(i)) { - max_log_event = i; - break; - } - } - if (EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL)) { - if (min_log_event > EVENT_NOTICE_MSG) - min_log_event = EVENT_NOTICE_MSG; - if (max_log_event < EVENT_ERR_MSG) - max_log_event = EVENT_ERR_MSG; - } - if (min_log_event <= max_log_event) - change_callback_log_severity(event_to_log_severity(min_log_event), - event_to_log_severity(max_log_event), - control_event_logmsg); - else - change_callback_log_severity(LOG_ERR, LOG_ERR, - control_event_logmsg); -} - -/** Return true iff the event with code c is being sent to any current - * control connection. This is useful if the amount of work needed to prepare - * to call the appropriate control_event_...() function is high. - */ -int -control_event_is_interesting(int event) -{ - return EVENT_IS_INTERESTING(event); -} - -/** Append a NUL-terminated string s to the end of - * conn-\>outbuf. - */ -static INLINE void -connection_write_str_to_buf(const char *s, control_connection_t *conn) -{ - size_t len = strlen(s); - connection_write_to_buf(s, len, TO_CONN(conn)); -} - -/** Given a len-character string in data, made of lines - * terminated by CRLF, allocate a new string in *out, and copy the - * contents of data into *out, adding a period before any period - * that appears at the start of a line, and adding a period-CRLF line at - * the end. Replace all LF characters sequences with CRLF. Return the number - * of bytes in *out. - */ -STATIC size_t -write_escaped_data(const char *data, size_t len, char **out) -{ - size_t sz_out = len+8; - char *outp; - const char *start = data, *end; - int i; - int start_of_line; - for (i=0; i<(int)len; ++i) { - if (data[i]== '\n') - sz_out += 2; /* Maybe add a CR; maybe add a dot. */ - } - *out = outp = tor_malloc(sz_out+1); - end = data+len; - start_of_line = 1; - while (data < end) { - if (*data == '\n') { - if (data > start && data[-1] != '\r') - *outp++ = '\r'; - start_of_line = 1; - } else if (*data == '.') { - if (start_of_line) { - start_of_line = 0; - *outp++ = '.'; - } - } else { - start_of_line = 0; - } - *outp++ = *data++; - } - if (outp < *out+2 || fast_memcmp(outp-2, "\r\n", 2)) { - *outp++ = '\r'; - *outp++ = '\n'; - } - *outp++ = '.'; - *outp++ = '\r'; - *outp++ = '\n'; - *outp = '\0'; /* NUL-terminate just in case. */ - tor_assert((outp - *out) <= (int)sz_out); - return outp - *out; -} - -/** Given a len-character string in data, made of lines - * terminated by CRLF, allocate a new string in *out, and copy - * the contents of data into *out, removing any period - * that appears at the start of a line, and replacing all CRLF sequences - * with LF. Return the number of - * bytes in *out. */ -STATIC size_t -read_escaped_data(const char *data, size_t len, char **out) -{ - char *outp; - const char *next; - const char *end; - - *out = outp = tor_malloc(len+1); - - end = data+len; - - while (data < end) { - /* we're at the start of a line. */ - if (*data == '.') - ++data; - next = memchr(data, '\n', end-data); - if (next) { - size_t n_to_copy = next-data; - /* Don't copy a CR that precedes this LF. */ - if (n_to_copy && *(next-1) == '\r') - --n_to_copy; - memcpy(outp, data, n_to_copy); - outp += n_to_copy; - data = next+1; /* This will point at the start of the next line, - * or the end of the string, or a period. */ - } else { - memcpy(outp, data, end-data); - outp += (end-data); - *outp = '\0'; - return outp - *out; - } - *outp++ = '\n'; - } - - *outp = '\0'; - return outp - *out; -} - -/** If the first in_len_max characters in start contain a - * double-quoted string with escaped characters, return the length of that - * string (as encoded, including quotes). Otherwise return -1. */ -static INLINE int -get_escaped_string_length(const char *start, size_t in_len_max, - int *chars_out) -{ - const char *cp, *end; - int chars = 0; - - if (*start != '\"') - return -1; - - cp = start+1; - end = start+in_len_max; - - /* Calculate length. */ - while (1) { - if (cp >= end) { - return -1; /* Too long. */ - } else if (*cp == '\\') { - if (++cp == end) - return -1; /* Can't escape EOS. */ - ++cp; - ++chars; - } else if (*cp == '\"') { - break; - } else { - ++cp; - ++chars; - } - } - if (chars_out) - *chars_out = chars; - return (int)(cp - start+1); -} - -/** As decode_escaped_string, but does not decode the string: copies the - * entire thing, including quotation marks. */ -static const char * -extract_escaped_string(const char *start, size_t in_len_max, - char **out, size_t *out_len) -{ - int length = get_escaped_string_length(start, in_len_max, NULL); - if (length<0) - return NULL; - *out_len = length; - *out = tor_strndup(start, *out_len); - return start+length; -} - -/** Given a pointer to a string starting at start containing - * in_len_max characters, decode a string beginning with one double - * quote, containing any number of non-quote characters or characters escaped - * with a backslash, and ending with a final double quote. Place the resulting - * string (unquoted, unescaped) into a newly allocated string in *out; - * store its length in out_len. On success, return a pointer to the - * character immediately following the escaped string. On failure, return - * NULL. */ -static const char * -decode_escaped_string(const char *start, size_t in_len_max, - char **out, size_t *out_len) -{ - const char *cp, *end; - char *outp; - int len, n_chars = 0; - - len = get_escaped_string_length(start, in_len_max, &n_chars); - if (len<0) - return NULL; - - end = start+len-1; /* Index of last quote. */ - tor_assert(*end == '\"'); - outp = *out = tor_malloc(len+1); - *out_len = n_chars; - - cp = start+1; - while (cp < end) { - if (*cp == '\\') - ++cp; - *outp++ = *cp++; - } - *outp = '\0'; - tor_assert((outp - *out) == (int)*out_len); - - return end+1; -} - -/** Acts like sprintf, but writes its formatted string to the end of - * conn-\>outbuf. */ -static void -connection_printf_to_buf(control_connection_t *conn, const char *format, ...) -{ - va_list ap; - char *buf = NULL; - int len; - - va_start(ap,format); - len = tor_vasprintf(&buf, format, ap); - va_end(ap); - - if (len < 0) { - log_err(LD_BUG, "Unable to format string for controller."); - tor_assert(0); - } - - connection_write_to_buf(buf, (size_t)len, TO_CONN(conn)); - - tor_free(buf); -} - -/** Write all of the open control ports to ControlPortWriteToFile */ -void -control_ports_write_to_file(void) -{ - smartlist_t *lines; - char *joined = NULL; - const or_options_t *options = get_options(); - - if (!options->ControlPortWriteToFile) - return; - - lines = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(get_connection_array(), const connection_t *, conn) { - if (conn->type != CONN_TYPE_CONTROL_LISTENER || conn->marked_for_close) - continue; -#ifdef AF_UNIX - if (conn->socket_family == AF_UNIX) { - smartlist_add_asprintf(lines, "UNIX_PORT=%s\n", conn->address); - continue; - } -#endif - smartlist_add_asprintf(lines, "PORT=%s:%d\n", conn->address, conn->port); - } SMARTLIST_FOREACH_END(conn); - - joined = smartlist_join_strings(lines, "", 0, NULL); - - if (write_str_to_file(options->ControlPortWriteToFile, joined, 0) < 0) { - log_warn(LD_CONTROL, "Writing %s failed: %s", - options->ControlPortWriteToFile, strerror(errno)); - } -#ifndef _WIN32 - if (options->ControlPortFileGroupReadable) { - if (chmod(options->ControlPortWriteToFile, 0640)) { - log_warn(LD_FS,"Unable to make %s group-readable.", - options->ControlPortWriteToFile); - } - } -#endif - tor_free(joined); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); -} - -/** Send a "DONE" message down the control connection conn. */ -static void -send_control_done(control_connection_t *conn) -{ - connection_write_str_to_buf("250 OK\r\n", conn); -} - -/** Send an event to all v1 controllers that are listening for code - * event. The event's body is given by msg. - * - * If which & SHORT_NAMES, the event contains short-format names: send - * it to controllers that haven't enabled the VERBOSE_NAMES feature. If - * which & LONG_NAMES, the event contains long-format names: send it - * to controllers that have enabled VERBOSE_NAMES. - * - * The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with - * respect to the EXTENDED_EVENTS feature. */ -MOCK_IMPL(STATIC void, -send_control_event_string,(uint16_t event, event_format_t which, - const char *msg)) -{ - smartlist_t *conns = get_connection_array(); - (void)which; - tor_assert(event >= EVENT_MIN_ && event <= EVENT_MAX_); - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->type == CONN_TYPE_CONTROL && - !conn->marked_for_close && - conn->state == CONTROL_CONN_STATE_OPEN) { - control_connection_t *control_conn = TO_CONTROL_CONN(conn); - - if (control_conn->event_mask & (1<event
. The event's body is created by the printf-style format in - * format, and other arguments as provided. */ -static void -send_control_event_impl(uint16_t event, event_format_t which, - const char *format, va_list ap) -{ - char *buf = NULL; - int len; - - len = tor_vasprintf(&buf, format, ap); - if (len < 0) { - log_warn(LD_BUG, "Unable to format event for controller."); - return; - } - - send_control_event_string(event, which|ALL_FORMATS, buf); - - tor_free(buf); -} - -/** Send an event to all v1 controllers that are listening for code - * event. The event's body is created by the printf-style format in - * format, and other arguments as provided. */ -static void -send_control_event(uint16_t event, event_format_t which, - const char *format, ...) -{ - va_list ap; - va_start(ap, format); - send_control_event_impl(event, which, format, ap); - va_end(ap); -} - -/** Given a text circuit id, return the corresponding circuit. */ -static origin_circuit_t * -get_circ(const char *id) -{ - uint32_t n_id; - int ok; - n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL); - if (!ok) - return NULL; - return circuit_get_by_global_id(n_id); -} - -/** Given a text stream id, return the corresponding AP connection. */ -static entry_connection_t * -get_stream(const char *id) -{ - uint64_t n_id; - int ok; - connection_t *conn; - n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL); - if (!ok) - return NULL; - conn = connection_get_by_global_id(n_id); - if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close) - return NULL; - return TO_ENTRY_CONN(conn); -} - -/** Helper for setconf and resetconf. Acts like setconf, except - * it passes use_defaults on to options_trial_assign(). Modifies the - * contents of body. - */ -static int -control_setconf_helper(control_connection_t *conn, uint32_t len, char *body, - int use_defaults) -{ - setopt_err_t opt_err; - config_line_t *lines=NULL; - char *start = body; - char *errstring = NULL; - const int clear_first = 1; - - char *config; - smartlist_t *entries = smartlist_new(); - - /* We have a string, "body", of the format '(key(=val|="val")?)' entries - * separated by space. break it into a list of configuration entries. */ - while (*body) { - char *eq = body; - char *key; - char *entry; - while (!TOR_ISSPACE(*eq) && *eq != '=') - ++eq; - key = tor_strndup(body, eq-body); - body = eq+1; - if (*eq == '=') { - char *val=NULL; - size_t val_len=0; - if (*body != '\"') { - char *val_start = body; - while (!TOR_ISSPACE(*body)) - body++; - val = tor_strndup(val_start, body-val_start); - val_len = strlen(val); - } else { - body = (char*)extract_escaped_string(body, (len - (body-start)), - &val, &val_len); - if (!body) { - connection_write_str_to_buf("551 Couldn't parse string\r\n", conn); - SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); - smartlist_free(entries); - tor_free(key); - return 0; - } - } - tor_asprintf(&entry, "%s %s", key, val); - tor_free(key); - tor_free(val); - } else { - entry = key; - } - smartlist_add(entries, entry); - while (TOR_ISSPACE(*body)) - ++body; - } - - smartlist_add(entries, tor_strdup("")); - config = smartlist_join_strings(entries, "\n", 0, NULL); - SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); - smartlist_free(entries); - - if (config_get_lines(config, &lines, 0) < 0) { - log_warn(LD_CONTROL,"Controller gave us config lines we can't parse."); - connection_write_str_to_buf("551 Couldn't parse configuration\r\n", - conn); - tor_free(config); - return 0; - } - tor_free(config); - - opt_err = options_trial_assign(lines, use_defaults, clear_first, &errstring); - { - const char *msg; - switch (opt_err) { - case SETOPT_ERR_MISC: - msg = "552 Unrecognized option"; - break; - case SETOPT_ERR_PARSE: - msg = "513 Unacceptable option value"; - break; - case SETOPT_ERR_TRANSITION: - msg = "553 Transition not allowed"; - break; - case SETOPT_ERR_SETTING: - default: - msg = "553 Unable to set option"; - break; - case SETOPT_OK: - config_free_lines(lines); - send_control_done(conn); - return 0; - } - log_warn(LD_CONTROL, - "Controller gave us config lines that didn't validate: %s", - errstring); - connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring); - config_free_lines(lines); - tor_free(errstring); - return 0; - } -} - -/** Called when we receive a SETCONF message: parse the body and try - * to update our configuration. Reply with a DONE or ERROR message. - * Modifies the contents of body.*/ -static int -handle_control_setconf(control_connection_t *conn, uint32_t len, char *body) -{ - return control_setconf_helper(conn, len, body, 0); -} - -/** Called when we receive a RESETCONF message: parse the body and try - * to update our configuration. Reply with a DONE or ERROR message. - * Modifies the contents of body. */ -static int -handle_control_resetconf(control_connection_t *conn, uint32_t len, char *body) -{ - return control_setconf_helper(conn, len, body, 1); -} - -/** Called when we receive a GETCONF message. Parse the request, and - * reply with a CONFVALUE or an ERROR message */ -static int -handle_control_getconf(control_connection_t *conn, uint32_t body_len, - const char *body) -{ - smartlist_t *questions = smartlist_new(); - smartlist_t *answers = smartlist_new(); - smartlist_t *unrecognized = smartlist_new(); - char *msg = NULL; - size_t msg_len; - const or_options_t *options = get_options(); - int i, len; - - (void) body_len; /* body is NUL-terminated; so we can ignore len. */ - smartlist_split_string(questions, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(questions, const char *, q) { - if (!option_is_recognized(q)) { - smartlist_add(unrecognized, (char*) q); - } else { - config_line_t *answer = option_get_assignment(options,q); - if (!answer) { - const char *name = option_get_canonical_name(q); - smartlist_add_asprintf(answers, "250-%s\r\n", name); - } - - while (answer) { - config_line_t *next; - smartlist_add_asprintf(answers, "250-%s=%s\r\n", - answer->key, answer->value); - - next = answer->next; - tor_free(answer->key); - tor_free(answer->value); - tor_free(answer); - answer = next; - } - } - } SMARTLIST_FOREACH_END(q); - - if ((len = smartlist_len(unrecognized))) { - for (i=0; i < len-1; ++i) - connection_printf_to_buf(conn, - "552-Unrecognized configuration key \"%s\"\r\n", - (char*)smartlist_get(unrecognized, i)); - connection_printf_to_buf(conn, - "552 Unrecognized configuration key \"%s\"\r\n", - (char*)smartlist_get(unrecognized, len-1)); - } else if ((len = smartlist_len(answers))) { - char *tmp = smartlist_get(answers, len-1); - tor_assert(strlen(tmp)>4); - tmp[3] = ' '; - msg = smartlist_join_strings(answers, "", 0, &msg_len); - connection_write_to_buf(msg, msg_len, TO_CONN(conn)); - } else { - connection_write_str_to_buf("250 OK\r\n", conn); - } - - SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp)); - smartlist_free(answers); - SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp)); - smartlist_free(questions); - smartlist_free(unrecognized); - - tor_free(msg); - - return 0; -} - -/** Called when we get a +LOADCONF message. */ -static int -handle_control_loadconf(control_connection_t *conn, uint32_t len, - const char *body) -{ - setopt_err_t retval; - char *errstring = NULL; - const char *msg = NULL; - (void) len; - - retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring); - - if (retval != SETOPT_OK) - log_warn(LD_CONTROL, - "Controller gave us config file that didn't validate: %s", - errstring); - - switch (retval) { - case SETOPT_ERR_PARSE: - msg = "552 Invalid config file"; - break; - case SETOPT_ERR_TRANSITION: - msg = "553 Transition not allowed"; - break; - case SETOPT_ERR_SETTING: - msg = "553 Unable to set option"; - break; - case SETOPT_ERR_MISC: - default: - msg = "550 Unable to load config"; - break; - case SETOPT_OK: - break; - } - if (msg) { - if (errstring) - connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring); - else - connection_printf_to_buf(conn, "%s\r\n", msg); - } else { - send_control_done(conn); - } - tor_free(errstring); - return 0; -} - -/** Helper structure: maps event values to their names. */ -struct control_event_t { - uint16_t event_code; - const char *event_name; -}; -/** Table mapping event values to their names. Used to implement SETEVENTS - * and GETINFO events/names, and to keep they in sync. */ -static const struct control_event_t control_event_table[] = { - { EVENT_CIRCUIT_STATUS, "CIRC" }, - { EVENT_CIRCUIT_STATUS_MINOR, "CIRC_MINOR" }, - { EVENT_STREAM_STATUS, "STREAM" }, - { EVENT_OR_CONN_STATUS, "ORCONN" }, - { EVENT_BANDWIDTH_USED, "BW" }, - { EVENT_DEBUG_MSG, "DEBUG" }, - { EVENT_INFO_MSG, "INFO" }, - { EVENT_NOTICE_MSG, "NOTICE" }, - { EVENT_WARN_MSG, "WARN" }, - { EVENT_ERR_MSG, "ERR" }, - { EVENT_NEW_DESC, "NEWDESC" }, - { EVENT_ADDRMAP, "ADDRMAP" }, - { EVENT_AUTHDIR_NEWDESCS, "AUTHDIR_NEWDESCS" }, - { EVENT_DESCCHANGED, "DESCCHANGED" }, - { EVENT_NS, "NS" }, - { EVENT_STATUS_GENERAL, "STATUS_GENERAL" }, - { EVENT_STATUS_CLIENT, "STATUS_CLIENT" }, - { EVENT_STATUS_SERVER, "STATUS_SERVER" }, - { EVENT_GUARD, "GUARD" }, - { EVENT_STREAM_BANDWIDTH_USED, "STREAM_BW" }, - { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" }, - { EVENT_NEWCONSENSUS, "NEWCONSENSUS" }, - { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" }, - { EVENT_SIGNAL, "SIGNAL" }, - { EVENT_CONF_CHANGED, "CONF_CHANGED"}, - { EVENT_CONN_BW, "CONN_BW" }, - { EVENT_CELL_STATS, "CELL_STATS" }, - { EVENT_TB_EMPTY, "TB_EMPTY" }, - { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" }, - { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" }, - { EVENT_HS_DESC, "HS_DESC" }, - { 0, NULL }, -}; - -/** Called when we get a SETEVENTS message: update conn->event_mask, - * and reply with DONE or ERROR. */ -static int -handle_control_setevents(control_connection_t *conn, uint32_t len, - const char *body) -{ - int event_code = -1; - uint32_t event_mask = 0; - smartlist_t *events = smartlist_new(); - - (void) len; - - smartlist_split_string(events, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(events, const char *, ev) - { - if (!strcasecmp(ev, "EXTENDED")) { - continue; - } else { - int i; - for (i = 0; control_event_table[i].event_name != NULL; ++i) { - if (!strcasecmp(ev, control_event_table[i].event_name)) { - event_code = control_event_table[i].event_code; - break; - } - } - - if (event_code == -1) { - connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n", - ev); - SMARTLIST_FOREACH(events, char *, e, tor_free(e)); - smartlist_free(events); - return 0; - } - } - event_mask |= (1 << event_code); - } - SMARTLIST_FOREACH_END(ev); - SMARTLIST_FOREACH(events, char *, e, tor_free(e)); - smartlist_free(events); - - conn->event_mask = event_mask; - - control_update_global_event_mask(); - send_control_done(conn); - return 0; -} - -/** Decode the hashed, base64'd passwords stored in passwords. - * Return a smartlist of acceptable passwords (unterminated strings of - * length S2K_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on failure. - */ -smartlist_t * -decode_hashed_passwords(config_line_t *passwords) -{ - char decoded[64]; - config_line_t *cl; - smartlist_t *sl = smartlist_new(); - - tor_assert(passwords); - - for (cl = passwords; cl; cl = cl->next) { - const char *hashed = cl->value; - - if (!strcmpstart(hashed, "16:")) { - if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))<0 - || strlen(hashed+3) != (S2K_SPECIFIER_LEN+DIGEST_LEN)*2) { - goto err; - } - } else { - if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed)) - != S2K_SPECIFIER_LEN+DIGEST_LEN) { - goto err; - } - } - smartlist_add(sl, tor_memdup(decoded, S2K_SPECIFIER_LEN+DIGEST_LEN)); - } - - return sl; - - err: - SMARTLIST_FOREACH(sl, char*, cp, tor_free(cp)); - smartlist_free(sl); - return NULL; -} - -/** Called when we get an AUTHENTICATE message. Check whether the - * authentication is valid, and if so, update the connection's state to - * OPEN. Reply with DONE or ERROR. - */ -static int -handle_control_authenticate(control_connection_t *conn, uint32_t len, - const char *body) -{ - int used_quoted_string = 0; - const or_options_t *options = get_options(); - const char *errstr = NULL; - char *password; - size_t password_len; - const char *cp; - int i; - int bad_cookie=0, bad_password=0; - smartlist_t *sl = NULL; - - if (!len) { - password = tor_strdup(""); - password_len = 0; - } else if (TOR_ISXDIGIT(body[0])) { - cp = body; - while (TOR_ISXDIGIT(*cp)) - ++cp; - i = (int)(cp - body); - tor_assert(i>0); - password_len = i/2; - password = tor_malloc(password_len + 1); - if (base16_decode(password, password_len+1, body, i)<0) { - connection_write_str_to_buf( - "551 Invalid hexadecimal encoding. Maybe you tried a plain text " - "password? If so, the standard requires that you put it in " - "double quotes.\r\n", conn); - connection_mark_for_close(TO_CONN(conn)); - tor_free(password); - return 0; - } - } else { - if (!decode_escaped_string(body, len, &password, &password_len)) { - connection_write_str_to_buf("551 Invalid quoted string. You need " - "to put the password in double quotes.\r\n", conn); - connection_mark_for_close(TO_CONN(conn)); - return 0; - } - used_quoted_string = 1; - } - - if (conn->safecookie_client_hash != NULL) { - /* The controller has chosen safe cookie authentication; the only - * acceptable authentication value is the controller-to-server - * response. */ - - tor_assert(authentication_cookie_is_set); - - if (password_len != DIGEST256_LEN) { - log_warn(LD_CONTROL, - "Got safe cookie authentication response with wrong length " - "(%d)", (int)password_len); - errstr = "Wrong length for safe cookie response."; - goto err; - } - - if (tor_memneq(conn->safecookie_client_hash, password, DIGEST256_LEN)) { - log_warn(LD_CONTROL, - "Got incorrect safe cookie authentication response"); - errstr = "Safe cookie response did not match expected value."; - goto err; - } - - tor_free(conn->safecookie_client_hash); - goto ok; - } - - if (!options->CookieAuthentication && !options->HashedControlPassword && - !options->HashedControlSessionPassword) { - /* if Tor doesn't demand any stronger authentication, then - * the controller can get in with anything. */ - goto ok; - } - - if (options->CookieAuthentication) { - int also_password = options->HashedControlPassword != NULL || - options->HashedControlSessionPassword != NULL; - if (password_len != AUTHENTICATION_COOKIE_LEN) { - if (!also_password) { - log_warn(LD_CONTROL, "Got authentication cookie with wrong length " - "(%d)", (int)password_len); - errstr = "Wrong length on authentication cookie."; - goto err; - } - bad_cookie = 1; - } else if (tor_memneq(authentication_cookie, password, password_len)) { - if (!also_password) { - log_warn(LD_CONTROL, "Got mismatched authentication cookie"); - errstr = "Authentication cookie did not match expected value."; - goto err; - } - bad_cookie = 1; - } else { - goto ok; - } - } - - if (options->HashedControlPassword || - options->HashedControlSessionPassword) { - int bad = 0; - smartlist_t *sl_tmp; - char received[DIGEST_LEN]; - int also_cookie = options->CookieAuthentication; - sl = smartlist_new(); - if (options->HashedControlPassword) { - sl_tmp = decode_hashed_passwords(options->HashedControlPassword); - if (!sl_tmp) - bad = 1; - else { - smartlist_add_all(sl, sl_tmp); - smartlist_free(sl_tmp); - } - } - if (options->HashedControlSessionPassword) { - sl_tmp = decode_hashed_passwords(options->HashedControlSessionPassword); - if (!sl_tmp) - bad = 1; - else { - smartlist_add_all(sl, sl_tmp); - smartlist_free(sl_tmp); - } - } - if (bad) { - if (!also_cookie) { - log_warn(LD_CONTROL, - "Couldn't decode HashedControlPassword: invalid base16"); - errstr="Couldn't decode HashedControlPassword value in configuration."; - } - bad_password = 1; - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); - smartlist_free(sl); - } else { - SMARTLIST_FOREACH(sl, char *, expected, - { - secret_to_key(received,DIGEST_LEN,password,password_len,expected); - if (tor_memeq(expected+S2K_SPECIFIER_LEN, received, DIGEST_LEN)) - goto ok; - }); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); - smartlist_free(sl); - - if (used_quoted_string) - errstr = "Password did not match HashedControlPassword value from " - "configuration"; - else - errstr = "Password did not match HashedControlPassword value from " - "configuration. Maybe you tried a plain text password? " - "If so, the standard requires that you put it in double quotes."; - bad_password = 1; - if (!also_cookie) - goto err; - } - } - - /** We only get here if both kinds of authentication failed. */ - tor_assert(bad_password && bad_cookie); - log_warn(LD_CONTROL, "Bad password or authentication cookie on controller."); - errstr = "Password did not match HashedControlPassword *or* authentication " - "cookie."; - - err: - tor_free(password); - connection_printf_to_buf(conn, "515 Authentication failed: %s\r\n", - errstr ? errstr : "Unknown reason."); - connection_mark_for_close(TO_CONN(conn)); - return 0; - ok: - log_info(LD_CONTROL, "Authenticated control connection ("TOR_SOCKET_T_FORMAT - ")", conn->base_.s); - send_control_done(conn); - conn->base_.state = CONTROL_CONN_STATE_OPEN; - tor_free(password); - if (sl) { /* clean up */ - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); - smartlist_free(sl); - } - return 0; -} - -/** Called when we get a SAVECONF command. Try to flush the current options to - * disk, and report success or failure. */ -static int -handle_control_saveconf(control_connection_t *conn, uint32_t len, - const char *body) -{ - (void) len; - (void) body; - if (options_save_current()<0) { - connection_write_str_to_buf( - "551 Unable to write configuration to disk.\r\n", conn); - } else { - send_control_done(conn); - } - 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.) */ -static int -handle_control_signal(control_connection_t *conn, uint32_t len, - const char *body) -{ - int sig = -1; - int i; - int n = 0; - char *s; - - (void) len; - - while (body[n] && ! TOR_ISSPACE(body[n])) - ++n; - s = tor_strndup(body, n); - - 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); - tor_free(s); - if (sig < 0) - return 0; - - send_control_done(conn); - /* Flush the "done" first if the signal might make us shut down. */ - if (sig == SIGTERM || sig == SIGINT) - connection_flush(TO_CONN(conn)); - - process_signal(sig); - - return 0; -} - -/** Called when we get a TAKEOWNERSHIP command. Mark this connection - * as an owning connection, so that we will exit if the connection - * closes. */ -static int -handle_control_takeownership(control_connection_t *conn, uint32_t len, - const char *body) -{ - (void)len; - (void)body; - - conn->is_owning_control_connection = 1; - - log_info(LD_CONTROL, "Control connection %d has taken ownership of this " - "Tor instance.", - (int)(conn->base_.s)); - - send_control_done(conn); - return 0; -} - -/** Return true iff addr is unusable as a mapaddress target because of - * containing funny characters. */ -static int -address_is_invalid_mapaddress_target(const char *addr) -{ - if (!strcmpstart(addr, "*.")) - return address_is_invalid_destination(addr+2, 1); - else - return address_is_invalid_destination(addr, 1); -} - -/** Called when we get a MAPADDRESS command; try to bind all listed addresses, - * and report success or failure. */ -static int -handle_control_mapaddress(control_connection_t *conn, uint32_t len, - const char *body) -{ - smartlist_t *elts; - smartlist_t *lines; - smartlist_t *reply; - char *r; - size_t sz; - (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */ - - lines = smartlist_new(); - elts = smartlist_new(); - reply = smartlist_new(); - smartlist_split_string(lines, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(lines, char *, line) { - tor_strlower(line); - smartlist_split_string(elts, line, "=", 0, 2); - if (smartlist_len(elts) == 2) { - const char *from = smartlist_get(elts,0); - const char *to = smartlist_get(elts,1); - if (address_is_invalid_mapaddress_target(to)) { - smartlist_add_asprintf(reply, - "512-syntax error: invalid address '%s'", to); - log_warn(LD_CONTROL, - "Skipping invalid argument '%s' in MapAddress msg", to); - } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0") || - !strcmp(from, "::")) { - const char type = - !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME : - (!strcmp(from, "0.0.0.0") ? RESOLVED_TYPE_IPV4 : RESOLVED_TYPE_IPV6); - const char *address = addressmap_register_virtual_address( - type, tor_strdup(to)); - if (!address) { - smartlist_add_asprintf(reply, - "451-resource exhausted: skipping '%s'", line); - log_warn(LD_CONTROL, - "Unable to allocate address for '%s' in MapAddress msg", - safe_str_client(line)); - } else { - smartlist_add_asprintf(reply, "250-%s=%s", address, to); - } - } else { - 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 " - "not of expected form 'foo=bar'.", line); - log_info(LD_CONTROL, "Skipping MapAddress '%s': wrong " - "number of items.", - safe_str_client(line)); - } - SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); - smartlist_clear(elts); - } SMARTLIST_FOREACH_END(line); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); - smartlist_free(elts); - - if (smartlist_len(reply)) { - ((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' '; - r = smartlist_join_strings(reply, "\r\n", 1, &sz); - connection_write_to_buf(r, sz, TO_CONN(conn)); - tor_free(r); - } else { - const char *response = - "512 syntax error: not enough arguments to mapaddress.\r\n"; - connection_write_to_buf(response, strlen(response), TO_CONN(conn)); - } - - SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp)); - smartlist_free(reply); - return 0; -} - -/** Implementation helper for GETINFO: knows the answers for various - * trivial-to-implement questions. */ -static int -getinfo_helper_misc(control_connection_t *conn, const char *question, - char **answer, const char **errmsg) -{ - (void) conn; - if (!strcmp(question, "version")) { - *answer = tor_strdup(get_version()); - } else if (!strcmp(question, "config-file")) { - *answer = tor_strdup(get_torrc_fname(0)); - } else if (!strcmp(question, "config-defaults-file")) { - *answer = tor_strdup(get_torrc_fname(1)); - } else if (!strcmp(question, "config-text")) { - *answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL); - } else if (!strcmp(question, "info/names")) { - *answer = list_getinfo_options(); - } else if (!strcmp(question, "dormant")) { - int dormant = rep_hist_circbuilding_dormant(time(NULL)); - *answer = tor_strdup(dormant ? "1" : "0"); - } else if (!strcmp(question, "events/names")) { - int i; - smartlist_t *event_names = smartlist_new(); - - for (i = 0; control_event_table[i].event_name != NULL; ++i) { - smartlist_add(event_names, (char *)control_event_table[i].event_name); - } - - *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")) { - uint32_t addr; - if (router_pick_published_address(get_options(), &addr) < 0) { - *errmsg = "Address unknown"; - return -1; - } - *answer = tor_dup_ip(addr); - } else if (!strcmp(question, "traffic/read")) { - tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_read())); - } else if (!strcmp(question, "traffic/written")) { - tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_written())); - } else if (!strcmp(question, "process/pid")) { - int myPid = -1; - - #ifdef _WIN32 - myPid = _getpid(); - #else - myPid = getpid(); - #endif - - tor_asprintf(answer, "%d", myPid); - } else if (!strcmp(question, "process/uid")) { - #ifdef _WIN32 - *answer = tor_strdup("-1"); - #else - int myUid = geteuid(); - tor_asprintf(answer, "%d", myUid); - #endif - } else if (!strcmp(question, "process/user")) { - #ifdef _WIN32 - *answer = tor_strdup(""); - #else - int myUid = geteuid(); - struct passwd *myPwEntry = getpwuid(myUid); - - if (myPwEntry) { - *answer = tor_strdup(myPwEntry->pw_name); - } else { - *answer = tor_strdup(""); - } - #endif - } else if (!strcmp(question, "process/descriptor-limit")) { - int max_fds=-1; - set_max_file_descriptors(0, &max_fds); - tor_asprintf(answer, "%d", max_fds); - } else if (!strcmp(question, "dir-usage")) { - *answer = directory_dump_request_log(); - } else if (!strcmp(question, "fingerprint")) { - crypto_pk_t *server_key; - if (!server_mode(get_options())) { - *errmsg = "Not running in server mode"; - return -1; - } - server_key = get_server_identity_key(); - *answer = tor_malloc(HEX_DIGEST_LEN+1); - crypto_pk_get_fingerprint(server_key, *answer, 0); - } - return 0; -} - -/** Awful hack: return a newly allocated string based on a routerinfo and - * (possibly) an extrainfo, sticking the read-history and write-history from - * ei into the resulting string. The thing you get back won't - * necessarily have a valid signature. - * - * New code should never use this; it's for backward compatibility. - * - * NOTE: ri_body is as returned by signed_descriptor_get_body: it might - * not be NUL-terminated. */ -static char * -munge_extrainfo_into_routerinfo(const char *ri_body, - const signed_descriptor_t *ri, - const signed_descriptor_t *ei) -{ - char *out = NULL, *outp; - int i; - const char *router_sig; - const char *ei_body = signed_descriptor_get_body(ei); - size_t ri_len = ri->signed_descriptor_len; - size_t ei_len = ei->signed_descriptor_len; - if (!ei_body) - goto bail; - - outp = out = tor_malloc(ri_len+ei_len+1); - if (!(router_sig = tor_memstr(ri_body, ri_len, "\nrouter-signature"))) - goto bail; - ++router_sig; - memcpy(out, ri_body, router_sig-ri_body); - outp += router_sig-ri_body; - - for (i=0; i < 2; ++i) { - const char *kwd = i ? "\nwrite-history " : "\nread-history "; - const char *cp, *eol; - if (!(cp = tor_memstr(ei_body, ei_len, kwd))) - continue; - ++cp; - if (!(eol = memchr(cp, '\n', ei_len - (cp-ei_body)))) - continue; - memcpy(outp, cp, eol-cp+1); - outp += eol-cp+1; - } - memcpy(outp, router_sig, ri_len - (router_sig-ri_body)); - *outp++ = '\0'; - tor_assert(outp-out < (int)(ri_len+ei_len+1)); - - return out; - bail: - tor_free(out); - return tor_strndup(ri_body, ri->signed_descriptor_len); -} - -/** Implementation helper for GETINFO: answers requests for information about - * which ports are bound. */ -static int -getinfo_helper_listeners(control_connection_t *control_conn, - const char *question, - char **answer, const char **errmsg) -{ - int type; - smartlist_t *res; - - (void)control_conn; - (void)errmsg; - - if (!strcmp(question, "net/listeners/or")) - type = CONN_TYPE_OR_LISTENER; - else if (!strcmp(question, "net/listeners/dir")) - type = CONN_TYPE_DIR_LISTENER; - else if (!strcmp(question, "net/listeners/socks")) - type = CONN_TYPE_AP_LISTENER; - else if (!strcmp(question, "net/listeners/trans")) - type = CONN_TYPE_AP_TRANS_LISTENER; - else if (!strcmp(question, "net/listeners/natd")) - type = CONN_TYPE_AP_NATD_LISTENER; - else if (!strcmp(question, "net/listeners/dns")) - type = CONN_TYPE_AP_DNS_LISTENER; - else if (!strcmp(question, "net/listeners/control")) - type = CONN_TYPE_CONTROL_LISTENER; - else - return 0; /* unknown key */ - - res = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { - struct sockaddr_storage ss; - socklen_t ss_len = sizeof(ss); - - if (conn->type != type || conn->marked_for_close || !SOCKET_OK(conn->s)) - continue; - - if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) { - smartlist_add_asprintf(res, "%s:%d", conn->address, (int)conn->port); - } else { - char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss); - smartlist_add(res, esc_for_log(tmp)); - tor_free(tmp); - } - - } SMARTLIST_FOREACH_END(conn); - - *answer = smartlist_join_strings(res, " ", 0, NULL); - - SMARTLIST_FOREACH(res, char *, cp, tor_free(cp)); - smartlist_free(res); - return 0; -} - -/** Implementation helper for GETINFO: knows the answers for questions about - * directory information. */ -static int -getinfo_helper_dir(control_connection_t *control_conn, - const char *question, char **answer, - const char **errmsg) -{ - const node_t *node; - const routerinfo_t *ri = NULL; - (void) control_conn; - if (!strcmpstart(question, "desc/id/")) { - node = node_get_by_hex_id(question+strlen("desc/id/")); - if (node) - ri = node->ri; - if (ri) { - const char *body = signed_descriptor_get_body(&ri->cache_info); - if (body) - *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len); - } - } else if (!strcmpstart(question, "desc/name/")) { - /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the - * warning goes to the user, not to the controller. */ - node = node_get_by_nickname(question+strlen("desc/name/"), 1); - if (node) - ri = node->ri; - if (ri) { - const char *body = signed_descriptor_get_body(&ri->cache_info); - if (body) - *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len); - } - } else if (!strcmp(question, "desc/all-recent")) { - routerlist_t *routerlist = router_get_routerlist(); - smartlist_t *sl = smartlist_new(); - if (routerlist && routerlist->routers) { - SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri, - { - const char *body = signed_descriptor_get_body(&ri->cache_info); - if (body) - smartlist_add(sl, - tor_strndup(body, ri->cache_info.signed_descriptor_len)); - }); - } - *answer = smartlist_join_strings(sl, "", 0, NULL); - SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); - smartlist_free(sl); - } else if (!strcmp(question, "desc/all-recent-extrainfo-hack")) { - /* XXXX Remove this once Torstat asks for extrainfos. */ - routerlist_t *routerlist = router_get_routerlist(); - smartlist_t *sl = smartlist_new(); - if (routerlist && routerlist->routers) { - SMARTLIST_FOREACH_BEGIN(routerlist->routers, const routerinfo_t *, ri) { - const char *body = signed_descriptor_get_body(&ri->cache_info); - signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest( - ri->cache_info.extra_info_digest); - if (ei && body) { - smartlist_add(sl, munge_extrainfo_into_routerinfo(body, - &ri->cache_info, ei)); - } else if (body) { - smartlist_add(sl, - tor_strndup(body, ri->cache_info.signed_descriptor_len)); - } - } SMARTLIST_FOREACH_END(ri); - } - *answer = smartlist_join_strings(sl, "", 0, NULL); - SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); - smartlist_free(sl); - } else if (!strcmpstart(question, "md/id/")) { - const node_t *node = node_get_by_hex_id(question+strlen("md/id/")); - const microdesc_t *md = NULL; - if (node) md = node->md; - if (md && md->body) { - *answer = tor_strndup(md->body, md->bodylen); - } - } else if (!strcmpstart(question, "md/name/")) { - /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the - * warning goes to the user, not to the controller. */ - const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 1); - /* XXXX duplicated code */ - const microdesc_t *md = NULL; - if (node) md = node->md; - if (md && md->body) { - *answer = tor_strndup(md->body, md->bodylen); - } - } else if (!strcmpstart(question, "desc-annotations/id/")) { - node = node_get_by_hex_id(question+strlen("desc-annotations/id/")); - if (node) - ri = node->ri; - if (ri) { - const char *annotations = - signed_descriptor_get_annotations(&ri->cache_info); - if (annotations) - *answer = tor_strndup(annotations, - ri->cache_info.annotations_len); - } - } else if (!strcmpstart(question, "dir/server/")) { - size_t answer_len = 0; - char *url = NULL; - smartlist_t *descs = smartlist_new(); - const char *msg; - int res; - char *cp; - tor_asprintf(&url, "/tor/%s", question+4); - res = dirserv_get_routerdescs(descs, url, &msg); - if (res) { - log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg); - smartlist_free(descs); - tor_free(url); - *errmsg = msg; - return -1; - } - SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd, - answer_len += sd->signed_descriptor_len); - cp = *answer = tor_malloc(answer_len+1); - SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd, - { - memcpy(cp, signed_descriptor_get_body(sd), - sd->signed_descriptor_len); - cp += sd->signed_descriptor_len; - }); - *cp = '\0'; - tor_free(url); - smartlist_free(descs); - } else if (!strcmpstart(question, "dir/status/")) { - *answer = tor_strdup(""); - } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */ - if (directory_caches_dir_info(get_options())) { - const cached_dir_t *consensus = dirserv_get_consensus("ns"); - if (consensus) - *answer = tor_strdup(consensus->dir); - } - if (!*answer) { /* try loading it from disk */ - char *filename = get_datadir_fname("cached-consensus"); - *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); - tor_free(filename); - } - } else if (!strcmp(question, "network-status")) { /* v1 */ - routerlist_t *routerlist = router_get_routerlist(); - if (!routerlist || !routerlist->routers || - list_server_status_v1(routerlist->routers, answer, 1) < 0) { - return -1; - } - } else if (!strcmpstart(question, "extra-info/digest/")) { - question += strlen("extra-info/digest/"); - if (strlen(question) == HEX_DIGEST_LEN) { - char d[DIGEST_LEN]; - signed_descriptor_t *sd = NULL; - if (base16_decode(d, sizeof(d), question, strlen(question))==0) { - /* XXXX this test should move into extrainfo_get_by_descriptor_digest, - * but I don't want to risk affecting other parts of the code, - * especially since the rules for using our own extrainfo (including - * when it might be freed) are different from those for using one - * we have downloaded. */ - if (router_extrainfo_digest_is_me(d)) - sd = &(router_get_my_extrainfo()->cache_info); - else - sd = extrainfo_get_by_descriptor_digest(d); - } - if (sd) { - const char *body = signed_descriptor_get_body(sd); - if (body) - *answer = tor_strndup(body, sd->signed_descriptor_len); - } - } - } - - return 0; -} - -/** Allocate and return a description of circ's current status, - * including its path (if any). */ -static char * -circuit_describe_status_for_controller(origin_circuit_t *circ) -{ - char *rv; - smartlist_t *descparts = smartlist_new(); - - { - char *vpath = circuit_list_path_for_controller(circ); - if (*vpath) { - smartlist_add(descparts, vpath); - } else { - tor_free(vpath); /* empty path; don't put an extra space in the result */ - } - } - - { - cpath_build_state_t *build_state = circ->build_state; - smartlist_t *flaglist = smartlist_new(); - char *flaglist_joined; - - if (build_state->onehop_tunnel) - smartlist_add(flaglist, (void *)"ONEHOP_TUNNEL"); - if (build_state->is_internal) - smartlist_add(flaglist, (void *)"IS_INTERNAL"); - if (build_state->need_capacity) - smartlist_add(flaglist, (void *)"NEED_CAPACITY"); - if (build_state->need_uptime) - smartlist_add(flaglist, (void *)"NEED_UPTIME"); - - /* Only emit a BUILD_FLAGS argument if it will have a non-empty value. */ - if (smartlist_len(flaglist)) { - flaglist_joined = smartlist_join_strings(flaglist, ",", 0, NULL); - - smartlist_add_asprintf(descparts, "BUILD_FLAGS=%s", flaglist_joined); - - tor_free(flaglist_joined); - } - - smartlist_free(flaglist); - } - - smartlist_add_asprintf(descparts, "PURPOSE=%s", - circuit_purpose_to_controller_string(circ->base_.purpose)); - - { - const char *hs_state = - circuit_purpose_to_controller_hs_state_string(circ->base_.purpose); - - if (hs_state != NULL) { - smartlist_add_asprintf(descparts, "HS_STATE=%s", hs_state); - } - } - - if (circ->rend_data != NULL) { - smartlist_add_asprintf(descparts, "REND_QUERY=%s", - circ->rend_data->onion_address); - } - - { - char tbuf[ISO_TIME_USEC_LEN+1]; - format_iso_time_nospace_usec(tbuf, &circ->base_.timestamp_created); - - smartlist_add_asprintf(descparts, "TIME_CREATED=%s", tbuf); - } - - rv = smartlist_join_strings(descparts, " ", 0, NULL); - - SMARTLIST_FOREACH(descparts, char *, cp, tor_free(cp)); - smartlist_free(descparts); - - return rv; -} - -/** Implementation helper for GETINFO: knows how to generate summaries of the - * current states of things we send events about. */ -static int -getinfo_helper_events(control_connection_t *control_conn, - const char *question, char **answer, - const char **errmsg) -{ - (void) control_conn; - if (!strcmp(question, "circuit-status")) { - circuit_t *circ_; - smartlist_t *status = smartlist_new(); - TOR_LIST_FOREACH(circ_, circuit_get_global_list(), head) { - origin_circuit_t *circ; - char *circdesc; - const char *state; - if (! CIRCUIT_IS_ORIGIN(circ_) || circ_->marked_for_close) - continue; - circ = TO_ORIGIN_CIRCUIT(circ_); - - if (circ->base_.state == CIRCUIT_STATE_OPEN) - state = "BUILT"; - else if (circ->cpath) - state = "EXTENDED"; - else - state = "LAUNCHED"; - - circdesc = circuit_describe_status_for_controller(circ); - - smartlist_add_asprintf(status, "%lu %s%s%s", - (unsigned long)circ->global_identifier, - state, *circdesc ? " " : "", circdesc); - tor_free(circdesc); - } - *answer = smartlist_join_strings(status, "\r\n", 0, NULL); - SMARTLIST_FOREACH(status, char *, cp, tor_free(cp)); - smartlist_free(status); - } else if (!strcmp(question, "stream-status")) { - smartlist_t *conns = get_connection_array(); - smartlist_t *status = smartlist_new(); - char buf[256]; - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { - const char *state; - entry_connection_t *conn; - circuit_t *circ; - origin_circuit_t *origin_circ = NULL; - if (base_conn->type != CONN_TYPE_AP || - base_conn->marked_for_close || - base_conn->state == AP_CONN_STATE_SOCKS_WAIT || - base_conn->state == AP_CONN_STATE_NATD_WAIT) - continue; - conn = TO_ENTRY_CONN(base_conn); - switch (base_conn->state) - { - case AP_CONN_STATE_CONTROLLER_WAIT: - case AP_CONN_STATE_CIRCUIT_WAIT: - if (conn->socks_request && - SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) - state = "NEWRESOLVE"; - else - state = "NEW"; - break; - case AP_CONN_STATE_RENDDESC_WAIT: - case AP_CONN_STATE_CONNECT_WAIT: - state = "SENTCONNECT"; break; - case AP_CONN_STATE_RESOLVE_WAIT: - state = "SENTRESOLVE"; break; - case AP_CONN_STATE_OPEN: - state = "SUCCEEDED"; break; - default: - log_warn(LD_BUG, "Asked for stream in unknown state %d", - base_conn->state); - continue; - } - circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn)); - if (circ && CIRCUIT_IS_ORIGIN(circ)) - origin_circ = TO_ORIGIN_CIRCUIT(circ); - write_stream_target_to_buf(conn, buf, sizeof(buf)); - smartlist_add_asprintf(status, "%lu %s %lu %s", - (unsigned long) base_conn->global_identifier,state, - origin_circ? - (unsigned long)origin_circ->global_identifier : 0ul, - buf); - } SMARTLIST_FOREACH_END(base_conn); - *answer = smartlist_join_strings(status, "\r\n", 0, NULL); - SMARTLIST_FOREACH(status, char *, cp, tor_free(cp)); - smartlist_free(status); - } else if (!strcmp(question, "orconn-status")) { - smartlist_t *conns = get_connection_array(); - smartlist_t *status = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) { - const char *state; - char name[128]; - or_connection_t *conn; - if (base_conn->type != CONN_TYPE_OR || base_conn->marked_for_close) - continue; - conn = TO_OR_CONN(base_conn); - if (conn->base_.state == OR_CONN_STATE_OPEN) - state = "CONNECTED"; - else if (conn->nickname) - state = "LAUNCHED"; - else - state = "NEW"; - orconn_target_get_name(name, sizeof(name), conn); - smartlist_add_asprintf(status, "%s %s", name, state); - } SMARTLIST_FOREACH_END(base_conn); - *answer = smartlist_join_strings(status, "\r\n", 0, NULL); - SMARTLIST_FOREACH(status, char *, cp, tor_free(cp)); - smartlist_free(status); - } else if (!strcmpstart(question, "address-mappings/")) { - time_t min_e, max_e; - smartlist_t *mappings; - question += strlen("address-mappings/"); - if (!strcmp(question, "all")) { - min_e = 0; max_e = TIME_MAX; - } else if (!strcmp(question, "cache")) { - min_e = 2; max_e = TIME_MAX; - } else if (!strcmp(question, "config")) { - min_e = 0; max_e = 0; - } else if (!strcmp(question, "control")) { - min_e = 1; max_e = 1; - } else { - return 0; - } - mappings = smartlist_new(); - addressmap_get_mappings(mappings, min_e, max_e, 1); - *answer = smartlist_join_strings(mappings, "\r\n", 0, NULL); - SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp)); - smartlist_free(mappings); - } else if (!strcmpstart(question, "status/")) { - /* Note that status/ is not a catch-all for events; there's only supposed - * to be a status GETINFO if there's a corresponding STATUS event. */ - if (!strcmp(question, "status/circuit-established")) { - *answer = tor_strdup(can_complete_circuit ? "1" : "0"); - } else if (!strcmp(question, "status/enough-dir-info")) { - *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0"); - } else if (!strcmp(question, "status/good-server-descriptor") || - !strcmp(question, "status/accepted-server-descriptor")) { - /* They're equivalent for now, until we can figure out how to make - * good-server-descriptor be what we want. See comment in - * control-spec.txt. */ - *answer = tor_strdup(directories_have_accepted_server_descriptor() - ? "1" : "0"); - } else if (!strcmp(question, "status/reachability-succeeded/or")) { - *answer = tor_strdup(check_whether_orport_reachable() ? "1" : "0"); - } else if (!strcmp(question, "status/reachability-succeeded/dir")) { - *answer = tor_strdup(check_whether_dirport_reachable() ? "1" : "0"); - } else if (!strcmp(question, "status/reachability-succeeded")) { - tor_asprintf(answer, "OR=%d DIR=%d", - check_whether_orport_reachable() ? 1 : 0, - check_whether_dirport_reachable() ? 1 : 0); - } else if (!strcmp(question, "status/bootstrap-phase")) { - *answer = tor_strdup(last_sent_bootstrap_message); - } else if (!strcmpstart(question, "status/version/")) { - int is_server = server_mode(get_options()); - networkstatus_t *c = networkstatus_get_latest_consensus(); - version_status_t status; - const char *recommended; - if (c) { - recommended = is_server ? c->server_versions : c->client_versions; - status = tor_version_is_obsolete(VERSION, recommended); - } else { - recommended = "?"; - status = VS_UNKNOWN; - } - - if (!strcmp(question, "status/version/recommended")) { - *answer = tor_strdup(recommended); - return 0; - } - if (!strcmp(question, "status/version/current")) { - switch (status) - { - case VS_RECOMMENDED: *answer = tor_strdup("recommended"); break; - case VS_OLD: *answer = tor_strdup("obsolete"); break; - case VS_NEW: *answer = tor_strdup("new"); break; - case VS_NEW_IN_SERIES: *answer = tor_strdup("new in series"); break; - case VS_UNRECOMMENDED: *answer = tor_strdup("unrecommended"); break; - case VS_EMPTY: *answer = tor_strdup("none recommended"); break; - case VS_UNKNOWN: *answer = tor_strdup("unknown"); break; - default: tor_fragile_assert(); - } - } else if (!strcmp(question, "status/version/num-versioning") || - !strcmp(question, "status/version/num-concurring")) { - tor_asprintf(answer, "%d", get_n_authorities(V3_DIRINFO)); - log_warn(LD_GENERAL, "%s is deprecated; it no longer gives useful " - "information", question); - } - } else if (!strcmp(question, "status/clients-seen")) { - char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL)); - if (!bridge_stats) { - *errmsg = "No bridge-client stats available"; - return -1; - } - *answer = bridge_stats; - } else { - return 0; - } - } - return 0; -} - -/** Callback function for GETINFO: on a given control connection, try to - * answer the question q and store the newly-allocated answer in - * *a. If an internal error occurs, return -1 and optionally set - * *error_out to point to an error message to be delivered to the - * controller. On success, _or if the key is not recognized_, return 0. Do not - * set a if the key is not recognized. - */ -typedef int (*getinfo_helper_t)(control_connection_t *, - const char *q, char **a, - const char **error_out); - -/** A single item for the GETINFO question-to-answer-function table. */ -typedef struct getinfo_item_t { - const char *varname; /**< The value (or prefix) of the question. */ - getinfo_helper_t fn; /**< The function that knows the answer: NULL if - * this entry is documentation-only. */ - const char *desc; /**< Description of the variable. */ - int is_prefix; /** Must varname match exactly, or must it be a prefix? */ -} getinfo_item_t; - -#define ITEM(name, fn, desc) { name, getinfo_helper_##fn, desc, 0 } -#define PREFIX(name, fn, desc) { name, getinfo_helper_##fn, desc, 1 } -#define DOC(name, desc) { name, NULL, desc, 0 } - -/** Table mapping questions accepted by GETINFO to the functions that know how - * to answer them. */ -static const getinfo_item_t getinfo_items[] = { - ITEM("version", misc, "The current version of Tor."), - ITEM("config-file", misc, "Current location of the \"torrc\" file."), - ITEM("config-defaults-file", misc, "Current location of the defaults file."), - ITEM("config-text", misc, - "Return the string that would be written by a saveconf command."), - ITEM("accounting/bytes", accounting, - "Number of bytes read/written so far in the accounting interval."), - ITEM("accounting/bytes-left", accounting, - "Number of bytes left to write/read so far in the accounting interval."), - ITEM("accounting/enabled", accounting, "Is accounting currently enabled?"), - ITEM("accounting/hibernating", accounting, "Are we hibernating or awake?"), - ITEM("accounting/interval-start", accounting, - "Time when the accounting period starts."), - ITEM("accounting/interval-end", accounting, - "Time when the accounting period ends."), - ITEM("accounting/interval-wake", accounting, - "Time to wake up in this accounting period."), - ITEM("helper-nodes", entry_guards, NULL), /* deprecated */ - ITEM("entry-guards", entry_guards, - "Which nodes are we using as entry guards?"), - ITEM("fingerprint", misc, NULL), - 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."), - ITEM("desc/all-recent", dir, - "All non-expired, non-superseded router descriptors."), - ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */ - PREFIX("md/id/", dir, "Microdescriptors by ID"), - PREFIX("md/name/", dir, "Microdescriptors by name"), - PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."), - PREFIX("net/listeners/", listeners, "Bound addresses by type"), - ITEM("ns/all", networkstatus, - "Brief summary of router status (v2 directory format)"), - PREFIX("ns/id/", networkstatus, - "Brief summary of router status by ID (v2 directory format)."), - PREFIX("ns/name/", networkstatus, - "Brief summary of router status by nickname (v2 directory format)."), - PREFIX("ns/purpose/", networkstatus, - "Brief summary of router status by purpose (v2 directory format)."), - ITEM("network-status", dir, - "Brief summary of router status (v1 directory format)"), - ITEM("circuit-status", events, "List of current circuits originating here."), - ITEM("stream-status", events,"List of current streams."), - ITEM("orconn-status", events, "A list of current OR connections."), - ITEM("dormant", misc, - "Is Tor dormant (not building circuits because it's idle)?"), - PREFIX("address-mappings/", events, NULL), - DOC("address-mappings/all", "Current address mappings."), - DOC("address-mappings/cache", "Current cached DNS replies."), - DOC("address-mappings/config", - "Current address mappings from configuration."), - DOC("address-mappings/control", "Current address mappings from controller."), - PREFIX("status/", events, NULL), - DOC("status/circuit-established", - "Whether we think client functionality is working."), - DOC("status/enough-dir-info", - "Whether we have enough up-to-date directory information to build " - "circuits."), - DOC("status/bootstrap-phase", - "The last bootstrap phase status event that Tor sent."), - DOC("status/clients-seen", - "Breakdown of client countries seen by a bridge."), - DOC("status/version/recommended", "List of currently recommended versions."), - DOC("status/version/current", "Status of the current version."), - DOC("status/version/num-versioning", "Number of versioning authorities."), - DOC("status/version/num-concurring", - "Number of versioning authorities agreeing on the status of the " - "current version"), - ITEM("address", misc, "IP address of this Tor host, if we can guess it."), - ITEM("traffic/read", misc,"Bytes read since the process was started."), - ITEM("traffic/written", misc, - "Bytes written since the process was started."), - ITEM("process/pid", misc, "Process id belonging to the main tor process."), - ITEM("process/uid", misc, "User id running the tor process."), - ITEM("process/user", misc, - "Username under which the tor process is running."), - ITEM("process/descriptor-limit", misc, "File descriptor limit."), - ITEM("dir-usage", misc, "Breakdown of bytes transferred over DirPort."), - PREFIX("desc-annotations/id/", dir, "Router annotations by hexdigest."), - PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."), - PREFIX("dir/status/", dir, - "v2 networkstatus docs as retrieved from a DirPort."), - ITEM("dir/status-vote/current/consensus", dir, - "v3 Networkstatus consensus as retrieved from a DirPort."), - ITEM("exit-policy/default", policies, - "The default value appended to the configured exit policy."), - PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"), - { NULL, NULL, NULL, 0 } -}; - -/** Allocate and return a list of recognized GETINFO options. */ -static char * -list_getinfo_options(void) -{ - int i; - smartlist_t *lines = smartlist_new(); - char *ans; - for (i = 0; getinfo_items[i].varname; ++i) { - if (!getinfo_items[i].desc) - continue; - - smartlist_add_asprintf(lines, "%s%s -- %s\n", - getinfo_items[i].varname, - getinfo_items[i].is_prefix ? "*" : "", - getinfo_items[i].desc); - } - smartlist_sort_strings(lines); - - ans = smartlist_join_strings(lines, "", 0, NULL); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); - - return ans; -} - -/** Lookup the 'getinfo' entry question, and return - * the answer in *answer (or NULL if key not recognized). - * Return 0 if success or unrecognized, or -1 if recognized but - * internal error. */ -static int -handle_getinfo_helper(control_connection_t *control_conn, - const char *question, char **answer, - const char **err_out) -{ - int i; - *answer = NULL; /* unrecognized key by default */ - - for (i = 0; getinfo_items[i].varname; ++i) { - int match; - if (getinfo_items[i].is_prefix) - match = !strcmpstart(question, getinfo_items[i].varname); - else - match = !strcmp(question, getinfo_items[i].varname); - if (match) { - tor_assert(getinfo_items[i].fn); - return getinfo_items[i].fn(control_conn, question, answer, err_out); - } - } - - return 0; /* unrecognized */ -} - -/** Called when we receive a GETINFO command. Try to fetch all requested - * information, and reply with information or error message. */ -static int -handle_control_getinfo(control_connection_t *conn, uint32_t len, - const char *body) -{ - smartlist_t *questions = smartlist_new(); - smartlist_t *answers = smartlist_new(); - smartlist_t *unrecognized = smartlist_new(); - char *msg = NULL, *ans = NULL; - int i; - (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */ - - smartlist_split_string(questions, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(questions, const char *, q) { - const char *errmsg = NULL; - if (handle_getinfo_helper(conn, q, &ans, &errmsg) < 0) { - if (!errmsg) - errmsg = "Internal error"; - connection_printf_to_buf(conn, "551 %s\r\n", errmsg); - goto done; - } - if (!ans) { - smartlist_add(unrecognized, (char*)q); - } else { - smartlist_add(answers, tor_strdup(q)); - smartlist_add(answers, ans); - } - } SMARTLIST_FOREACH_END(q); - if (smartlist_len(unrecognized)) { - for (i=0; i < smartlist_len(unrecognized)-1; ++i) - connection_printf_to_buf(conn, - "552-Unrecognized key \"%s\"\r\n", - (char*)smartlist_get(unrecognized, i)); - connection_printf_to_buf(conn, - "552 Unrecognized key \"%s\"\r\n", - (char*)smartlist_get(unrecognized, i)); - goto done; - } - - for (i = 0; i < smartlist_len(answers); i += 2) { - char *k = smartlist_get(answers, i); - char *v = smartlist_get(answers, i+1); - if (!strchr(v, '\n') && !strchr(v, '\r')) { - connection_printf_to_buf(conn, "250-%s=", k); - connection_write_str_to_buf(v, conn); - connection_write_str_to_buf("\r\n", conn); - } else { - char *esc = NULL; - size_t esc_len; - esc_len = write_escaped_data(v, strlen(v), &esc); - connection_printf_to_buf(conn, "250+%s=\r\n", k); - connection_write_to_buf(esc, esc_len, TO_CONN(conn)); - tor_free(esc); - } - } - connection_write_str_to_buf("250 OK\r\n", conn); - - done: - SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp)); - smartlist_free(answers); - SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp)); - smartlist_free(questions); - smartlist_free(unrecognized); - tor_free(msg); - - return 0; -} - -/** Given a string, convert it to a circuit purpose. */ -static uint8_t -circuit_purpose_from_string(const char *string) -{ - if (!strcasecmpstart(string, "purpose=")) - string += strlen("purpose="); - - if (!strcasecmp(string, "general")) - return CIRCUIT_PURPOSE_C_GENERAL; - else if (!strcasecmp(string, "controller")) - return CIRCUIT_PURPOSE_CONTROLLER; - else - return CIRCUIT_PURPOSE_UNKNOWN; -} - -/** Return a newly allocated smartlist containing the arguments to the command - * waiting in body. If there are fewer than min_args arguments, - * or if max_args is nonnegative and there are more than - * max_args arguments, send a 512 error to the controller, using - * command as the command name in the error message. */ -static smartlist_t * -getargs_helper(const char *command, control_connection_t *conn, - const char *body, int min_args, int max_args) -{ - smartlist_t *args = smartlist_new(); - smartlist_split_string(args, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (smartlist_len(args) < min_args) { - connection_printf_to_buf(conn, "512 Missing argument to %s\r\n",command); - goto err; - } else if (max_args >= 0 && smartlist_len(args) > max_args) { - connection_printf_to_buf(conn, "512 Too many arguments to %s\r\n",command); - goto err; - } - return args; - err: - SMARTLIST_FOREACH(args, char *, s, tor_free(s)); - smartlist_free(args); - return NULL; -} - -/** Helper. Return the first element of sl at index start_at or - * higher that starts with prefix, case-insensitive. Return NULL if no - * such element exists. */ -static const char * -find_element_starting_with(smartlist_t *sl, int start_at, const char *prefix) -{ - int i; - for (i = start_at; i < smartlist_len(sl); ++i) { - const char *elt = smartlist_get(sl, i); - if (!strcasecmpstart(elt, prefix)) - return elt; - } - return NULL; -} - -/** Helper. Return true iff s is an argument that we should treat as a - * key-value pair. */ -static int -is_keyval_pair(const char *s) -{ - /* An argument is a key-value pair if it has an =, and it isn't of the form - * $fingeprint=name */ - return strchr(s, '=') && s[0] != '$'; -} - -/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed - * circuit, and report success or failure. */ -static int -handle_control_extendcircuit(control_connection_t *conn, uint32_t len, - const char *body) -{ - smartlist_t *router_nicknames=NULL, *nodes=NULL; - origin_circuit_t *circ = NULL; - int zero_circ; - uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL; - smartlist_t *args; - (void) len; - - router_nicknames = smartlist_new(); - - args = getargs_helper("EXTENDCIRCUIT", conn, body, 1, -1); - if (!args) - goto done; - - zero_circ = !strcmp("0", (char*)smartlist_get(args,0)); - - if (zero_circ) { - const char *purp = find_element_starting_with(args, 1, "PURPOSE="); - - if (purp) { - intended_purpose = circuit_purpose_from_string(purp); - if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) { - connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp); - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - goto done; - } - } - - if ((smartlist_len(args) == 1) || - (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) { - // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar" - circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY); - if (!circ) { - connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); - } else { - connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n", - (unsigned long)circ->global_identifier); - } - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - goto done; - } - // "EXTENDCIRCUIT 0 router1,router2" || - // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo" - } - - if (!zero_circ && !(circ = get_circ(smartlist_get(args,0)))) { - connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - goto done; - } - - smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0); - - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - - nodes = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) { - const node_t *node = node_get_by_nickname(n, 1); - if (!node) { - connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n); - goto done; - } - if (!node_has_descriptor(node)) { - connection_printf_to_buf(conn, "552 descriptor for \"%s\"\r\n", n); - goto done; - } - smartlist_add(nodes, (void*)node); - } SMARTLIST_FOREACH_END(n); - if (!smartlist_len(nodes)) { - connection_write_str_to_buf("512 No router names provided\r\n", conn); - goto done; - } - - if (zero_circ) { - /* start a new circuit */ - circ = origin_circuit_init(intended_purpose, 0); - } - - /* now circ refers to something that is ready to be extended */ - SMARTLIST_FOREACH(nodes, const node_t *, node, - { - extend_info_t *info = extend_info_from_node(node, 0); - tor_assert(info); /* True, since node_has_descriptor(node) == true */ - circuit_append_new_exit(circ, info); - extend_info_free(info); - }); - - /* now that we've populated the cpath, start extending */ - if (zero_circ) { - int err_reason = 0; - if ((err_reason = circuit_handle_first_hop(circ)) < 0) { - circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason); - connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); - goto done; - } - } else { - if (circ->base_.state == CIRCUIT_STATE_OPEN) { - int err_reason = 0; - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); - if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { - log_info(LD_CONTROL, - "send_next_onion_skin failed; circuit marked for closing."); - circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason); - connection_write_str_to_buf("551 Couldn't send onion skin\r\n", conn); - goto done; - } - } - } - - connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n", - (unsigned long)circ->global_identifier); - if (zero_circ) /* send a 'launched' event, for completeness */ - control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED, 0); - done: - SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n)); - smartlist_free(router_nicknames); - smartlist_free(nodes); - return 0; -} - -/** Called when we get a SETCIRCUITPURPOSE message. If we can find the - * circuit and it's a valid purpose, change it. */ -static int -handle_control_setcircuitpurpose(control_connection_t *conn, - uint32_t len, const char *body) -{ - origin_circuit_t *circ = NULL; - uint8_t new_purpose; - smartlist_t *args; - (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */ - - args = getargs_helper("SETCIRCUITPURPOSE", conn, body, 2, -1); - if (!args) - goto done; - - if (!(circ = get_circ(smartlist_get(args,0)))) { - connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - goto done; - } - - { - const char *purp = find_element_starting_with(args,1,"PURPOSE="); - if (!purp) { - connection_write_str_to_buf("552 No purpose given\r\n", conn); - goto done; - } - new_purpose = circuit_purpose_from_string(purp); - if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) { - connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp); - goto done; - } - } - - circuit_change_purpose(TO_CIRCUIT(circ), new_purpose); - connection_write_str_to_buf("250 OK\r\n", conn); - - done: - if (args) { - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - } - return 0; -} - -/** Called when we get an ATTACHSTREAM message. Try to attach the requested - * stream, and report success or failure. */ -static int -handle_control_attachstream(control_connection_t *conn, uint32_t len, - const char *body) -{ - entry_connection_t *ap_conn = NULL; - origin_circuit_t *circ = NULL; - int zero_circ; - smartlist_t *args; - crypt_path_t *cpath=NULL; - int hop=0, hop_line_ok=1; - (void) len; - - args = getargs_helper("ATTACHSTREAM", conn, body, 2, -1); - if (!args) - return 0; - - zero_circ = !strcmp("0", (char*)smartlist_get(args,1)); - - if (!(ap_conn = get_stream(smartlist_get(args, 0)))) { - connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - } else if (!zero_circ && !(circ = get_circ(smartlist_get(args, 1)))) { - connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", - (char*)smartlist_get(args, 1)); - } else if (circ) { - const char *hopstring = find_element_starting_with(args,2,"HOP="); - if (hopstring) { - hopstring += strlen("HOP="); - hop = (int) tor_parse_ulong(hopstring, 10, 0, INT_MAX, - &hop_line_ok, NULL); - if (!hop_line_ok) { /* broken hop line */ - connection_printf_to_buf(conn, "552 Bad value hop=%s\r\n", hopstring); - } - } - } - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok) - return 0; - - if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT && - ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT && - ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) { - connection_write_str_to_buf( - "555 Connection is not managed by controller.\r\n", - conn); - return 0; - } - - /* Do we need to detach it first? */ - if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) { - edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); - circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn); - connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT); - /* Un-mark it as ending, since we're going to reuse it. */ - edge_conn->edge_has_sent_end = 0; - edge_conn->end_reason = 0; - if (tmpcirc) - circuit_detach_stream(tmpcirc, edge_conn); - TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; - } - - if (circ && (circ->base_.state != CIRCUIT_STATE_OPEN)) { - connection_write_str_to_buf( - "551 Can't attach stream to non-open origin circuit\r\n", - conn); - return 0; - } - /* Is this a single hop circuit? */ - if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) { - const node_t *node = NULL; - char *exit_digest; - if (circ->build_state && - circ->build_state->chosen_exit && - !tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) { - exit_digest = circ->build_state->chosen_exit->identity_digest; - node = node_get_by_id(exit_digest); - } - /* Do both the client and relay allow one-hop exit circuits? */ - if (!node || - !node_allows_single_hop_exits(node) || - !get_options()->AllowSingleHopCircuits) { - connection_write_str_to_buf( - "551 Can't attach stream to this one-hop circuit.\r\n", conn); - return 0; - } - ap_conn->chosen_exit_name = tor_strdup(hex_str(exit_digest, DIGEST_LEN)); - } - - if (circ && hop>0) { - /* find this hop in the circuit, and set cpath */ - cpath = circuit_get_cpath_hop(circ, hop); - if (!cpath) { - connection_printf_to_buf(conn, - "551 Circuit doesn't have %d hops.\r\n", hop); - return 0; - } - } - if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) { - connection_write_str_to_buf("551 Unable to attach stream\r\n", conn); - return 0; - } - send_control_done(conn); - return 0; -} - -/** Called when we get a POSTDESCRIPTOR message. Try to learn the provided - * descriptor, and report success or failure. */ -static int -handle_control_postdescriptor(control_connection_t *conn, uint32_t len, - const char *body) -{ - char *desc; - const char *msg=NULL; - uint8_t purpose = ROUTER_PURPOSE_GENERAL; - int cache = 0; /* eventually, we may switch this to 1 */ - - char *cp = memchr(body, '\n', len); - smartlist_t *args = smartlist_new(); - tor_assert(cp); - *cp++ = '\0'; - - smartlist_split_string(args, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(args, char *, option) { - if (!strcasecmpstart(option, "purpose=")) { - option += strlen("purpose="); - purpose = router_purpose_from_string(option); - if (purpose == ROUTER_PURPOSE_UNKNOWN) { - connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", - option); - goto done; - } - } else if (!strcasecmpstart(option, "cache=")) { - option += strlen("cache="); - if (!strcasecmp(option, "no")) - cache = 0; - else if (!strcasecmp(option, "yes")) - cache = 1; - else { - connection_printf_to_buf(conn, "552 Unknown cache request \"%s\"\r\n", - option); - goto done; - } - } else { /* unrecognized argument? */ - connection_printf_to_buf(conn, - "512 Unexpected argument \"%s\" to postdescriptor\r\n", option); - goto done; - } - } SMARTLIST_FOREACH_END(option); - - read_escaped_data(cp, len-(cp-body), &desc); - - switch (router_load_single_router(desc, purpose, cache, &msg)) { - case -1: - if (!msg) msg = "Could not parse descriptor"; - connection_printf_to_buf(conn, "554 %s\r\n", msg); - break; - case 0: - if (!msg) msg = "Descriptor not added"; - connection_printf_to_buf(conn, "251 %s\r\n",msg); - break; - case 1: - send_control_done(conn); - break; - } - - tor_free(desc); - done: - SMARTLIST_FOREACH(args, char *, arg, tor_free(arg)); - smartlist_free(args); - return 0; -} - -/** Called when we receive a REDIRECTSTERAM command. Try to change the target - * address of the named AP stream, and report success or failure. */ -static int -handle_control_redirectstream(control_connection_t *conn, uint32_t len, - const char *body) -{ - entry_connection_t *ap_conn = NULL; - char *new_addr = NULL; - uint16_t new_port = 0; - smartlist_t *args; - (void) len; - - args = getargs_helper("REDIRECTSTREAM", conn, body, 2, -1); - if (!args) - return 0; - - if (!(ap_conn = get_stream(smartlist_get(args, 0))) - || !ap_conn->socks_request) { - connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - } else { - int ok = 1; - if (smartlist_len(args) > 2) { /* they included a port too */ - new_port = (uint16_t) tor_parse_ulong(smartlist_get(args, 2), - 10, 1, 65535, &ok, NULL); - } - if (!ok) { - connection_printf_to_buf(conn, "512 Cannot parse port \"%s\"\r\n", - (char*)smartlist_get(args, 2)); - } else { - new_addr = tor_strdup(smartlist_get(args, 1)); - } - } - - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - if (!new_addr) - return 0; - - strlcpy(ap_conn->socks_request->address, new_addr, - sizeof(ap_conn->socks_request->address)); - if (new_port) - ap_conn->socks_request->port = new_port; - tor_free(new_addr); - send_control_done(conn); - return 0; -} - -/** Called when we get a CLOSESTREAM command; try to close the named stream - * and report success or failure. */ -static int -handle_control_closestream(control_connection_t *conn, uint32_t len, - const char *body) -{ - entry_connection_t *ap_conn=NULL; - uint8_t reason=0; - smartlist_t *args; - int ok; - (void) len; - - args = getargs_helper("CLOSESTREAM", conn, body, 2, -1); - if (!args) - return 0; - - else if (!(ap_conn = get_stream(smartlist_get(args, 0)))) - connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - else { - reason = (uint8_t) tor_parse_ulong(smartlist_get(args,1), 10, 0, 255, - &ok, NULL); - if (!ok) { - connection_printf_to_buf(conn, "552 Unrecognized reason \"%s\"\r\n", - (char*)smartlist_get(args, 1)); - ap_conn = NULL; - } - } - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - if (!ap_conn) - return 0; - - connection_mark_unattached_ap(ap_conn, reason); - send_control_done(conn); - return 0; -} - -/** Called when we get a CLOSECIRCUIT command; try to close the named circuit - * and report success or failure. */ -static int -handle_control_closecircuit(control_connection_t *conn, uint32_t len, - const char *body) -{ - origin_circuit_t *circ = NULL; - int safe = 0; - smartlist_t *args; - (void) len; - - args = getargs_helper("CLOSECIRCUIT", conn, body, 1, -1); - if (!args) - return 0; - - if (!(circ=get_circ(smartlist_get(args, 0)))) - connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", - (char*)smartlist_get(args, 0)); - else { - int i; - for (i=1; i < smartlist_len(args); ++i) { - if (!strcasecmp(smartlist_get(args, i), "IfUnused")) - safe = 1; - else - log_info(LD_CONTROL, "Skipping unknown option %s", - (char*)smartlist_get(args,i)); - } - } - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - if (!circ) - return 0; - - if (!safe || !circ->p_streams) { - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_REQUESTED); - } - - send_control_done(conn); - return 0; -} - -/** Called when we get a RESOLVE command: start trying to resolve - * the listed addresses. */ -static int -handle_control_resolve(control_connection_t *conn, uint32_t len, - const char *body) -{ - smartlist_t *args, *failed; - int is_reverse = 0; - (void) len; /* body is nul-terminated; it's safe to ignore the length */ - - if (!(conn->event_mask & ((uint32_t)1L<have_sent_protocolinfo = 1; - args = smartlist_new(); - smartlist_split_string(args, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH(args, const char *, arg, { - int ok; - tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL); - if (!ok) { - bad_arg = arg; - break; - } - }); - if (bad_arg) { - connection_printf_to_buf(conn, "513 No such version %s\r\n", - escaped(bad_arg)); - /* Don't tolerate bad arguments when not authenticated. */ - if (!STATE_IS_OPEN(TO_CONN(conn)->state)) - connection_mark_for_close(TO_CONN(conn)); - goto done; - } else { - const or_options_t *options = get_options(); - int cookies = options->CookieAuthentication; - char *cfile = get_cookie_file(); - char *abs_cfile; - char *esc_cfile; - char *methods; - abs_cfile = make_path_absolute(cfile); - esc_cfile = esc_for_log(abs_cfile); - { - int passwd = (options->HashedControlPassword != NULL || - options->HashedControlSessionPassword != NULL); - smartlist_t *mlist = smartlist_new(); - if (cookies) { - smartlist_add(mlist, (char*)"COOKIE"); - smartlist_add(mlist, (char*)"SAFECOOKIE"); - } - if (passwd) - smartlist_add(mlist, (char*)"HASHEDPASSWORD"); - if (!cookies && !passwd) - smartlist_add(mlist, (char*)"NULL"); - methods = smartlist_join_strings(mlist, ",", 0, NULL); - smartlist_free(mlist); - } - - connection_printf_to_buf(conn, - "250-PROTOCOLINFO 1\r\n" - "250-AUTH METHODS=%s%s%s\r\n" - "250-VERSION Tor=%s\r\n" - "250 OK\r\n", - methods, - cookies?" COOKIEFILE=":"", - cookies?esc_cfile:"", - escaped(VERSION)); - tor_free(methods); - tor_free(cfile); - tor_free(abs_cfile); - tor_free(esc_cfile); - } - done: - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - return 0; -} - -/** Called when we get an AUTHCHALLENGE command. */ -static int -handle_control_authchallenge(control_connection_t *conn, uint32_t len, - const char *body) -{ - const char *cp = body; - char *client_nonce; - size_t client_nonce_len; - char server_hash[DIGEST256_LEN]; - char server_hash_encoded[HEX_DIGEST256_LEN+1]; - char server_nonce[SAFECOOKIE_SERVER_NONCE_LEN]; - char server_nonce_encoded[(2*SAFECOOKIE_SERVER_NONCE_LEN) + 1]; - - cp += strspn(cp, " \t\n\r"); - if (!strcasecmpstart(cp, "SAFECOOKIE")) { - cp += strlen("SAFECOOKIE"); - } else { - connection_write_str_to_buf("513 AUTHCHALLENGE only supports SAFECOOKIE " - "authentication\r\n", conn); - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - - if (!authentication_cookie_is_set) { - connection_write_str_to_buf("515 Cookie authentication is disabled\r\n", - conn); - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - - cp += strspn(cp, " \t\n\r"); - if (*cp == '"') { - const char *newcp = - decode_escaped_string(cp, len - (cp - body), - &client_nonce, &client_nonce_len); - if (newcp == NULL) { - connection_write_str_to_buf("513 Invalid quoted client nonce\r\n", - conn); - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - cp = newcp; - } else { - size_t client_nonce_encoded_len = strspn(cp, "0123456789ABCDEFabcdef"); - - client_nonce_len = client_nonce_encoded_len / 2; - client_nonce = tor_malloc_zero(client_nonce_len); - - if (base16_decode(client_nonce, client_nonce_len, - cp, client_nonce_encoded_len) < 0) { - connection_write_str_to_buf("513 Invalid base16 client nonce\r\n", - conn); - connection_mark_for_close(TO_CONN(conn)); - tor_free(client_nonce); - return -1; - } - - cp += client_nonce_encoded_len; - } - - cp += strspn(cp, " \t\n\r"); - if (*cp != '\0' || - cp != body + len) { - connection_write_str_to_buf("513 Junk at end of AUTHCHALLENGE command\r\n", - conn); - connection_mark_for_close(TO_CONN(conn)); - tor_free(client_nonce); - return -1; - } - - tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN)); - - /* Now compute and send the server-to-controller response, and the - * server's nonce. */ - tor_assert(authentication_cookie != NULL); - - { - size_t tmp_len = (AUTHENTICATION_COOKIE_LEN + - client_nonce_len + - SAFECOOKIE_SERVER_NONCE_LEN); - char *tmp = tor_malloc_zero(tmp_len); - char *client_hash = tor_malloc_zero(DIGEST256_LEN); - memcpy(tmp, authentication_cookie, AUTHENTICATION_COOKIE_LEN); - memcpy(tmp + AUTHENTICATION_COOKIE_LEN, client_nonce, client_nonce_len); - memcpy(tmp + AUTHENTICATION_COOKIE_LEN + client_nonce_len, - server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); - - crypto_hmac_sha256(server_hash, - SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT, - strlen(SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT), - tmp, - tmp_len); - - crypto_hmac_sha256(client_hash, - SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT, - strlen(SAFECOOKIE_CONTROLLER_TO_SERVER_CONSTANT), - tmp, - tmp_len); - - conn->safecookie_client_hash = client_hash; - - tor_free(tmp); - } - - base16_encode(server_hash_encoded, sizeof(server_hash_encoded), - server_hash, sizeof(server_hash)); - base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), - server_nonce, sizeof(server_nonce)); - - connection_printf_to_buf(conn, - "250 AUTHCHALLENGE SERVERHASH=%s " - "SERVERNONCE=%s\r\n", - server_hash_encoded, - server_nonce_encoded); - - tor_free(client_nonce); - return 0; -} - -/** Called when we get a USEFEATURE command: parse the feature list, and - * set up the control_connection's options properly. */ -static int -handle_control_usefeature(control_connection_t *conn, - uint32_t len, - const char *body) -{ - smartlist_t *args; - int bad = 0; - (void) len; /* body is nul-terminated; it's safe to ignore the length */ - args = smartlist_new(); - smartlist_split_string(args, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - SMARTLIST_FOREACH_BEGIN(args, const char *, arg) { - if (!strcasecmp(arg, "VERBOSE_NAMES")) - ; - else if (!strcasecmp(arg, "EXTENDED_EVENTS")) - ; - else { - connection_printf_to_buf(conn, "552 Unrecognized feature \"%s\"\r\n", - arg); - bad = 1; - break; - } - } SMARTLIST_FOREACH_END(arg); - - if (!bad) { - send_control_done(conn); - } - - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - return 0; -} - -/** Implementation for the DROPGUARDS command. */ -static int -handle_control_dropguards(control_connection_t *conn, - uint32_t len, - const char *body) -{ - smartlist_t *args; - (void) len; /* body is nul-terminated; it's safe to ignore the length */ - args = smartlist_new(); - smartlist_split_string(args, body, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - - if (smartlist_len(args)) { - connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n"); - } else { - remove_all_entry_guards(); - send_control_done(conn); - } - - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - return 0; -} - -/** Called when conn has no more bytes left on its outbuf. */ -int -connection_control_finished_flushing(control_connection_t *conn) -{ - tor_assert(conn); - return 0; -} - -/** Called when conn has gotten its socket closed. */ -int -connection_control_reached_eof(control_connection_t *conn) -{ - tor_assert(conn); - - log_info(LD_CONTROL,"Control connection reached EOF. Closing."); - connection_mark_for_close(TO_CONN(conn)); - return 0; -} - -/** Shut down this Tor instance in the same way that SIGINT would, but - * with a log message appropriate for the loss of an owning controller. */ -static void -lost_owning_controller(const char *owner_type, const char *loss_manner) -{ - int shutdown_slowly = server_mode(get_options()); - - log_notice(LD_CONTROL, "Owning controller %s has %s -- %s.", - owner_type, loss_manner, - shutdown_slowly ? "shutting down" : "exiting now"); - - /* XXXX Perhaps this chunk of code should be a separate function, - * called here and by process_signal(SIGINT). */ - - if (!shutdown_slowly) { - tor_cleanup(); - exit(0); - } - /* XXXX This will close all listening sockets except control-port - * listeners. Perhaps we should close those too. */ - hibernate_begin_shutdown(); -} - -/** Called when conn is being freed. */ -void -connection_control_closed(control_connection_t *conn) -{ - tor_assert(conn); - - conn->event_mask = 0; - control_update_global_event_mask(); - - if (conn->is_owning_control_connection) { - lost_owning_controller("connection", "closed"); - } -} - -/** Return true iff cmd is allowable (or at least forgivable) at this - * stage of the protocol. */ -static int -is_valid_initial_command(control_connection_t *conn, const char *cmd) -{ - if (conn->base_.state == CONTROL_CONN_STATE_OPEN) - return 1; - if (!strcasecmp(cmd, "PROTOCOLINFO")) - return (!conn->have_sent_protocolinfo && - conn->safecookie_client_hash == NULL); - if (!strcasecmp(cmd, "AUTHCHALLENGE")) - return (conn->safecookie_client_hash == NULL); - if (!strcasecmp(cmd, "AUTHENTICATE") || - !strcasecmp(cmd, "QUIT")) - return 1; - return 0; -} - -/** Do not accept any control command of more than 1MB in length. Anything - * that needs to be anywhere near this long probably means that one of our - * interfaces is broken. */ -#define MAX_COMMAND_LINE_LENGTH (1024*1024) - -/** Wrapper around peek_(evbuffer|buf)_has_control0 command: presents the same - * interface as those underlying functions, but takes a connection_t intead of - * an evbuffer or a buf_t. - */ -static int -peek_connection_has_control0_command(connection_t *conn) -{ - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return peek_evbuffer_has_control0_command(input); - }) ELSE_IF_NO_BUFFEREVENT { - return peek_buf_has_control0_command(conn->inbuf); - } -} - -/** Called when data has arrived on a v1 control connection: Try to fetch - * commands from conn->inbuf, and execute them. - */ -int -connection_control_process_inbuf(control_connection_t *conn) -{ - size_t data_len; - uint32_t cmd_data_len; - int cmd_len; - char *args; - - tor_assert(conn); - tor_assert(conn->base_.state == CONTROL_CONN_STATE_OPEN || - conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH); - - if (!conn->incoming_cmd) { - conn->incoming_cmd = tor_malloc(1024); - conn->incoming_cmd_len = 1024; - conn->incoming_cmd_cur_len = 0; - } - - if (conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH && - peek_connection_has_control0_command(TO_CONN(conn))) { - /* Detect v0 commands and send a "no more v0" message. */ - size_t body_len; - char buf[128]; - set_uint16(buf+2, htons(0x0000)); /* type == error */ - set_uint16(buf+4, htons(0x0001)); /* code == internal error */ - strlcpy(buf+6, "The v0 control protocol is not supported by Tor 0.1.2.17 " - "and later; upgrade your controller.", - sizeof(buf)-6); - body_len = 2+strlen(buf+6)+2; /* code, msg, nul. */ - set_uint16(buf+0, htons(body_len)); - connection_write_to_buf(buf, 4+body_len, TO_CONN(conn)); - - connection_mark_and_flush(TO_CONN(conn)); - return 0; - } - - again: - while (1) { - size_t last_idx; - int r; - /* First, fetch a line. */ - do { - data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len; - r = connection_fetch_from_buf_line(TO_CONN(conn), - conn->incoming_cmd+conn->incoming_cmd_cur_len, - &data_len); - if (r == 0) - /* Line not all here yet. Wait. */ - return 0; - else if (r == -1) { - if (data_len + conn->incoming_cmd_cur_len > MAX_COMMAND_LINE_LENGTH) { - connection_write_str_to_buf("500 Line too long.\r\n", conn); - connection_stop_reading(TO_CONN(conn)); - connection_mark_and_flush(TO_CONN(conn)); - } - while (conn->incoming_cmd_len < data_len+conn->incoming_cmd_cur_len) - conn->incoming_cmd_len *= 2; - conn->incoming_cmd = tor_realloc(conn->incoming_cmd, - conn->incoming_cmd_len); - } - } while (r != 1); - - tor_assert(data_len); - - last_idx = conn->incoming_cmd_cur_len; - conn->incoming_cmd_cur_len += (int)data_len; - - /* We have appended a line to incoming_cmd. Is the command done? */ - if (last_idx == 0 && *conn->incoming_cmd != '+') - /* One line command, didn't start with '+'. */ - break; - /* XXXX this code duplication is kind of dumb. */ - if (last_idx+3 == conn->incoming_cmd_cur_len && - tor_memeq(conn->incoming_cmd + last_idx, ".\r\n", 3)) { - /* Just appended ".\r\n"; we're done. Remove it. */ - conn->incoming_cmd[last_idx] = '\0'; - conn->incoming_cmd_cur_len -= 3; - break; - } else if (last_idx+2 == conn->incoming_cmd_cur_len && - tor_memeq(conn->incoming_cmd + last_idx, ".\n", 2)) { - /* Just appended ".\n"; we're done. Remove it. */ - conn->incoming_cmd[last_idx] = '\0'; - conn->incoming_cmd_cur_len -= 2; - break; - } - /* Otherwise, read another line. */ - } - data_len = conn->incoming_cmd_cur_len; - /* Okay, we now have a command sitting on conn->incoming_cmd. See if we - * recognize it. - */ - cmd_len = 0; - while ((size_t)cmd_len < data_len - && !TOR_ISSPACE(conn->incoming_cmd[cmd_len])) - ++cmd_len; - - conn->incoming_cmd[cmd_len]='\0'; - args = conn->incoming_cmd+cmd_len+1; - tor_assert(data_len>(size_t)cmd_len); - data_len -= (cmd_len+1); /* skip the command and NUL we added after it */ - while (TOR_ISSPACE(*args)) { - ++args; - --data_len; - } - - /* If the connection is already closing, ignore further commands */ - if (TO_CONN(conn)->marked_for_close) { - return 0; - } - - /* Otherwise, Quit is always valid. */ - if (!strcasecmp(conn->incoming_cmd, "QUIT")) { - connection_write_str_to_buf("250 closing connection\r\n", conn); - connection_mark_and_flush(TO_CONN(conn)); - return 0; - } - - if (conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH && - !is_valid_initial_command(conn, conn->incoming_cmd)) { - connection_write_str_to_buf("514 Authentication required.\r\n", conn); - connection_mark_for_close(TO_CONN(conn)); - return 0; - } - - if (data_len >= UINT32_MAX) { - connection_write_str_to_buf("500 A 4GB command? Nice try.\r\n", conn); - connection_mark_for_close(TO_CONN(conn)); - return 0; - } - - /* XXXX Why is this not implemented as a table like the GETINFO - * items are? Even handling the plus signs at the beginnings of - * commands wouldn't be very hard with proper macros. */ - cmd_data_len = (uint32_t)data_len; - if (!strcasecmp(conn->incoming_cmd, "SETCONF")) { - if (handle_control_setconf(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "RESETCONF")) { - if (handle_control_resetconf(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "GETCONF")) { - if (handle_control_getconf(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "+LOADCONF")) { - if (handle_control_loadconf(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "SETEVENTS")) { - if (handle_control_setevents(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "AUTHENTICATE")) { - if (handle_control_authenticate(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "SAVECONF")) { - if (handle_control_saveconf(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) { - if (handle_control_signal(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) { - if (handle_control_takeownership(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) { - if (handle_control_mapaddress(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "GETINFO")) { - if (handle_control_getinfo(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "EXTENDCIRCUIT")) { - if (handle_control_extendcircuit(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "SETCIRCUITPURPOSE")) { - if (handle_control_setcircuitpurpose(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "SETROUTERPURPOSE")) { - connection_write_str_to_buf("511 SETROUTERPURPOSE is obsolete.\r\n", conn); - } else if (!strcasecmp(conn->incoming_cmd, "ATTACHSTREAM")) { - if (handle_control_attachstream(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "+POSTDESCRIPTOR")) { - if (handle_control_postdescriptor(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "REDIRECTSTREAM")) { - if (handle_control_redirectstream(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "CLOSESTREAM")) { - if (handle_control_closestream(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "CLOSECIRCUIT")) { - if (handle_control_closecircuit(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "USEFEATURE")) { - if (handle_control_usefeature(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) { - if (handle_control_resolve(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "PROTOCOLINFO")) { - if (handle_control_protocolinfo(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "AUTHCHALLENGE")) { - if (handle_control_authchallenge(conn, cmd_data_len, args)) - return -1; - } else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) { - if (handle_control_dropguards(conn, cmd_data_len, args)) - return -1; - } else { - connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n", - conn->incoming_cmd); - } - - conn->incoming_cmd_cur_len = 0; - goto again; -} - -/** Something major has happened to circuit circ: tell any - * interested control connections. */ -int -control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp, - int reason_code) -{ - const char *status; - char reasons[64] = ""; - if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS)) - return 0; - tor_assert(circ); - - switch (tp) - { - case CIRC_EVENT_LAUNCHED: status = "LAUNCHED"; break; - case CIRC_EVENT_BUILT: status = "BUILT"; break; - case CIRC_EVENT_EXTENDED: status = "EXTENDED"; break; - case CIRC_EVENT_FAILED: status = "FAILED"; break; - case CIRC_EVENT_CLOSED: status = "CLOSED"; break; - default: - log_warn(LD_BUG, "Unrecognized status code %d", (int)tp); - tor_fragile_assert(); - return 0; - } - - if (tp == CIRC_EVENT_FAILED || tp == CIRC_EVENT_CLOSED) { - const char *reason_str = circuit_end_reason_to_control_string(reason_code); - char unk_reason_buf[16]; - if (!reason_str) { - tor_snprintf(unk_reason_buf, 16, "UNKNOWN_%d", reason_code); - reason_str = unk_reason_buf; - } - if (reason_code > 0 && reason_code & END_CIRC_REASON_FLAG_REMOTE) { - tor_snprintf(reasons, sizeof(reasons), - " REASON=DESTROYED REMOTE_REASON=%s", reason_str); - } else { - tor_snprintf(reasons, sizeof(reasons), - " REASON=%s", reason_str); - } - } - - { - char *circdesc = circuit_describe_status_for_controller(circ); - const char *sp = strlen(circdesc) ? " " : ""; - send_control_event(EVENT_CIRCUIT_STATUS, ALL_FORMATS, - "650 CIRC %lu %s%s%s%s\r\n", - (unsigned long)circ->global_identifier, - status, sp, - circdesc, - reasons); - tor_free(circdesc); - } - - return 0; -} - -/** Something minor has happened to circuit circ: tell any - * interested control connections. */ -static int -control_event_circuit_status_minor(origin_circuit_t *circ, - circuit_status_minor_event_t e, - int purpose, const struct timeval *tv) -{ - const char *event_desc; - char event_tail[160] = ""; - if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS_MINOR)) - return 0; - tor_assert(circ); - - switch (e) - { - case CIRC_MINOR_EVENT_PURPOSE_CHANGED: - event_desc = "PURPOSE_CHANGED"; - - { - /* event_tail can currently be up to 68 chars long */ - const char *hs_state_str = - circuit_purpose_to_controller_hs_state_string(purpose); - tor_snprintf(event_tail, sizeof(event_tail), - " OLD_PURPOSE=%s%s%s", - circuit_purpose_to_controller_string(purpose), - (hs_state_str != NULL) ? " OLD_HS_STATE=" : "", - (hs_state_str != NULL) ? hs_state_str : ""); - } - - break; - case CIRC_MINOR_EVENT_CANNIBALIZED: - event_desc = "CANNIBALIZED"; - - { - /* event_tail can currently be up to 130 chars long */ - const char *hs_state_str = - circuit_purpose_to_controller_hs_state_string(purpose); - const struct timeval *old_timestamp_began = tv; - char tbuf[ISO_TIME_USEC_LEN+1]; - format_iso_time_nospace_usec(tbuf, old_timestamp_began); - - tor_snprintf(event_tail, sizeof(event_tail), - " OLD_PURPOSE=%s%s%s OLD_TIME_CREATED=%s", - circuit_purpose_to_controller_string(purpose), - (hs_state_str != NULL) ? " OLD_HS_STATE=" : "", - (hs_state_str != NULL) ? hs_state_str : "", - tbuf); - } - - break; - default: - log_warn(LD_BUG, "Unrecognized status code %d", (int)e); - tor_fragile_assert(); - return 0; - } - - { - char *circdesc = circuit_describe_status_for_controller(circ); - const char *sp = strlen(circdesc) ? " " : ""; - send_control_event(EVENT_CIRCUIT_STATUS_MINOR, ALL_FORMATS, - "650 CIRC_MINOR %lu %s%s%s%s\r\n", - (unsigned long)circ->global_identifier, - event_desc, sp, - circdesc, - event_tail); - tor_free(circdesc); - } - - return 0; -} - -/** - * circ has changed its purpose from old_purpose: tell any - * interested controllers. - */ -int -control_event_circuit_purpose_changed(origin_circuit_t *circ, - int old_purpose) -{ - return control_event_circuit_status_minor(circ, - CIRC_MINOR_EVENT_PURPOSE_CHANGED, - old_purpose, - NULL); -} - -/** - * circ has changed its purpose from old_purpose, and its - * created-time from old_tv_created: tell any interested controllers. - */ -int -control_event_circuit_cannibalized(origin_circuit_t *circ, - int old_purpose, - const struct timeval *old_tv_created) -{ - return control_event_circuit_status_minor(circ, - CIRC_MINOR_EVENT_CANNIBALIZED, - old_purpose, - old_tv_created); -} - -/** Given an AP connection conn and a len-character buffer - * buf, determine the address:port combination requested on - * conn, and write it to buf. Return 0 on success, -1 on - * failure. */ -static int -write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len) -{ - char buf2[256]; - if (conn->chosen_exit_name) - if (tor_snprintf(buf2, sizeof(buf2), ".%s.exit", conn->chosen_exit_name)<0) - return -1; - if (!conn->socks_request) - return -1; - if (tor_snprintf(buf, len, "%s%s%s:%d", - conn->socks_request->address, - conn->chosen_exit_name ? buf2 : "", - !conn->chosen_exit_name && connection_edge_is_rendezvous_stream( - ENTRY_TO_EDGE_CONN(conn)) ? ".onion" : "", - conn->socks_request->port)<0) - return -1; - return 0; -} - -/** Something has happened to the stream associated with AP connection - * conn: tell any interested control connections. */ -int -control_event_stream_status(entry_connection_t *conn, stream_status_event_t tp, - int reason_code) -{ - char reason_buf[64]; - char addrport_buf[64]; - const char *status; - circuit_t *circ; - origin_circuit_t *origin_circ = NULL; - char buf[256]; - const char *purpose = ""; - tor_assert(conn->socks_request); - - if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS)) - return 0; - - if (tp == STREAM_EVENT_CLOSED && - (reason_code & END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED)) - return 0; - - write_stream_target_to_buf(conn, buf, sizeof(buf)); - - reason_buf[0] = '\0'; - switch (tp) - { - case STREAM_EVENT_SENT_CONNECT: status = "SENTCONNECT"; break; - case STREAM_EVENT_SENT_RESOLVE: status = "SENTRESOLVE"; break; - case STREAM_EVENT_SUCCEEDED: status = "SUCCEEDED"; break; - case STREAM_EVENT_FAILED: status = "FAILED"; break; - case STREAM_EVENT_CLOSED: status = "CLOSED"; break; - case STREAM_EVENT_NEW: status = "NEW"; break; - case STREAM_EVENT_NEW_RESOLVE: status = "NEWRESOLVE"; break; - case STREAM_EVENT_FAILED_RETRIABLE: status = "DETACHED"; break; - case STREAM_EVENT_REMAP: status = "REMAP"; break; - default: - log_warn(LD_BUG, "Unrecognized status code %d", (int)tp); - return 0; - } - if (reason_code && (tp == STREAM_EVENT_FAILED || - tp == STREAM_EVENT_CLOSED || - tp == STREAM_EVENT_FAILED_RETRIABLE)) { - const char *reason_str = stream_end_reason_to_control_string(reason_code); - char *r = NULL; - if (!reason_str) { - tor_asprintf(&r, " UNKNOWN_%d", reason_code); - reason_str = r; - } - if (reason_code & END_STREAM_REASON_FLAG_REMOTE) - tor_snprintf(reason_buf, sizeof(reason_buf), - " REASON=END REMOTE_REASON=%s", reason_str); - else - tor_snprintf(reason_buf, sizeof(reason_buf), - " REASON=%s", reason_str); - tor_free(r); - } else if (reason_code && tp == STREAM_EVENT_REMAP) { - switch (reason_code) { - case REMAP_STREAM_SOURCE_CACHE: - strlcpy(reason_buf, " SOURCE=CACHE", sizeof(reason_buf)); - break; - case REMAP_STREAM_SOURCE_EXIT: - strlcpy(reason_buf, " SOURCE=EXIT", sizeof(reason_buf)); - break; - default: - tor_snprintf(reason_buf, sizeof(reason_buf), " REASON=UNKNOWN_%d", - reason_code); - /* XXX do we want SOURCE=UNKNOWN_%d above instead? -RD */ - break; - } - } - - if (tp == STREAM_EVENT_NEW || tp == STREAM_EVENT_NEW_RESOLVE) { - /* - * When the control conn is an AF_UNIX socket and we have no address, - * it gets set to "(Tor_internal)"; see dnsserv_launch_request() in - * dnsserv.c. - */ - if (strcmp(ENTRY_TO_CONN(conn)->address, "(Tor_internal)") != 0) { - tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d", - ENTRY_TO_CONN(conn)->address, ENTRY_TO_CONN(conn)->port); - } else { - /* - * else leave it blank so control on AF_UNIX doesn't need to make - * something up. - */ - addrport_buf[0] = '\0'; - } - } else { - addrport_buf[0] = '\0'; - } - - if (tp == STREAM_EVENT_NEW_RESOLVE) { - purpose = " PURPOSE=DNS_REQUEST"; - } else if (tp == STREAM_EVENT_NEW) { - if (conn->use_begindir) { - connection_t *linked = ENTRY_TO_CONN(conn)->linked_conn; - int linked_dir_purpose = -1; - if (linked && linked->type == CONN_TYPE_DIR) - linked_dir_purpose = linked->purpose; - if (DIR_PURPOSE_IS_UPLOAD(linked_dir_purpose)) - purpose = " PURPOSE=DIR_UPLOAD"; - else - purpose = " PURPOSE=DIR_FETCH"; - } else - purpose = " PURPOSE=USER"; - } - - circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn)); - if (circ && CIRCUIT_IS_ORIGIN(circ)) - origin_circ = TO_ORIGIN_CIRCUIT(circ); - send_control_event(EVENT_STREAM_STATUS, ALL_FORMATS, - "650 STREAM "U64_FORMAT" %s %lu %s%s%s%s\r\n", - U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier), - status, - origin_circ? - (unsigned long)origin_circ->global_identifier : 0ul, - buf, reason_buf, addrport_buf, purpose); - - /* XXX need to specify its intended exit, etc? */ - - return 0; -} - -/** Figure out the best name for the target router of an OR connection - * conn, and write it into the len-character buffer - * name. */ -static void -orconn_target_get_name(char *name, size_t len, or_connection_t *conn) -{ - const node_t *node = node_get_by_id(conn->identity_digest); - if (node) { - tor_assert(len > MAX_VERBOSE_NICKNAME_LEN); - node_get_verbose_nickname(node, name); - } else if (! tor_digest_is_zero(conn->identity_digest)) { - name[0] = '$'; - base16_encode(name+1, len-1, conn->identity_digest, - DIGEST_LEN); - } else { - tor_snprintf(name, len, "%s:%d", - conn->base_.address, conn->base_.port); - } -} - -/** Called when the status of an OR connection conn changes: tell any - * interested control connections. tp is the new status for the - * connection. If conn has just closed or failed, then reason - * may be the reason why. - */ -int -control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, - int reason) -{ - int ncircs = 0; - const char *status; - char name[128]; - char ncircs_buf[32] = {0}; /* > 8 + log10(2^32)=10 + 2 */ - - if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS)) - return 0; - - switch (tp) - { - case OR_CONN_EVENT_LAUNCHED: status = "LAUNCHED"; break; - case OR_CONN_EVENT_CONNECTED: status = "CONNECTED"; break; - case OR_CONN_EVENT_FAILED: status = "FAILED"; break; - case OR_CONN_EVENT_CLOSED: status = "CLOSED"; break; - case OR_CONN_EVENT_NEW: status = "NEW"; break; - default: - log_warn(LD_BUG, "Unrecognized status code %d", (int)tp); - return 0; - } - if (conn->chan) { - ncircs = circuit_count_pending_on_channel(TLS_CHAN_TO_BASE(conn->chan)); - } else { - ncircs = 0; - } - ncircs += connection_or_get_num_circuits(conn); - if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) { - tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs); - } - - orconn_target_get_name(name, sizeof(name), conn); - send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS, - "650 ORCONN %s %s%s%s%s ID="U64_FORMAT"\r\n", - name, status, - reason ? " REASON=" : "", - orconn_end_reason_to_control_string(reason), - ncircs_buf, - U64_PRINTF_ARG(conn->base_.global_identifier)); - - return 0; -} - -/** - * Print out STREAM_BW event for a single conn - */ -int -control_event_stream_bandwidth(edge_connection_t *edge_conn) -{ - circuit_t *circ; - origin_circuit_t *ocirc; - if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) { - if (!edge_conn->n_read && !edge_conn->n_written) - return 0; - - send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS, - "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n", - U64_PRINTF_ARG(edge_conn->base_.global_identifier), - (unsigned long)edge_conn->n_read, - (unsigned long)edge_conn->n_written); - - circ = circuit_get_by_edge_conn(edge_conn); - if (circ && CIRCUIT_IS_ORIGIN(circ)) { - ocirc = TO_ORIGIN_CIRCUIT(circ); - ocirc->n_read_circ_bw += edge_conn->n_read; - ocirc->n_written_circ_bw += edge_conn->n_written; - } - edge_conn->n_written = edge_conn->n_read = 0; - } - - return 0; -} - -/** A second or more has elapsed: tell any interested control - * connections how much bandwidth streams have used. */ -int -control_event_stream_bandwidth_used(void) -{ - if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) { - smartlist_t *conns = get_connection_array(); - edge_connection_t *edge_conn; - - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) - { - if (conn->type != CONN_TYPE_AP) - continue; - edge_conn = TO_EDGE_CONN(conn); - if (!edge_conn->n_read && !edge_conn->n_written) - continue; - - send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS, - "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n", - U64_PRINTF_ARG(edge_conn->base_.global_identifier), - (unsigned long)edge_conn->n_read, - (unsigned long)edge_conn->n_written); - - edge_conn->n_written = edge_conn->n_read = 0; - } - SMARTLIST_FOREACH_END(conn); - } - - return 0; -} - -/** A second or more has elapsed: tell any interested control connections - * how much bandwidth origin circuits have used. */ -int -control_event_circ_bandwidth_used(void) -{ - circuit_t *circ; - origin_circuit_t *ocirc; - if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED)) - return 0; - - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - ocirc = TO_ORIGIN_CIRCUIT(circ); - if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw) - continue; - send_control_event(EVENT_CIRC_BANDWIDTH_USED, ALL_FORMATS, - "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n", - ocirc->global_identifier, - (unsigned long)ocirc->n_read_circ_bw, - (unsigned long)ocirc->n_written_circ_bw); - ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0; - } - - return 0; -} - -/** Print out CONN_BW event for a single OR/DIR/EXIT conn and reset - * bandwidth counters. */ -int -control_event_conn_bandwidth(connection_t *conn) -{ - const char *conn_type_str; - if (!get_options()->TestingEnableConnBwEvent || - !EVENT_IS_INTERESTING(EVENT_CONN_BW)) - return 0; - if (!conn->n_read_conn_bw && !conn->n_written_conn_bw) - return 0; - switch (conn->type) { - case CONN_TYPE_OR: - conn_type_str = "OR"; - break; - case CONN_TYPE_DIR: - conn_type_str = "DIR"; - break; - case CONN_TYPE_EXIT: - conn_type_str = "EXIT"; - break; - default: - return 0; - } - send_control_event(EVENT_CONN_BW, ALL_FORMATS, - "650 CONN_BW ID="U64_FORMAT" TYPE=%s " - "READ=%lu WRITTEN=%lu\r\n", - U64_PRINTF_ARG(conn->global_identifier), - conn_type_str, - (unsigned long)conn->n_read_conn_bw, - (unsigned long)conn->n_written_conn_bw); - conn->n_written_conn_bw = conn->n_read_conn_bw = 0; - return 0; -} - -/** A second or more has elapsed: tell any interested control - * connections how much bandwidth connections have used. */ -int -control_event_conn_bandwidth_used(void) -{ - if (get_options()->TestingEnableConnBwEvent && - EVENT_IS_INTERESTING(EVENT_CONN_BW)) { - SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn, - control_event_conn_bandwidth(conn)); - } - return 0; -} - -/** Helper: iterate over cell statistics of circ and sum up added - * cells, removed cells, and waiting times by cell command and direction. - * Store results in cell_stats. Free cell statistics of the - * circuit afterwards. */ -void -sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) -{ - memset(cell_stats, 0, sizeof(cell_stats_t)); - SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats, - testing_cell_stats_entry_t *, ent) { - tor_assert(ent->command <= CELL_COMMAND_MAX_); - if (!ent->removed && !ent->exitward) { - cell_stats->added_cells_appward[ent->command] += 1; - } else if (!ent->removed && ent->exitward) { - cell_stats->added_cells_exitward[ent->command] += 1; - } else if (!ent->exitward) { - cell_stats->removed_cells_appward[ent->command] += 1; - cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10; - } else { - cell_stats->removed_cells_exitward[ent->command] += 1; - cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10; - } - tor_free(ent); - } SMARTLIST_FOREACH_END(ent); - smartlist_free(circ->testing_cell_stats); - circ->testing_cell_stats = NULL; -} - -/** Helper: append a cell statistics string to event_parts, - * prefixed with key=. Statistics consist of comma-separated - * key:value pairs with lower-case command strings as keys and cell - * numbers or total waiting times as values. A key:value pair is included - * if the entry in include_if_non_zero is not zero, but with - * the (possibly zero) entry from number_to_include. Both - * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no - * entry in include_if_non_zero is positive, no string will - * be added to event_parts. */ -void -append_cell_stats_by_command(smartlist_t *event_parts, const char *key, - const uint64_t *include_if_non_zero, - const uint64_t *number_to_include) -{ - smartlist_t *key_value_strings = smartlist_new(); - int i; - for (i = 0; i <= CELL_COMMAND_MAX_; i++) { - if (include_if_non_zero[i] > 0) { - smartlist_add_asprintf(key_value_strings, "%s:"U64_FORMAT, - cell_command_to_string(i), - U64_PRINTF_ARG(number_to_include[i])); - } - } - if (smartlist_len(key_value_strings) > 0) { - char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL); - smartlist_add_asprintf(event_parts, "%s=%s", key, joined); - SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp)); - tor_free(joined); - } - smartlist_free(key_value_strings); -} - -/** Helper: format cell_stats for circ for inclusion in a - * CELL_STATS event and write result string to event_string. */ -void -format_cell_stats(char **event_string, circuit_t *circ, - cell_stats_t *cell_stats) -{ - smartlist_t *event_parts = smartlist_new(); - if (CIRCUIT_IS_ORIGIN(circ)) { - origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); - smartlist_add_asprintf(event_parts, "ID=%lu", - (unsigned long)ocirc->global_identifier); - } else if (TO_OR_CIRCUIT(circ)->p_chan) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - smartlist_add_asprintf(event_parts, "InboundQueue=%lu", - (unsigned long)or_circ->p_circ_id); - smartlist_add_asprintf(event_parts, "InboundConn="U64_FORMAT, - U64_PRINTF_ARG(or_circ->p_chan->global_identifier)); - append_cell_stats_by_command(event_parts, "InboundAdded", - cell_stats->added_cells_appward, - cell_stats->added_cells_appward); - append_cell_stats_by_command(event_parts, "InboundRemoved", - cell_stats->removed_cells_appward, - cell_stats->removed_cells_appward); - append_cell_stats_by_command(event_parts, "InboundTime", - cell_stats->removed_cells_appward, - cell_stats->total_time_appward); - } - if (circ->n_chan) { - smartlist_add_asprintf(event_parts, "OutboundQueue=%lu", - (unsigned long)circ->n_circ_id); - smartlist_add_asprintf(event_parts, "OutboundConn="U64_FORMAT, - U64_PRINTF_ARG(circ->n_chan->global_identifier)); - append_cell_stats_by_command(event_parts, "OutboundAdded", - cell_stats->added_cells_exitward, - cell_stats->added_cells_exitward); - append_cell_stats_by_command(event_parts, "OutboundRemoved", - cell_stats->removed_cells_exitward, - cell_stats->removed_cells_exitward); - append_cell_stats_by_command(event_parts, "OutboundTime", - cell_stats->removed_cells_exitward, - cell_stats->total_time_exitward); - } - *event_string = smartlist_join_strings(event_parts, " ", 0, NULL); - SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp)); - smartlist_free(event_parts); -} - -/** A second or more has elapsed: tell any interested control connection - * how many cells have been processed for a given circuit. */ -int -control_event_circuit_cell_stats(void) -{ - circuit_t *circ; - cell_stats_t *cell_stats; - char *event_string; - if (!get_options()->TestingEnableCellStatsEvent || - !EVENT_IS_INTERESTING(EVENT_CELL_STATS)) - return 0; - cell_stats = tor_malloc(sizeof(cell_stats_t));; - TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) { - if (!circ->testing_cell_stats) - continue; - sum_up_cell_stats_by_command(circ, cell_stats); - format_cell_stats(&event_string, circ, cell_stats); - send_control_event(EVENT_CELL_STATS, ALL_FORMATS, - "650 CELL_STATS %s\r\n", event_string); - tor_free(event_string); - } - tor_free(cell_stats); - return 0; -} - -/** Tokens in bucket have been refilled: the read bucket was empty - * for read_empty_time millis, the write bucket was empty for - * write_empty_time millis, and buckets were last refilled - * milliseconds_elapsed millis ago. Only emit TB_EMPTY event if - * either read or write bucket have been empty before. */ -int -control_event_tb_empty(const char *bucket, uint32_t read_empty_time, - uint32_t write_empty_time, - int milliseconds_elapsed) -{ - if (get_options()->TestingEnableTbEmptyEvent && - EVENT_IS_INTERESTING(EVENT_TB_EMPTY) && - (read_empty_time > 0 || write_empty_time > 0)) { - send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, - "650 TB_EMPTY %s READ=%d WRITTEN=%d " - "LAST=%d\r\n", - bucket, read_empty_time, write_empty_time, - milliseconds_elapsed); - } - return 0; -} - -/** A second or more has elapsed: tell any interested control - * connections how much bandwidth we used. */ -int -control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) -{ - if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) { - send_control_event(EVENT_BANDWIDTH_USED, ALL_FORMATS, - "650 BW %lu %lu\r\n", - (unsigned long)n_read, - (unsigned long)n_written); - } - - return 0; -} - -/** Called when we are sending a log message to the controllers: suspend - * sending further log messages to the controllers until we're done. Used by - * CONN_LOG_PROTECT. */ -void -disable_control_logging(void) -{ - ++disable_log_messages; -} - -/** We're done sending a log message to the controllers: re-enable controller - * logging. Used by CONN_LOG_PROTECT. */ -void -enable_control_logging(void) -{ - if (--disable_log_messages < 0) - tor_assert(0); -} - -/** We got a log message: tell any interested control connections. */ -void -control_event_logmsg(int severity, uint32_t domain, const char *msg) -{ - int event; - - /* Don't even think of trying to add stuff to a buffer from a cpuworker - * thread. */ - if (! in_main_thread()) - return; - - if (disable_log_messages) - return; - - if (domain == LD_BUG && EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL) && - severity <= LOG_NOTICE) { - char *esc = esc_for_log(msg); - ++disable_log_messages; - control_event_general_status(severity, "BUG REASON=%s", esc); - --disable_log_messages; - tor_free(esc); - } - - event = log_severity_to_event(severity); - if (event >= 0 && EVENT_IS_INTERESTING(event)) { - char *b = NULL; - const char *s; - if (strchr(msg, '\n')) { - char *cp; - b = tor_strdup(msg); - for (cp = b; *cp; ++cp) - if (*cp == '\r' || *cp == '\n') - *cp = ' '; - } - switch (severity) { - case LOG_DEBUG: s = "DEBUG"; break; - case LOG_INFO: s = "INFO"; break; - case LOG_NOTICE: s = "NOTICE"; break; - case LOG_WARN: s = "WARN"; break; - case LOG_ERR: s = "ERR"; break; - default: s = "UnknownLogSeverity"; break; - } - ++disable_log_messages; - send_control_event(event, ALL_FORMATS, "650 %s %s\r\n", s, b?b:msg); - --disable_log_messages; - tor_free(b); - } -} - -/** Called whenever we receive new router descriptors: tell any - * interested control connections. routers is a list of - * routerinfo_t's. - */ -int -control_event_descriptors_changed(smartlist_t *routers) -{ - char *msg; - - if (!EVENT_IS_INTERESTING(EVENT_NEW_DESC)) - return 0; - - { - smartlist_t *names = smartlist_new(); - char *ids; - SMARTLIST_FOREACH(routers, routerinfo_t *, ri, { - char *b = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1); - router_get_verbose_nickname(b, ri); - smartlist_add(names, b); - }); - ids = smartlist_join_strings(names, " ", 0, NULL); - tor_asprintf(&msg, "650 NEWDESC %s\r\n", ids); - send_control_event_string(EVENT_NEW_DESC, ALL_FORMATS, msg); - tor_free(ids); - tor_free(msg); - SMARTLIST_FOREACH(names, char *, cp, tor_free(cp)); - smartlist_free(names); - } - return 0; -} - -/** Called when an address mapping on from from changes to to. - * expires values less than 3 are special; see connection_edge.c. If - * error is non-NULL, it is an error code describing the failure - * mode of the mapping. - */ -int -control_event_address_mapped(const char *from, const char *to, time_t expires, - const char *error, const int cached) -{ - if (!EVENT_IS_INTERESTING(EVENT_ADDRMAP)) - return 0; - - if (expires < 3 || expires == TIME_MAX) - send_control_event(EVENT_ADDRMAP, ALL_FORMATS, - "650 ADDRMAP %s %s NEVER %s%s" - "CACHED=\"%s\"\r\n", - from, to, error?error:"", error?" ":"", - cached?"YES":"NO"); - else { - char buf[ISO_TIME_LEN+1]; - char buf2[ISO_TIME_LEN+1]; - format_local_iso_time(buf,expires); - format_iso_time(buf2,expires); - send_control_event(EVENT_ADDRMAP, ALL_FORMATS, - "650 ADDRMAP %s %s \"%s\"" - " %s%sEXPIRES=\"%s\" CACHED=\"%s\"\r\n", - from, to, buf, - error?error:"", error?" ":"", - buf2, cached?"YES":"NO"); - } - - return 0; -} - -/** The authoritative dirserver has received a new descriptor that - * has passed basic syntax checks and is properly self-signed. - * - * Notify any interested party of the new descriptor and what has - * been done with it, and also optionally give an explanation/reason. */ -int -control_event_or_authdir_new_descriptor(const char *action, - const char *desc, size_t desclen, - const char *msg) -{ - char firstline[1024]; - char *buf; - size_t totallen; - char *esc = NULL; - size_t esclen; - - if (!EVENT_IS_INTERESTING(EVENT_AUTHDIR_NEWDESCS)) - return 0; - - tor_snprintf(firstline, sizeof(firstline), - "650+AUTHDIR_NEWDESC=\r\n%s\r\n%s\r\n", - action, - msg ? msg : ""); - - /* Escape the server descriptor properly */ - esclen = write_escaped_data(desc, desclen, &esc); - - totallen = strlen(firstline) + esclen + 1; - buf = tor_malloc(totallen); - strlcpy(buf, firstline, totallen); - strlcpy(buf+strlen(firstline), esc, totallen); - send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS, - buf); - send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS, - "650 OK\r\n"); - tor_free(esc); - tor_free(buf); - - return 0; -} - -/** Helper function for NS-style events. Constructs and sends an event - * of type event with string event_string out of the set of - * networkstatuses statuses. Currently it is used for NS events - * and NEWCONSENSUS events. */ -static int -control_event_networkstatus_changed_helper(smartlist_t *statuses, - uint16_t event, - const char *event_string) -{ - smartlist_t *strs; - char *s, *esc = NULL; - if (!EVENT_IS_INTERESTING(event) || !smartlist_len(statuses)) - return 0; - - strs = smartlist_new(); - smartlist_add(strs, tor_strdup("650+")); - smartlist_add(strs, tor_strdup(event_string)); - smartlist_add(strs, tor_strdup("\r\n")); - SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs, - { - s = networkstatus_getinfo_helper_single(rs); - if (!s) continue; - smartlist_add(strs, s); - }); - - s = smartlist_join_strings(strs, "", 0, NULL); - write_escaped_data(s, strlen(s), &esc); - SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp)); - smartlist_free(strs); - tor_free(s); - send_control_event_string(event, ALL_FORMATS, esc); - send_control_event_string(event, ALL_FORMATS, - "650 OK\r\n"); - - tor_free(esc); - return 0; -} - -/** Called when the routerstatus_ts statuses have changed: sends - * an NS event to any controller that cares. */ -int -control_event_networkstatus_changed(smartlist_t *statuses) -{ - return control_event_networkstatus_changed_helper(statuses, EVENT_NS, "NS"); -} - -/** Called when we get a new consensus networkstatus. Sends a NEWCONSENSUS - * event consisting of an NS-style line for each relay in the consensus. */ -int -control_event_newconsensus(const networkstatus_t *consensus) -{ - if (!control_event_is_interesting(EVENT_NEWCONSENSUS)) - return 0; - return control_event_networkstatus_changed_helper( - consensus->routerstatus_list, EVENT_NEWCONSENSUS, "NEWCONSENSUS"); -} - -/** Called when we compute a new circuitbuildtimeout */ -int -control_event_buildtimeout_set(buildtimeout_set_event_t type, - const char *args) -{ - const char *type_string = NULL; - - if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET)) - return 0; - - switch (type) { - case BUILDTIMEOUT_SET_EVENT_COMPUTED: - type_string = "COMPUTED"; - break; - case BUILDTIMEOUT_SET_EVENT_RESET: - type_string = "RESET"; - break; - case BUILDTIMEOUT_SET_EVENT_SUSPENDED: - type_string = "SUSPENDED"; - break; - case BUILDTIMEOUT_SET_EVENT_DISCARD: - type_string = "DISCARD"; - break; - case BUILDTIMEOUT_SET_EVENT_RESUME: - type_string = "RESUME"; - break; - default: - type_string = "UNKNOWN"; - break; - } - - send_control_event(EVENT_BUILDTIMEOUT_SET, ALL_FORMATS, - "650 BUILDTIMEOUT_SET %s %s\r\n", - type_string, args); - - return 0; -} - -/** Called when a signal has been processed from signal_callback */ -int -control_event_signal(uintptr_t signal) -{ - const char *signal_string = NULL; - - if (!control_event_is_interesting(EVENT_SIGNAL)) - return 0; - - switch (signal) { - case SIGHUP: - signal_string = "RELOAD"; - break; - case SIGUSR1: - signal_string = "DUMP"; - break; - case SIGUSR2: - signal_string = "DEBUG"; - break; - case SIGNEWNYM: - signal_string = "NEWNYM"; - break; - case SIGCLEARDNSCACHE: - signal_string = "CLEARDNSCACHE"; - break; - default: - log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal", - (unsigned long)signal); - return -1; - } - - send_control_event(EVENT_SIGNAL, ALL_FORMATS, "650 SIGNAL %s\r\n", - signal_string); - return 0; -} - -/** Called when a single local_routerstatus_t has changed: Sends an NS event - * to any controller that cares. */ -int -control_event_networkstatus_changed_single(const routerstatus_t *rs) -{ - smartlist_t *statuses; - int r; - - if (!EVENT_IS_INTERESTING(EVENT_NS)) - return 0; - - statuses = smartlist_new(); - smartlist_add(statuses, (void*)rs); - r = control_event_networkstatus_changed(statuses); - smartlist_free(statuses); - return r; -} - -/** Our own router descriptor has changed; tell any controllers that care. - */ -int -control_event_my_descriptor_changed(void) -{ - send_control_event(EVENT_DESCCHANGED, ALL_FORMATS, "650 DESCCHANGED\r\n"); - return 0; -} - -/** Helper: sends a status event where type is one of - * EVENT_STATUS_{GENERAL,CLIENT,SERVER}, where severity is one of - * LOG_{NOTICE,WARN,ERR}, and where format is a printf-style format - * string corresponding to args. */ -static int -control_event_status(int type, int severity, const char *format, va_list args) -{ - char *user_buf = NULL; - char format_buf[160]; - const char *status, *sev; - - switch (type) { - case EVENT_STATUS_GENERAL: - status = "STATUS_GENERAL"; - break; - case EVENT_STATUS_CLIENT: - status = "STATUS_CLIENT"; - break; - case EVENT_STATUS_SERVER: - status = "STATUS_SERVER"; - break; - default: - log_warn(LD_BUG, "Unrecognized status type %d", type); - return -1; - } - switch (severity) { - case LOG_NOTICE: - sev = "NOTICE"; - break; - case LOG_WARN: - sev = "WARN"; - break; - case LOG_ERR: - sev = "ERR"; - break; - default: - log_warn(LD_BUG, "Unrecognized status severity %d", severity); - return -1; - } - if (tor_snprintf(format_buf, sizeof(format_buf), "650 %s %s", - status, sev)<0) { - log_warn(LD_BUG, "Format string too long."); - return -1; - } - tor_vasprintf(&user_buf, format, args); - - send_control_event(type, ALL_FORMATS, "%s %s\r\n", format_buf, user_buf); - tor_free(user_buf); - return 0; -} - -/** Format and send an EVENT_STATUS_GENERAL event whose main text is obtained - * by formatting the arguments using the printf-style format. */ -int -control_event_general_status(int severity, const char *format, ...) -{ - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL)) - return 0; - - va_start(ap, format); - r = control_event_status(EVENT_STATUS_GENERAL, severity, format, ap); - va_end(ap); - return r; -} - -/** Format and send an EVENT_STATUS_CLIENT event whose main text is obtained - * by formatting the arguments using the printf-style format. */ -int -control_event_client_status(int severity, const char *format, ...) -{ - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_CLIENT)) - return 0; - - va_start(ap, format); - r = control_event_status(EVENT_STATUS_CLIENT, severity, format, ap); - va_end(ap); - return r; -} - -/** Format and send an EVENT_STATUS_SERVER event whose main text is obtained - * by formatting the arguments using the printf-style format. */ -int -control_event_server_status(int severity, const char *format, ...) -{ - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_SERVER)) - return 0; - - va_start(ap, format); - r = control_event_status(EVENT_STATUS_SERVER, severity, format, ap); - va_end(ap); - return r; -} - -/** Called when the status of an entry guard with the given nickname - * and identity digest has changed to status: tells any - * controllers that care. */ -int -control_event_guard(const char *nickname, const char *digest, - const char *status) -{ - char hbuf[HEX_DIGEST_LEN+1]; - base16_encode(hbuf, sizeof(hbuf), digest, DIGEST_LEN); - if (!EVENT_IS_INTERESTING(EVENT_GUARD)) - return 0; - - { - char buf[MAX_VERBOSE_NICKNAME_LEN+1]; - const node_t *node = node_get_by_id(digest); - if (node) { - node_get_verbose_nickname(node, buf); - } else { - tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname); - } - send_control_event(EVENT_GUARD, ALL_FORMATS, - "650 GUARD ENTRY %s %s\r\n", buf, status); - } - return 0; -} - -/** Called when a configuration option changes. This is generally triggered - * by SETCONF requests and RELOAD/SIGHUP signals. The elements is - * a smartlist_t containing (key, value, ...) pairs in sequence. - * value can be NULL. */ -int -control_event_conf_changed(const smartlist_t *elements) -{ - int i; - char *result; - smartlist_t *lines; - if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) || - smartlist_len(elements) == 0) { - return 0; - } - lines = smartlist_new(); - for (i = 0; i < smartlist_len(elements); i += 2) { - char *k = smartlist_get(elements, i); - char *v = smartlist_get(elements, i+1); - if (v == NULL) { - smartlist_add_asprintf(lines, "650-%s", k); - } else { - smartlist_add_asprintf(lines, "650-%s=%s", k, v); - } - } - result = smartlist_join_strings(lines, "\r\n", 0, NULL); - send_control_event(EVENT_CONF_CHANGED, 0, - "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result); - tor_free(result); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); - return 0; -} - -/** Helper: Return a newly allocated string containing a path to the - * file where we store our authentication cookie. */ -static char * -get_cookie_file(void) -{ - const or_options_t *options = get_options(); - if (options->CookieAuthFile && strlen(options->CookieAuthFile)) { - return tor_strdup(options->CookieAuthFile); - } else { - return get_datadir_fname("control_auth_cookie"); - } -} - -/* Initialize the cookie-based authentication system of the - * ControlPort. If enabled is 0, then disable the cookie - * authentication system. */ -int -init_control_cookie_authentication(int enabled) -{ - char *fname = NULL; - int retval; - - if (!enabled) { - authentication_cookie_is_set = 0; - return 0; - } - - fname = get_cookie_file(); - retval = init_cookie_authentication(fname, "", /* no header */ - AUTHENTICATION_COOKIE_LEN, - &authentication_cookie, - &authentication_cookie_is_set); - tor_free(fname); - return retval; -} - -/** A copy of the process specifier of Tor's owning controller, or - * NULL if this Tor instance is not currently owned by a process. */ -static char *owning_controller_process_spec = NULL; - -/** A process-termination monitor for Tor's owning controller, or NULL - * if this Tor instance is not currently owned by a process. */ -static tor_process_monitor_t *owning_controller_process_monitor = NULL; - -/** Process-termination monitor callback for Tor's owning controller - * process. */ -static void -owning_controller_procmon_cb(void *unused) -{ - (void)unused; - - lost_owning_controller("process", "vanished"); -} - -/** Set process_spec as Tor's owning controller process. - * Exit on failure. */ -void -monitor_owning_controller_process(const char *process_spec) -{ - const char *msg; - - tor_assert((owning_controller_process_spec == NULL) == - (owning_controller_process_monitor == NULL)); - - if (owning_controller_process_spec != NULL) { - if ((process_spec != NULL) && !strcmp(process_spec, - owning_controller_process_spec)) { - /* Same process -- return now, instead of disposing of and - * recreating the process-termination monitor. */ - return; - } - - /* We are currently owned by a process, and we should no longer be - * owned by it. Free the process-termination monitor. */ - tor_process_monitor_free(owning_controller_process_monitor); - owning_controller_process_monitor = NULL; - - tor_free(owning_controller_process_spec); - owning_controller_process_spec = NULL; - } - - tor_assert((owning_controller_process_spec == NULL) && - (owning_controller_process_monitor == NULL)); - - if (process_spec == NULL) - return; - - owning_controller_process_spec = tor_strdup(process_spec); - owning_controller_process_monitor = - tor_process_monitor_new(tor_libevent_get_base(), - owning_controller_process_spec, - LD_CONTROL, - owning_controller_procmon_cb, NULL, - &msg); - - if (owning_controller_process_monitor == NULL) { - log_err(LD_BUG, "Couldn't create process-termination monitor for " - "owning controller: %s. Exiting.", - msg); - owning_controller_process_spec = NULL; - tor_cleanup(); - exit(0); - } -} - -/** Convert the name of a bootstrapping phase s into strings - * tag and summary suitable for display by the controller. */ -static int -bootstrap_status_to_string(bootstrap_status_t s, const char **tag, - const char **summary) -{ - switch (s) { - case BOOTSTRAP_STATUS_UNDEF: - *tag = "undef"; - *summary = "Undefined"; - break; - case BOOTSTRAP_STATUS_STARTING: - *tag = "starting"; - *summary = "Starting"; - break; - case BOOTSTRAP_STATUS_CONN_DIR: - *tag = "conn_dir"; - *summary = "Connecting to directory server"; - break; - case BOOTSTRAP_STATUS_HANDSHAKE: - *tag = "status_handshake"; - *summary = "Finishing handshake"; - break; - case BOOTSTRAP_STATUS_HANDSHAKE_DIR: - *tag = "handshake_dir"; - *summary = "Finishing handshake with directory server"; - break; - case BOOTSTRAP_STATUS_ONEHOP_CREATE: - *tag = "onehop_create"; - *summary = "Establishing an encrypted directory connection"; - break; - case BOOTSTRAP_STATUS_REQUESTING_STATUS: - *tag = "requesting_status"; - *summary = "Asking for networkstatus consensus"; - break; - case BOOTSTRAP_STATUS_LOADING_STATUS: - *tag = "loading_status"; - *summary = "Loading networkstatus consensus"; - break; - case BOOTSTRAP_STATUS_LOADING_KEYS: - *tag = "loading_keys"; - *summary = "Loading authority key certs"; - break; - case BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS: - *tag = "requesting_descriptors"; - *summary = "Asking for relay descriptors"; - break; - case BOOTSTRAP_STATUS_LOADING_DESCRIPTORS: - *tag = "loading_descriptors"; - *summary = "Loading relay descriptors"; - break; - case BOOTSTRAP_STATUS_CONN_OR: - *tag = "conn_or"; - *summary = "Connecting to the Tor network"; - break; - case BOOTSTRAP_STATUS_HANDSHAKE_OR: - *tag = "handshake_or"; - *summary = "Finishing handshake with first hop"; - break; - case BOOTSTRAP_STATUS_CIRCUIT_CREATE: - *tag = "circuit_create"; - *summary = "Establishing a Tor circuit"; - break; - case BOOTSTRAP_STATUS_DONE: - *tag = "done"; - *summary = "Done"; - break; - default: -// log_warn(LD_BUG, "Unrecognized bootstrap status code %d", s); - *tag = *summary = "unknown"; - return -1; - } - return 0; -} - -/** What percentage through the bootstrap process are we? We remember - * this so we can avoid sending redundant bootstrap status events, and - * so we can guess context for the bootstrap messages which are - * ambiguous. It starts at 'undef', but gets set to 'starting' while - * Tor initializes. */ -static int bootstrap_percent = BOOTSTRAP_STATUS_UNDEF; - -/** How many problems have we had getting to the next bootstrapping phase? - * These include failure to establish a connection to a Tor relay, - * failures to finish the TLS handshake, failures to validate the - * consensus document, etc. */ -static int bootstrap_problems = 0; - -/* We only tell the controller once we've hit a threshold of problems - * for the current phase. */ -#define BOOTSTRAP_PROBLEM_THRESHOLD 10 - -/** Called when Tor has made progress at bootstrapping its directory - * information and initial circuits. - * - * status is the new status, that is, what task we will be doing - * next. progress is zero if we just started this task, else it - * represents progress on the task. */ -void -control_event_bootstrap(bootstrap_status_t status, int progress) -{ - const char *tag, *summary; - char buf[BOOTSTRAP_MSG_LEN]; - - if (bootstrap_percent == BOOTSTRAP_STATUS_DONE) - return; /* already bootstrapped; nothing to be done here. */ - - /* special case for handshaking status, since our TLS handshaking code - * can't distinguish what the connection is going to be for. */ - if (status == BOOTSTRAP_STATUS_HANDSHAKE) { - if (bootstrap_percent < BOOTSTRAP_STATUS_CONN_OR) { - status = BOOTSTRAP_STATUS_HANDSHAKE_DIR; - } else { - status = BOOTSTRAP_STATUS_HANDSHAKE_OR; - } - } - - if (status > bootstrap_percent || - (progress && progress > bootstrap_percent)) { - bootstrap_status_to_string(status, &tag, &summary); - tor_log(status ? LOG_NOTICE : LOG_INFO, LD_CONTROL, - "Bootstrapped %d%%: %s.", progress ? progress : status, summary); - tor_snprintf(buf, sizeof(buf), - "BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"", - progress ? progress : status, tag, summary); - tor_snprintf(last_sent_bootstrap_message, - sizeof(last_sent_bootstrap_message), - "NOTICE %s", buf); - control_event_client_status(LOG_NOTICE, "%s", buf); - if (status > bootstrap_percent) { - bootstrap_percent = status; /* new milestone reached */ - } - if (progress > bootstrap_percent) { - /* incremental progress within a milestone */ - bootstrap_percent = progress; - bootstrap_problems = 0; /* Progress! Reset our problem counter. */ - } - } -} - -/** Called when Tor has failed to make bootstrapping progress in a way - * that indicates a problem. warn gives a hint as to why, and - * reason provides an "or_conn_end_reason" tag. - */ -MOCK_IMPL(void, -control_event_bootstrap_problem, (const char *warn, int reason)) -{ - int status = bootstrap_percent; - const char *tag, *summary; - char buf[BOOTSTRAP_MSG_LEN]; - const char *recommendation = "ignore"; - int severity; - - /* bootstrap_percent must not be in "undefined" state here. */ - tor_assert(status >= 0); - - if (bootstrap_percent == 100) - return; /* already bootstrapped; nothing to be done here. */ - - bootstrap_problems++; - - if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD) - recommendation = "warn"; - - if (reason == END_OR_CONN_REASON_NO_ROUTE) - recommendation = "warn"; - - if (get_options()->UseBridges && - !any_bridge_descriptors_known() && - !any_pending_bridge_descriptor_fetches()) - recommendation = "warn"; - - if (we_are_hibernating()) - recommendation = "ignore"; - - while (status>=0 && bootstrap_status_to_string(status, &tag, &summary) < 0) - status--; /* find a recognized status string based on current progress */ - status = bootstrap_percent; /* set status back to the actual number */ - - severity = !strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO; - - log_fn(severity, - LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; " - "count %d; recommendation %s)", - status, summary, warn, - orconn_end_reason_to_control_string(reason), - bootstrap_problems, recommendation); - - connection_or_report_broken_states(severity, LD_HANDSHAKE); - - tor_snprintf(buf, sizeof(buf), - "BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s " - "COUNT=%d RECOMMENDATION=%s", - bootstrap_percent, tag, summary, warn, - orconn_end_reason_to_control_string(reason), bootstrap_problems, - recommendation); - tor_snprintf(last_sent_bootstrap_message, - sizeof(last_sent_bootstrap_message), - "WARN %s", buf); - control_event_client_status(LOG_WARN, "%s", buf); -} - -/** We just generated a new summary of which countries we've seen clients - * from recently. Send a copy to the controller in case it wants to - * display it for the user. */ -void -control_event_clients_seen(const char *controller_str) -{ - send_control_event(EVENT_CLIENTS_SEEN, 0, - "650 CLIENTS_SEEN %s\r\n", controller_str); -} - -/** A new pluggable transport called transport_name was - * launched on addr:port. mode is either - * "server" or "client" depending on the mode of the pluggable - * transport. - * "650" SP "TRANSPORT_LAUNCHED" SP Mode SP Name SP Address SP Port - */ -void -control_event_transport_launched(const char *mode, const char *transport_name, - tor_addr_t *addr, uint16_t port) -{ - send_control_event(EVENT_TRANSPORT_LAUNCHED, ALL_FORMATS, - "650 TRANSPORT_LAUNCHED %s %s %s %u\r\n", - mode, transport_name, fmt_addr(addr), port); -} - -/** Convert rendezvous auth type to string for HS_DESC control events - */ -const char * -rend_auth_type_to_string(rend_auth_type_t auth_type) -{ - const char *str; - - switch (auth_type) { - case REND_NO_AUTH: - str = "NO_AUTH"; - break; - case REND_BASIC_AUTH: - str = "BASIC_AUTH"; - break; - case REND_STEALTH_AUTH: - str = "STEALTH_AUTH"; - break; - default: - str = "UNKNOWN"; - } - - return str; -} - -/** Return a longname the node whose identity is id_digest. If - * node_get_by_id() returns NULL, base 16 encoding of id_digest is - * returned instead. - * - * This function is not thread-safe. Each call to this function invalidates - * previous values returned by this function. - */ -MOCK_IMPL(const char *, -node_describe_longname_by_id,(const char *id_digest)) -{ - static char longname[MAX_VERBOSE_NICKNAME_LEN+1]; - node_get_verbose_nickname_by_id(id_digest, longname); - return longname; -} - -/** send HS_DESC requested event. - * - * rend_query is used to fetch requested onion address and auth type. - * hs_dir is the description of contacting hs directory. - * desc_id_base32 is the ID of requested hs descriptor. - */ -void -control_event_hs_descriptor_requested(const rend_data_t *rend_query, - const char *id_digest, - const char *desc_id_base32) -{ - if (!id_digest || !rend_query || !desc_id_base32) { - log_warn(LD_BUG, "Called with rend_query==%p, " - "id_digest==%p, desc_id_base32==%p", - rend_query, id_digest, desc_id_base32); - return; - } - - send_control_event(EVENT_HS_DESC, ALL_FORMATS, - "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_query->onion_address, - rend_auth_type_to_string(rend_query->auth_type), - node_describe_longname_by_id(id_digest), - desc_id_base32); -} - -/** send HS_DESC event after got response from hs directory. - * - * NOTE: this is an internal function used by following functions: - * control_event_hs_descriptor_received - * control_event_hs_descriptor_failed - * - * So do not call this function directly. - */ -void -control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, - const char *id_digest) -{ - if (!action || !rend_query || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, rend_query==%p, " - "id_digest==%p", action, rend_query, id_digest); - return; - } - - send_control_event(EVENT_HS_DESC, ALL_FORMATS, - "650 HS_DESC %s %s %s %s\r\n", - action, - rend_query->onion_address, - rend_auth_type_to_string(rend_query->auth_type), - node_describe_longname_by_id(id_digest)); -} - -/** send HS_DESC RECEIVED event - * - * called when a we successfully received a hidden service descriptor. - */ -void -control_event_hs_descriptor_received(const rend_data_t *rend_query, - const char *id_digest) -{ - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); - return; - } - control_event_hs_descriptor_receive_end("RECEIVED", rend_query, id_digest); -} - -/** send HS_DESC FAILED event - * - * called when request for hidden service descriptor returned failure. - */ -void -control_event_hs_descriptor_failed(const rend_data_t *rend_query, - const char *id_digest) -{ - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); - return; - } - control_event_hs_descriptor_receive_end("FAILED", rend_query, id_digest); -} - -/** Free any leftover allocated memory of the control.c subsystem. */ -void -control_free_all(void) -{ - if (authentication_cookie) /* Free the auth cookie */ - tor_free(authentication_cookie); -} - -#ifdef TOR_UNIT_TESTS -/* For testing: change the value of global_event_mask */ -void -control_testing_set_global_event_mask(uint64_t mask) -{ - global_event_mask = mask; -} -#endif - diff --git a/src/tor/control.h b/src/tor/control.h deleted file mode 100644 index 0466de1..0000000 --- a/src/tor/control.h +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file control.h - * \brief Header file for control.c. - **/ - -#ifndef TOR_CONTROL_H -#define TOR_CONTROL_H - -void control_update_global_event_mask(void); -void control_adjust_event_log_severity(void); - -void control_ports_write_to_file(void); - -/** Log information about the connection conn, protecting it as with - * CONN_LOG_PROTECT. Example: - * - * LOG_FN_CONN(conn, (LOG_DEBUG, "Socket %d wants to write", conn->s)); - **/ -#define LOG_FN_CONN(conn, args) \ - CONN_LOG_PROTECT(conn, log_fn args) - -int connection_control_finished_flushing(control_connection_t *conn); -int connection_control_reached_eof(control_connection_t *conn); -void connection_control_closed(control_connection_t *conn); - -int connection_control_process_inbuf(control_connection_t *conn); - -#define EVENT_AUTHDIR_NEWDESCS 0x000D -#define EVENT_NS 0x000F -int control_event_is_interesting(int event); - -int control_event_circuit_status(origin_circuit_t *circ, - circuit_status_event_t e, int reason); -int control_event_circuit_purpose_changed(origin_circuit_t *circ, - int old_purpose); -int control_event_circuit_cannibalized(origin_circuit_t *circ, - int old_purpose, - const struct timeval *old_tv_created); -int control_event_stream_status(entry_connection_t *conn, - stream_status_event_t e, - int reason); -int control_event_or_conn_status(or_connection_t *conn, - or_conn_status_event_t e, int reason); -int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written); -int control_event_stream_bandwidth(edge_connection_t *edge_conn); -int control_event_stream_bandwidth_used(void); -int control_event_circ_bandwidth_used(void); -int control_event_conn_bandwidth(connection_t *conn); -int control_event_conn_bandwidth_used(void); -int control_event_circuit_cell_stats(void); -int control_event_tb_empty(const char *bucket, uint32_t read_empty_time, - uint32_t write_empty_time, - int milliseconds_elapsed); -void control_event_logmsg(int severity, uint32_t domain, const char *msg); -int control_event_descriptors_changed(smartlist_t *routers); -int control_event_address_mapped(const char *from, const char *to, - time_t expires, const char *error, - const int cached); -int control_event_or_authdir_new_descriptor(const char *action, - const char *desc, - size_t desclen, - const char *msg); -int control_event_my_descriptor_changed(void); -int control_event_networkstatus_changed(smartlist_t *statuses); - -int control_event_newconsensus(const networkstatus_t *consensus); -int control_event_networkstatus_changed_single(const routerstatus_t *rs); -int control_event_general_status(int severity, const char *format, ...) - CHECK_PRINTF(2,3); -int control_event_client_status(int severity, const char *format, ...) - CHECK_PRINTF(2,3); -int control_event_server_status(int severity, const char *format, ...) - CHECK_PRINTF(2,3); -int control_event_guard(const char *nickname, const char *digest, - const char *status); -int control_event_conf_changed(const smartlist_t *elements); -int control_event_buildtimeout_set(buildtimeout_set_event_t type, - const char *args); -int control_event_signal(uintptr_t signal); - -int init_control_cookie_authentication(int enabled); -smartlist_t *decode_hashed_passwords(config_line_t *passwords); -void disable_control_logging(void); -void enable_control_logging(void); - -void monitor_owning_controller_process(const char *process_spec); - -void control_event_bootstrap(bootstrap_status_t status, int progress); -MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn, - int reason)); - -void control_event_clients_seen(const char *controller_str); -void control_event_transport_launched(const char *mode, - const char *transport_name, - tor_addr_t *addr, uint16_t port); -const char *rend_auth_type_to_string(rend_auth_type_t auth_type); -MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); -void control_event_hs_descriptor_requested(const rend_data_t *rend_query, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, - const char *hs_dir); -void control_event_hs_descriptor_received(const rend_data_t *rend_query, - const char *hs_dir); -void control_event_hs_descriptor_failed(const rend_data_t *rend_query, - const char *hs_dir); - -void control_free_all(void); - -#ifdef CONTROL_PRIVATE -/* Recognized asynchronous event types. It's okay to expand this list - * because it is used both as a list of v0 event types, and as indices - * into the bitfield to determine which controllers want which events. - */ -#define EVENT_MIN_ 0x0001 -#define EVENT_CIRCUIT_STATUS 0x0001 -#define EVENT_STREAM_STATUS 0x0002 -#define EVENT_OR_CONN_STATUS 0x0003 -#define EVENT_BANDWIDTH_USED 0x0004 -#define EVENT_CIRCUIT_STATUS_MINOR 0x0005 -#define EVENT_NEW_DESC 0x0006 -#define EVENT_DEBUG_MSG 0x0007 -#define EVENT_INFO_MSG 0x0008 -#define EVENT_NOTICE_MSG 0x0009 -#define EVENT_WARN_MSG 0x000A -#define EVENT_ERR_MSG 0x000B -#define EVENT_ADDRMAP 0x000C -/* Exposed above */ -// #define EVENT_AUTHDIR_NEWDESCS 0x000D -#define EVENT_DESCCHANGED 0x000E -/* Exposed above */ -// #define EVENT_NS 0x000F -#define EVENT_STATUS_CLIENT 0x0010 -#define EVENT_STATUS_SERVER 0x0011 -#define EVENT_STATUS_GENERAL 0x0012 -#define EVENT_GUARD 0x0013 -#define EVENT_STREAM_BANDWIDTH_USED 0x0014 -#define EVENT_CLIENTS_SEEN 0x0015 -#define EVENT_NEWCONSENSUS 0x0016 -#define EVENT_BUILDTIMEOUT_SET 0x0017 -#define EVENT_SIGNAL 0x0018 -#define EVENT_CONF_CHANGED 0x0019 -#define EVENT_CONN_BW 0x001A -#define EVENT_CELL_STATS 0x001B -#define EVENT_TB_EMPTY 0x001C -#define EVENT_CIRC_BANDWIDTH_USED 0x001D -#define EVENT_TRANSPORT_LAUNCHED 0x0020 -#define EVENT_HS_DESC 0x0021 -#define EVENT_MAX_ 0x0021 -/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a - * different structure. */ - -/* Used only by control.c and test.c */ -STATIC size_t write_escaped_data(const char *data, size_t len, char **out); -STATIC size_t read_escaped_data(const char *data, size_t len, char **out); -/** Flag for event_format_t. Indicates that we should use the one standard - format. (Other formats previous existed, and are now deprecated) - */ -#define ALL_FORMATS 1 -/** Bit field of flags to select how to format a controller event. Recognized - * flag is ALL_FORMATS. */ -typedef int event_format_t; - -#ifdef TOR_UNIT_TESTS -MOCK_DECL(STATIC void, -send_control_event_string,(uint16_t event, event_format_t which, - const char *msg)); - -void control_testing_set_global_event_mask(uint64_t mask); -#endif - -/** Helper structure: temporarily stores cell statistics for a circuit. */ -typedef struct cell_stats_t { - /** Number of cells added in app-ward direction by command. */ - uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1]; - /** Number of cells added in exit-ward direction by command. */ - uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1]; - /** Number of cells removed in app-ward direction by command. */ - uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1]; - /** Number of cells removed in exit-ward direction by command. */ - uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1]; - /** Total waiting time of cells in app-ward direction by command. */ - uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1]; - /** Total waiting time of cells in exit-ward direction by command. */ - uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1]; -} cell_stats_t; -void sum_up_cell_stats_by_command(circuit_t *circ, - cell_stats_t *cell_stats); -void append_cell_stats_by_command(smartlist_t *event_parts, - const char *key, - const uint64_t *include_if_non_zero, - const uint64_t *number_to_include); -void format_cell_stats(char **event_string, circuit_t *circ, - cell_stats_t *cell_stats); -#endif - -#endif - diff --git a/src/tor/cpuworker.c b/src/tor/cpuworker.c deleted file mode 100644 index 9611c3b..0000000 --- a/src/tor/cpuworker.c +++ /dev/null @@ -1,718 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file cpuworker.c - * \brief Implements a farm of 'CPU worker' processes to perform - * CPU-intensive tasks in another thread or process, to not - * interrupt the main thread. - * - * Right now, we only use this for processing onionskins. - **/ -#include "or.h" -#include "buffers.h" -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "config.h" -#include "connection.h" -#include "connection_or.h" -#include "cpuworker.h" -#include "onion_main.h" -#include "onion.h" -#include "rephist.h" -#include "router.h" - -/** The maximum number of cpuworker processes we will keep around. */ -#define MAX_CPUWORKERS 16 -/** The minimum number of cpuworker processes we will keep around. */ -#define MIN_CPUWORKERS 1 - -/** The tag specifies which circuit this onionskin was from. */ -#define TAG_LEN 12 - -/** How many cpuworkers we have running right now. */ -static int num_cpuworkers=0; -/** How many of the running cpuworkers have an assigned task right now. */ -static int num_cpuworkers_busy=0; -/** We need to spawn new cpuworkers whenever we rotate the onion keys - * on platforms where execution contexts==processes. This variable stores - * the last time we got a key rotation event. */ -static time_t last_rotation_time=0; - -static void cpuworker_main(void *data) ATTR_NORETURN; -static int spawn_cpuworker(void); -static void spawn_enough_cpuworkers(void); -static void process_pending_task(connection_t *cpuworker); - -/** Initialize the cpuworker subsystem. - */ -void -cpu_init(void) -{ - cpuworkers_rotate(); -} - -/** Called when we're done sending a request to a cpuworker. */ -int -connection_cpu_finished_flushing(connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->type == CONN_TYPE_CPUWORKER); - return 0; -} - -/** Pack global_id and circ_id; set *tag to the result. (See note on - * cpuworker_main for wire format.) */ -static void -tag_pack(uint8_t *tag, uint64_t chan_id, circid_t circ_id) -{ - /*XXXX RETHINK THIS WHOLE MESS !!!! !NM NM NM NM*/ - /*XXXX DOUBLEPLUSTHIS!!!! AS AS AS AS*/ - set_uint64(tag, chan_id); - set_uint32(tag+8, circ_id); -} - -/** Unpack tag into addr, port, and circ_id. - */ -static void -tag_unpack(const uint8_t *tag, uint64_t *chan_id, circid_t *circ_id) -{ - *chan_id = get_uint64(tag); - *circ_id = get_uint32(tag+8); -} - -/** Magic numbers to make sure our cpuworker_requests don't grow any - * mis-framing bugs. */ -#define CPUWORKER_REQUEST_MAGIC 0xda4afeed -#define CPUWORKER_REPLY_MAGIC 0x5eedf00d - -/** A request sent to a cpuworker. */ -typedef struct cpuworker_request_t { - /** Magic number; must be CPUWORKER_REQUEST_MAGIC. */ - uint32_t magic; - /** Opaque tag to identify the job */ - uint8_t tag[TAG_LEN]; - /** Task code. Must be one of CPUWORKER_TASK_* */ - uint8_t task; - - /** Flag: Are we timing this request? */ - unsigned timed : 1; - /** If we're timing this request, when was it sent to the cpuworker? */ - struct timeval started_at; - - /** A create cell for the cpuworker to process. */ - create_cell_t create_cell; - - /* Turn the above into a tagged union if needed. */ -} cpuworker_request_t; - -/** A reply sent by a cpuworker. */ -typedef struct cpuworker_reply_t { - /** Magic number; must be CPUWORKER_REPLY_MAGIC. */ - uint32_t magic; - /** Opaque tag to identify the job; matches the request's tag.*/ - uint8_t tag[TAG_LEN]; - /** True iff we got a successful request. */ - uint8_t success; - - /** Are we timing this request? */ - unsigned int timed : 1; - /** What handshake type was the request? (Used for timing) */ - uint16_t handshake_type; - /** When did we send the request to the cpuworker? */ - struct timeval started_at; - /** Once the cpuworker received the request, how many microseconds did it - * take? (This shouldn't overflow; 4 billion micoseconds is over an hour, - * and we'll never have an onion handshake that takes so long.) */ - uint32_t n_usec; - - /** Output of processing a create cell - * - * @{ - */ - /** The created cell to send back. */ - created_cell_t created_cell; - /** The keys to use on this circuit. */ - uint8_t keys[CPATH_KEY_MATERIAL_LEN]; - /** Input to use for authenticating introduce1 cells. */ - uint8_t rend_auth_material[DIGEST_LEN]; -} cpuworker_reply_t; - -/** Called when the onion key has changed and we need to spawn new - * cpuworkers. Close all currently idle cpuworkers, and mark the last - * rotation time as now. - */ -void -cpuworkers_rotate(void) -{ - connection_t *cpuworker; - while ((cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, - CPUWORKER_STATE_IDLE))) { - connection_mark_for_close(cpuworker); - --num_cpuworkers; - } - last_rotation_time = time(NULL); - if (server_mode(get_options())) - spawn_enough_cpuworkers(); -} - -/** If the cpuworker closes the connection, - * mark it as closed and spawn a new one as needed. */ -int -connection_cpu_reached_eof(connection_t *conn) -{ - log_warn(LD_GENERAL,"Read eof. CPU worker died unexpectedly."); - if (conn->state != CPUWORKER_STATE_IDLE) { - /* the circ associated with this cpuworker will have to wait until - * it gets culled in run_connection_housekeeping(), since we have - * no way to find out which circ it was. */ - log_warn(LD_GENERAL,"...and it left a circuit queued; abandoning circ."); - num_cpuworkers_busy--; - } - num_cpuworkers--; - spawn_enough_cpuworkers(); /* try to regrow. hope we don't end up - spinning. */ - connection_mark_for_close(conn); - return 0; -} - -/** Indexed by handshake type: how many onionskins have we processed and - * counted of that type? */ -static uint64_t onionskins_n_processed[MAX_ONION_HANDSHAKE_TYPE+1]; -/** Indexed by handshake type, corresponding to the onionskins counted in - * onionskins_n_processed: how many microseconds have we spent in cpuworkers - * processing that kind of onionskin? */ -static uint64_t onionskins_usec_internal[MAX_ONION_HANDSHAKE_TYPE+1]; -/** Indexed by handshake type, corresponding to onionskins counted in - * onionskins_n_processed: how many microseconds have we spent waiting for - * cpuworkers to give us answers for that kind of onionskin? - */ -static uint64_t onionskins_usec_roundtrip[MAX_ONION_HANDSHAKE_TYPE+1]; - -/** If any onionskin takes longer than this, we clip them to this - * time. (microseconds) */ -#define MAX_BELIEVABLE_ONIONSKIN_DELAY (2*1000*1000) - -static tor_weak_rng_t request_sample_rng = TOR_WEAK_RNG_INIT; - -/** Return true iff we'd like to measure a handshake of type - * onionskin_type. Call only from the main thread. */ -static int -should_time_request(uint16_t onionskin_type) -{ - /* If we've never heard of this type, we shouldn't even be here. */ - if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) - return 0; - /* Measure the first N handshakes of each type, to ensure we have a - * sample */ - if (onionskins_n_processed[onionskin_type] < 4096) - return 1; - /** Otherwise, measure with P=1/128. We avoid doing this for every - * handshake, since the measurement itself can take a little time. */ - return tor_weak_random_one_in_n(&request_sample_rng, 128); -} - -/** Return an estimate of how many microseconds we will need for a single - * cpuworker to to process n_requests onionskins of type - * onionskin_type. */ -uint64_t -estimated_usec_for_onionskins(uint32_t n_requests, uint16_t onionskin_type) -{ - if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */ - return 1000 * (uint64_t)n_requests; - if (PREDICT_UNLIKELY(onionskins_n_processed[onionskin_type] < 100)) { - /* Until we have 100 data points, just asssume everything takes 1 msec. */ - return 1000 * (uint64_t)n_requests; - } else { - /* This can't overflow: we'll never have more than 500000 onionskins - * measured in onionskin_usec_internal, and they won't take anything near - * 1 sec each, and we won't have anything like 1 million queued - * onionskins. But that's 5e5 * 1e6 * 1e6, which is still less than - * UINT64_MAX. */ - return (onionskins_usec_internal[onionskin_type] * n_requests) / - onionskins_n_processed[onionskin_type]; - } -} - -/** Compute the absolute and relative overhead of using the cpuworker - * framework for onionskins of type onionskin_type.*/ -static int -get_overhead_for_onionskins(uint32_t *usec_out, double *frac_out, - uint16_t onionskin_type) -{ - uint64_t overhead; - - *usec_out = 0; - *frac_out = 0.0; - - if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */ - return -1; - if (onionskins_n_processed[onionskin_type] == 0 || - onionskins_usec_internal[onionskin_type] == 0 || - onionskins_usec_roundtrip[onionskin_type] == 0) - return -1; - - overhead = onionskins_usec_roundtrip[onionskin_type] - - onionskins_usec_internal[onionskin_type]; - - *usec_out = (uint32_t)(overhead / onionskins_n_processed[onionskin_type]); - *frac_out = U64_TO_DBL(overhead) / onionskins_usec_internal[onionskin_type]; - - return 0; -} - -/** If we've measured overhead for onionskins of type onionskin_type, - * log it. */ -void -cpuworker_log_onionskin_overhead(int severity, int onionskin_type, - const char *onionskin_type_name) -{ - uint32_t overhead; - double relative_overhead; - int r; - - r = get_overhead_for_onionskins(&overhead, &relative_overhead, - onionskin_type); - if (!overhead || r<0) - return; - - log_fn(severity, LD_OR, - "%s onionskins have averaged %u usec overhead (%.2f%%) in " - "cpuworker code ", - onionskin_type_name, (unsigned)overhead, relative_overhead*100); -} - -/** Called when we get data from a cpuworker. If the answer is not complete, - * wait for a complete answer. If the answer is complete, - * process it as appropriate. - */ -int -connection_cpu_process_inbuf(connection_t *conn) -{ - uint64_t chan_id; - circid_t circ_id; - channel_t *p_chan = NULL; - circuit_t *circ; - - tor_assert(conn); - tor_assert(conn->type == CONN_TYPE_CPUWORKER); - - if (!connection_get_inbuf_len(conn)) - return 0; - - if (conn->state == CPUWORKER_STATE_BUSY_ONION) { - cpuworker_reply_t rpl; - if (connection_get_inbuf_len(conn) < sizeof(cpuworker_reply_t)) - return 0; /* not yet */ - tor_assert(connection_get_inbuf_len(conn) == sizeof(cpuworker_reply_t)); - - connection_fetch_from_buf((void*)&rpl,sizeof(cpuworker_reply_t),conn); - - tor_assert(rpl.magic == CPUWORKER_REPLY_MAGIC); - - if (rpl.timed && rpl.success && - rpl.handshake_type <= MAX_ONION_HANDSHAKE_TYPE) { - /* Time how long this request took. The handshake_type check should be - needless, but let's leave it in to be safe. */ - struct timeval tv_end, tv_diff; - int64_t usec_roundtrip; - tor_gettimeofday(&tv_end); - timersub(&tv_end, &rpl.started_at, &tv_diff); - usec_roundtrip = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; - if (usec_roundtrip >= 0 && - usec_roundtrip < MAX_BELIEVABLE_ONIONSKIN_DELAY) { - ++onionskins_n_processed[rpl.handshake_type]; - onionskins_usec_internal[rpl.handshake_type] += rpl.n_usec; - onionskins_usec_roundtrip[rpl.handshake_type] += usec_roundtrip; - if (onionskins_n_processed[rpl.handshake_type] >= 500000) { - /* Scale down every 500000 handshakes. On a busy server, that's - * less impressive than it sounds. */ - onionskins_n_processed[rpl.handshake_type] /= 2; - onionskins_usec_internal[rpl.handshake_type] /= 2; - onionskins_usec_roundtrip[rpl.handshake_type] /= 2; - } - } - } - /* parse out the circ it was talking about */ - tag_unpack(rpl.tag, &chan_id, &circ_id); - circ = NULL; - log_debug(LD_OR, - "Unpacking cpuworker reply, chan_id is " U64_FORMAT - ", circ_id is %u", - U64_PRINTF_ARG(chan_id), (unsigned)circ_id); - p_chan = channel_find_by_global_id(chan_id); - - if (p_chan) - circ = circuit_get_by_circid_channel(circ_id, p_chan); - - if (rpl.success == 0) { - log_debug(LD_OR, - "decoding onionskin failed. " - "(Old key or bad software.) Closing."); - if (circ) - circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); - goto done_processing; - } - if (!circ) { - /* This happens because somebody sends us a destroy cell and the - * circuit goes away, while the cpuworker is working. This is also - * why our tag doesn't include a pointer to the circ, because we'd - * never know if it's still valid. - */ - log_debug(LD_OR,"processed onion for a circ that's gone. Dropping."); - goto done_processing; - } - tor_assert(! CIRCUIT_IS_ORIGIN(circ)); - if (onionskin_answer(TO_OR_CIRCUIT(circ), - &rpl.created_cell, - (const char*)rpl.keys, - rpl.rend_auth_material) < 0) { - log_warn(LD_OR,"onionskin_answer failed. Closing."); - circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL); - goto done_processing; - } - log_debug(LD_OR,"onionskin_answer succeeded. Yay."); - } else { - tor_assert(0); /* don't ask me to do handshakes yet */ - } - - done_processing: - conn->state = CPUWORKER_STATE_IDLE; - num_cpuworkers_busy--; - if (conn->timestamp_created < last_rotation_time) { - connection_mark_for_close(conn); - num_cpuworkers--; - spawn_enough_cpuworkers(); - } else { - process_pending_task(conn); - } - return 0; -} - -/** Implement a cpuworker. 'data' is an fdarray as returned by socketpair. - * Read and writes from fdarray[1]. Reads requests, writes answers. - * - * Request format: - * cpuworker_request_t. - * Response format: - * cpuworker_reply_t - */ -static void -cpuworker_main(void *data) -{ - /* For talking to the parent thread/process */ - tor_socket_t *fdarray = data; - tor_socket_t fd; - - /* variables for onion processing */ - server_onion_keys_t onion_keys; - cpuworker_request_t req; - cpuworker_reply_t rpl; - - fd = fdarray[1]; /* this side is ours */ -#ifndef TOR_IS_MULTITHREADED - tor_close_socket(fdarray[0]); /* this is the side of the socketpair the - * parent uses */ - tor_free_all(1); /* so the child doesn't hold the parent's fd's open */ - handle_signals(0); /* ignore interrupts from the keyboard, etc */ -#endif - tor_free(data); - - setup_server_onion_keys(&onion_keys); - - for (;;) { - if (read_all(fd, (void *)&req, sizeof(req), 1) != sizeof(req)) { - log_info(LD_OR, "read request failed. Exiting."); - goto end; - } - tor_assert(req.magic == CPUWORKER_REQUEST_MAGIC); - - memset(&rpl, 0, sizeof(rpl)); - - if (req.task == CPUWORKER_TASK_ONION) { - const create_cell_t *cc = &req.create_cell; - created_cell_t *cell_out = &rpl.created_cell; - struct timeval tv_start, tv_end; - int n; - rpl.timed = req.timed; - rpl.started_at = req.started_at; - rpl.handshake_type = cc->handshake_type; - if (req.timed) - tor_gettimeofday(&tv_start); - n = onion_skin_server_handshake(cc->handshake_type, - cc->onionskin, cc->handshake_len, - &onion_keys, - cell_out->reply, - rpl.keys, CPATH_KEY_MATERIAL_LEN, - rpl.rend_auth_material); - if (n < 0) { - /* failure */ - log_debug(LD_OR,"onion_skin_server_handshake failed."); - memset(&rpl, 0, sizeof(rpl)); - memcpy(rpl.tag, req.tag, TAG_LEN); - rpl.success = 0; - } else { - /* success */ - log_debug(LD_OR,"onion_skin_server_handshake succeeded."); - memcpy(rpl.tag, req.tag, TAG_LEN); - cell_out->handshake_len = n; - switch (cc->cell_type) { - case CELL_CREATE: - cell_out->cell_type = CELL_CREATED; break; - case CELL_CREATE2: - cell_out->cell_type = CELL_CREATED2; break; - case CELL_CREATE_FAST: - cell_out->cell_type = CELL_CREATED_FAST; break; - default: - tor_assert(0); - goto end; - } - rpl.success = 1; - } - rpl.magic = CPUWORKER_REPLY_MAGIC; - if (req.timed) { - struct timeval tv_diff; - int64_t usec; - tor_gettimeofday(&tv_end); - timersub(&tv_end, &tv_start, &tv_diff); - usec = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; - if (usec < 0 || usec > MAX_BELIEVABLE_ONIONSKIN_DELAY) - rpl.n_usec = MAX_BELIEVABLE_ONIONSKIN_DELAY; - else - rpl.n_usec = (uint32_t) usec; - } - if (write_all(fd, (void*)&rpl, sizeof(rpl), 1) != sizeof(rpl)) { - log_err(LD_BUG,"writing response buf failed. Exiting."); - goto end; - } - log_debug(LD_OR,"finished writing response."); - } else if (req.task == CPUWORKER_TASK_SHUTDOWN) { - log_info(LD_OR,"Clean shutdown: exiting"); - goto end; - } - memwipe(&req, 0, sizeof(req)); - memwipe(&rpl, 0, sizeof(req)); - } - end: - memwipe(&req, 0, sizeof(req)); - memwipe(&rpl, 0, sizeof(req)); - release_server_onion_keys(&onion_keys); - tor_close_socket(fd); - crypto_thread_cleanup(); - spawn_exit(); -} - -/** Launch a new cpuworker. Return 0 if we're happy, -1 if we failed. - */ -static int -spawn_cpuworker(void) -{ - tor_socket_t *fdarray; - tor_socket_t fd; - connection_t *conn; - int err; - - fdarray = tor_malloc(sizeof(tor_socket_t)*2); - if ((err = tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fdarray)) < 0) { - log_warn(LD_NET, "Couldn't construct socketpair for cpuworker: %s", - tor_socket_strerror(-err)); - tor_free(fdarray); - return -1; - } - - tor_assert(SOCKET_OK(fdarray[0])); - tor_assert(SOCKET_OK(fdarray[1])); - - fd = fdarray[0]; - spawn_func(cpuworker_main, (void*)fdarray); - log_debug(LD_OR,"just spawned a cpu worker."); -#ifndef TOR_IS_MULTITHREADED - tor_close_socket(fdarray[1]); /* don't need the worker's side of the pipe */ - tor_free(fdarray); -#endif - - conn = connection_new(CONN_TYPE_CPUWORKER, AF_UNIX); - - /* set up conn so it's got all the data we need to remember */ - conn->s = fd; - conn->address = tor_strdup("localhost"); - tor_addr_make_unspec(&conn->addr); - - if (set_socket_nonblocking(fd) == -1) { - connection_free(conn); /* this closes fd */ - return -1; - } - - if (connection_add(conn) < 0) { /* no space, forget it */ - log_warn(LD_NET,"connection_add for cpuworker failed. Giving up."); - connection_free(conn); /* this closes fd */ - return -1; - } - - conn->state = CPUWORKER_STATE_IDLE; - connection_start_reading(conn); - - return 0; /* success */ -} - -/** If we have too few or too many active cpuworkers, try to spawn new ones - * or kill idle ones. - */ -static void -spawn_enough_cpuworkers(void) -{ - int num_cpuworkers_needed = get_num_cpus(get_options()); - int reseed = 0; - - if (num_cpuworkers_needed < MIN_CPUWORKERS) - num_cpuworkers_needed = MIN_CPUWORKERS; - if (num_cpuworkers_needed > MAX_CPUWORKERS) - num_cpuworkers_needed = MAX_CPUWORKERS; - - while (num_cpuworkers < num_cpuworkers_needed) { - if (spawn_cpuworker() < 0) { - log_warn(LD_GENERAL,"Cpuworker spawn failed. Will try again later."); - return; - } - num_cpuworkers++; - reseed++; - } - - if (reseed) - crypto_seed_weak_rng(&request_sample_rng); -} - -/** Take a pending task from the queue and assign it to 'cpuworker'. */ -static void -process_pending_task(connection_t *cpuworker) -{ - or_circuit_t *circ; - create_cell_t *onionskin = NULL; - - tor_assert(cpuworker); - - /* for now only process onion tasks */ - - circ = onion_next_task(&onionskin); - if (!circ) - return; - if (assign_onionskin_to_cpuworker(cpuworker, circ, onionskin)) - log_warn(LD_OR,"assign_to_cpuworker failed. Ignoring."); -} - -/** How long should we let a cpuworker stay busy before we give - * up on it and decide that we have a bug or infinite loop? - * This value is high because some servers with low memory/cpu - * sometimes spend an hour or more swapping, and Tor starves. */ -#define CPUWORKER_BUSY_TIMEOUT (60*60*12) - -/** We have a bug that I can't find. Sometimes, very rarely, cpuworkers get - * stuck in the 'busy' state, even though the cpuworker process thinks of - * itself as idle. I don't know why. But here's a workaround to kill any - * cpuworker that's been busy for more than CPUWORKER_BUSY_TIMEOUT. - */ -static void -cull_wedged_cpuworkers(void) -{ - time_t now = time(NULL); - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (!conn->marked_for_close && - conn->type == CONN_TYPE_CPUWORKER && - conn->state == CPUWORKER_STATE_BUSY_ONION && - conn->timestamp_lastwritten + CPUWORKER_BUSY_TIMEOUT < now) { - log_notice(LD_BUG, - "closing wedged cpuworker. Can somebody find the bug?"); - num_cpuworkers_busy--; - num_cpuworkers--; - connection_mark_for_close(conn); - } - } SMARTLIST_FOREACH_END(conn); -} - -/** Try to tell a cpuworker to perform the public key operations necessary to - * respond to onionskin for the circuit circ. - * - * If cpuworker is defined, assert that he's idle, and use him. Else, - * look for an idle cpuworker and use him. If none idle, queue task onto the - * pending onion list and return. Return 0 if we successfully assign the - * task, or -1 on failure. - */ -int -assign_onionskin_to_cpuworker(connection_t *cpuworker, - or_circuit_t *circ, - create_cell_t *onionskin) -{ - cpuworker_request_t req; - time_t now = approx_time(); - static time_t last_culled_cpuworkers = 0; - int should_time; - - /* Checking for wedged cpuworkers requires a linear search over all - * connections, so let's do it only once a minute. - */ -#define CULL_CPUWORKERS_INTERVAL 60 - - if (last_culled_cpuworkers + CULL_CPUWORKERS_INTERVAL <= now) { - cull_wedged_cpuworkers(); - spawn_enough_cpuworkers(); - last_culled_cpuworkers = now; - } - - if (1) { - if (num_cpuworkers_busy == num_cpuworkers) { - log_debug(LD_OR,"No idle cpuworkers. Queuing."); - if (onion_pending_add(circ, onionskin) < 0) { - tor_free(onionskin); - return -1; - } - return 0; - } - - if (!cpuworker) - cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, - CPUWORKER_STATE_IDLE); - - tor_assert(cpuworker); - - if (!circ->p_chan) { - log_info(LD_OR,"circ->p_chan gone. Failing circ."); - tor_free(onionskin); - return -1; - } - - if (connection_or_digest_is_known_relay(circ->p_chan->identity_digest)) - rep_hist_note_circuit_handshake_completed(onionskin->handshake_type); - - should_time = should_time_request(onionskin->handshake_type); - memset(&req, 0, sizeof(req)); - req.magic = CPUWORKER_REQUEST_MAGIC; - tag_pack(req.tag, circ->p_chan->global_identifier, - circ->p_circ_id); - req.timed = should_time; - - cpuworker->state = CPUWORKER_STATE_BUSY_ONION; - /* touch the lastwritten timestamp, since that's how we check to - * see how long it's been since we asked the question, and sometimes - * we check before the first call to connection_handle_write(). */ - cpuworker->timestamp_lastwritten = now; - num_cpuworkers_busy++; - - req.task = CPUWORKER_TASK_ONION; - memcpy(&req.create_cell, onionskin, sizeof(create_cell_t)); - - tor_free(onionskin); - - if (should_time) - tor_gettimeofday(&req.started_at); - - connection_write_to_buf((void*)&req, sizeof(req), cpuworker); - memwipe(&req, 0, sizeof(req)); - } - return 0; -} - diff --git a/src/tor/cpuworker.h b/src/tor/cpuworker.h deleted file mode 100644 index 317cef4..0000000 --- a/src/tor/cpuworker.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file cpuworker.h - * \brief Header file for cpuworker.c. - **/ - -#ifndef TOR_CPUWORKER_H -#define TOR_CPUWORKER_H - -void cpu_init(void); -void cpuworkers_rotate(void); -int connection_cpu_finished_flushing(connection_t *conn); -int connection_cpu_reached_eof(connection_t *conn); -int connection_cpu_process_inbuf(connection_t *conn); -struct create_cell_t; -int assign_onionskin_to_cpuworker(connection_t *cpuworker, - or_circuit_t *circ, - struct create_cell_t *onionskin); - -uint64_t estimated_usec_for_onionskins(uint32_t n_requests, - uint16_t onionskin_type); -void cpuworker_log_onionskin_overhead(int severity, int onionskin_type, - const char *onionskin_type_name); - -#endif - diff --git a/src/tor/crypto.c b/src/tor/crypto.c deleted file mode 100644 index 3c67c03..0000000 --- a/src/tor/crypto.c +++ /dev/null @@ -1,3176 +0,0 @@ -/* Copyright (c) 2001, Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file crypto.c - * \brief Wrapper functions to present a consistent interface to - * public-key and symmetric cryptography operations from OpenSSL. - **/ - -#include "orconfig.h" - -#ifdef _WIN32 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#define WIN32_LEAN_AND_MEAN -#include -#include -/* Windows defines this; so does OpenSSL 0.9.8h and later. We don't actually - * use either definition. */ -#undef OCSP_RESPONSE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_CTYPE_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYS_FCNTL_H -#include -#endif - -#define CRYPTO_PRIVATE -#include "crypto.h" -#include "torlog.h" -#include "aes.h" -#include "tor_util.h" -#include "container.h" -#include "tor_compat.h" -#include "sandbox.h" - -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) -#error "We require OpenSSL >= 0.9.8" -#endif - -#ifdef ANDROID -/* Android's OpenSSL seems to have removed all of its Engine support. */ -#define DISABLE_ENGINES -#endif - -/** Longest recognized */ -#define MAX_DNS_LABEL_SIZE 63 - -/** Macro: is k a valid RSA public or private key? */ -#define PUBLIC_KEY_OK(k) ((k) && (k)->key && RSA_get0_n((k)->key)) -/** Macro: is k a valid RSA private key? */ -#define PRIVATE_KEY_OK(k) ((k) && (k)->key && RSA_get0_p((k)->key)) - -#ifdef TOR_IS_MULTITHREADED -/** A number of preallocated mutexes for use by OpenSSL. */ -static tor_mutex_t **openssl_mutexes_ = NULL; -/** How many mutexes have we allocated for use by OpenSSL? */ -static int n_openssl_mutexes_ = 0; -#endif - -/** A public key, or a public/private key-pair. */ -struct crypto_pk_t -{ - int refs; /**< reference count, so we don't have to copy keys */ - RSA *key; /**< The key itself */ -}; - -/** Key and stream information for a stream cipher. */ -struct crypto_cipher_t -{ - char key[CIPHER_KEY_LEN]; /**< The raw key. */ - char iv[CIPHER_IV_LEN]; /**< The initial IV. */ - aes_cnt_cipher_t *cipher; /**< The key in format usable for counter-mode AES - * encryption */ -}; - -/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake - * while we're waiting for the second.*/ -struct crypto_dh_t { - DH *dh; /**< The openssl DH object */ -}; - -static int setup_openssl_threading(void); -static int tor_check_dh_key(int severity, BIGNUM *bn); - -/** Return the number of bytes added by padding method padding. - */ -static INLINE int -crypto_get_rsa_padding_overhead(int padding) -{ - switch (padding) - { - case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; - default: tor_assert(0); return -1; - } -} - -/** Given a padding method padding, return the correct OpenSSL constant. - */ -static INLINE int -crypto_get_rsa_padding(int padding) -{ - switch (padding) - { - case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; - default: tor_assert(0); return -1; - } -} - -/** Boolean: has OpenSSL's crypto been initialized? */ -static int crypto_global_initialized_ = 0; - -/** Log all pending crypto errors at level severity. Use - * doing to describe our current activities. - */ -static void -crypto_log_errors(int severity, const char *doing) -{ - unsigned long err; - const char *msg, *lib, *func; - while ((err = ERR_get_error()) != 0) { - msg = (const char*)ERR_reason_error_string(err); - lib = (const char*)ERR_lib_error_string(err); - func = (const char*)ERR_func_error_string(err); - if (!msg) msg = "(null)"; - if (!lib) lib = "(null)"; - if (!func) func = "(null)"; - if (doing) { - tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", - doing, msg, lib, func); - } else { - tor_log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)", - msg, lib, func); - } - } -} - -#ifndef DISABLE_ENGINES -/** Log any OpenSSL engines we're using at NOTICE. */ -static void -log_engine(const char *fn, ENGINE *e) -{ - if (e) { - const char *name, *id; - name = ENGINE_get_name(e); - id = ENGINE_get_id(e); - log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]", - fn, name?name:"?", id?id:"?"); - } else { - log_info(LD_CRYPTO, "Using default implementation for %s", fn); - } -} -#endif - -#ifndef DISABLE_ENGINES -/** Try to load an engine in a shared library via fully qualified path. - */ -static ENGINE * -try_load_engine(const char *path, const char *engine) -{ - ENGINE *e = ENGINE_by_id("dynamic"); - if (e) { - if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) || - !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) || - !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) || - !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { - ENGINE_free(e); - e = NULL; - } - } - return e; -} -#endif - -/* Returns a trimmed and human-readable version of an openssl version string -* raw_version. They are usually in the form of 'OpenSSL 1.0.0b 10 -* May 2012' and this will parse them into a form similar to '1.0.0b' */ -static char * -parse_openssl_version_str(const char *raw_version) -{ - const char *end_of_version = NULL; - /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's - trim that down. */ - if (!strcmpstart(raw_version, "OpenSSL ")) { - raw_version += strlen("OpenSSL "); - end_of_version = strchr(raw_version, ' '); - } - - if (end_of_version) - return tor_strndup(raw_version, - end_of_version-raw_version); - else - return tor_strdup(raw_version); -} - -static char *crypto_openssl_version_str = NULL; -/* Return a human-readable version of the run-time openssl version number. */ -const char * -crypto_openssl_get_version_str(void) -{ - if (crypto_openssl_version_str == NULL) { - const char *raw_version = SSLeay_version(SSLEAY_VERSION); - crypto_openssl_version_str = parse_openssl_version_str(raw_version); - } - return crypto_openssl_version_str; -} - -static char *crypto_openssl_header_version_str = NULL; -/* Return a human-readable version of the compile-time openssl version -* number. */ -const char * -crypto_openssl_get_header_version_str(void) -{ - if (crypto_openssl_header_version_str == NULL) { - crypto_openssl_header_version_str = - parse_openssl_version_str(OPENSSL_VERSION_TEXT); - } - return crypto_openssl_header_version_str; -} - -/** Initialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_global_init(int useAccel, const char *accelName, const char *accelDir) -{ - if (!crypto_global_initialized_) { - ERR_load_crypto_strings(); - 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 (SSLeay() < OPENSSL_V_SERIES(1,0,0)) { - log_notice(LD_CRYPTO, - "Your OpenSSL version seems to be %s. We recommend 1.0.0 " - "or later.", - crypto_openssl_get_version_str()); - } - - if (useAccel > 0) { -#ifdef DISABLE_ENGINES - (void)accelName; - (void)accelDir; - log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled."); -#else - ENGINE *e = NULL; - - log_info(LD_CRYPTO, "Initializing OpenSSL engine support."); - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - if (accelName) { - if (accelDir) { - log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\"" - " via path \"%s\".", accelName, accelDir); - e = try_load_engine(accelName, accelDir); - } else { - log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\"" - " acceleration support.", accelName); - e = ENGINE_by_id(accelName); - } - if (!e) { - log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".", - accelName); - } else { - log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".", - accelName); - } - } - if (e) { - log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine," - " setting default ciphers."); - ENGINE_set_default(e, ENGINE_METHOD_ALL); - } - /* Log, if available, the intersection of the set of algorithms - used by Tor and the set of algorithms available in the engine */ - log_engine("RSA", ENGINE_get_default_RSA()); - log_engine("DH", ENGINE_get_default_DH()); -#if OPENSSL_VERSION_NUMBER < 0x30000000L - log_engine("ECDH", ENGINE_get_default_ECDH()); - log_engine("ECDSA", ENGINE_get_default_ECDSA()); -#endif - log_engine("RAND", ENGINE_get_default_RAND()); - log_engine("RAND (which we will not use)", ENGINE_get_default_RAND()); - log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1)); - log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc)); - log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb)); - log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc)); -#ifdef NID_aes_128_ctr - log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr)); -#endif -#ifdef NID_aes_128_gcm - log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm)); -#endif - log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc)); -#ifdef NID_aes_256_gcm - log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm)); -#endif - -#endif - } else { - log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); - } - -#if OPENSSL_VERSION_NUMBER < 0x30000000L - if (RAND_get_rand_method() != RAND_SSLeay()) { - log_notice(LD_CRYPTO, "It appears that one of our engines has provided " - "a replacement the OpenSSL RNG. Resetting it to the default " - "implementation."); - RAND_set_rand_method(RAND_SSLeay()); - } -#endif - - evaluate_evp_for_aes(-1); - evaluate_ctr_for_aes(); - - return crypto_seed_rng(1); - } - return 0; -} - -/** Free crypto resources held by this thread. */ -void -crypto_thread_cleanup(void) -{ - ERR_remove_state(0); -} - -/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ -crypto_pk_t * -crypto_new_pk_from_rsa_(RSA *rsa) -{ - crypto_pk_t *env; - tor_assert(rsa); - env = tor_malloc(sizeof(crypto_pk_t)); - env->refs = 1; - env->key = rsa; - return env; -} - -/** Helper, used by tor-checkkey.c and tor-gencert.c. Return the RSA from a - * crypto_pk_t. */ -RSA * -crypto_pk_get_rsa_(crypto_pk_t *env) -{ - return env->key; -} - -/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff - * private is set, include the private-key portion of the key. */ -EVP_PKEY * -crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private) -{ - RSA *key = NULL; - EVP_PKEY *pkey = NULL; - tor_assert(env->key); - if (private) { - if (!(key = RSAPrivateKey_dup(env->key))) - goto error; - } else { - if (!(key = RSAPublicKey_dup(env->key))) - goto error; - } - if (!(pkey = EVP_PKEY_new())) - goto error; - if (!(EVP_PKEY_assign_RSA(pkey, key))) - goto error; - return pkey; - error: - if (pkey) - EVP_PKEY_free(pkey); - if (key) - RSA_free(key); - return NULL; -} - -/** Used by tortls.c: Get the DH* from a crypto_dh_t. - */ -DH * -crypto_dh_get_dh_(crypto_dh_t *dh) -{ - return dh->dh; -} - -/** Allocate and return storage for a public key. The key itself will not yet - * be set. - */ -crypto_pk_t * -crypto_pk_new(void) -{ - RSA *rsa; - - rsa = RSA_new(); - tor_assert(rsa); - return crypto_new_pk_from_rsa_(rsa); -} - -/** Release a reference to an asymmetric key; when all the references - * are released, free the key. - */ -void -crypto_pk_free(crypto_pk_t *env) -{ - if (!env) - return; - - if (--env->refs > 0) - return; - tor_assert(env->refs == 0); - - if (env->key) - RSA_free(env->key); - - tor_free(env); -} - -/** Allocate and return a new symmetric cipher using the provided key and iv. - * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. If you - * provide NULL in place of either one, it is generated at random. - */ -crypto_cipher_t * -crypto_cipher_new_with_iv(const char *key, const char *iv) -{ - crypto_cipher_t *env; - - env = tor_malloc_zero(sizeof(crypto_cipher_t)); - - if (key == NULL) - crypto_rand(env->key, CIPHER_KEY_LEN); - else - memcpy(env->key, key, CIPHER_KEY_LEN); - if (iv == NULL) - crypto_rand(env->iv, CIPHER_IV_LEN); - else - memcpy(env->iv, iv, CIPHER_IV_LEN); - - env->cipher = aes_new_cipher(env->key, env->iv); - - return env; -} - -/** Return a new crypto_cipher_t with the provided key and an IV of all - * zero bytes. */ -crypto_cipher_t * -crypto_cipher_new(const char *key) -{ - char zeroiv[CIPHER_IV_LEN]; - memset(zeroiv, 0, sizeof(zeroiv)); - return crypto_cipher_new_with_iv(key, zeroiv); -} - -/** Free a symmetric cipher. - */ -void -crypto_cipher_free(crypto_cipher_t *env) -{ - if (!env) - return; - - tor_assert(env->cipher); - aes_cipher_free(env->cipher); - memwipe(env, 0, sizeof(crypto_cipher_t)); - tor_free(env); -} - -/* public key crypto */ - -/** Generate a bits-bit new public/private keypair in env. - * Return 0 on success, -1 on failure. - */ -int -crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) -{ - tor_assert(env); - - if (env->key) - RSA_free(env->key); - - { - BIGNUM *e = BN_new(); - RSA *r = NULL; - if (!e) - goto done; - if (! BN_set_word(e, 65537)) - goto done; - r = RSA_new(); - if (!r) - goto done; - if (RSA_generate_key_ex(r, bits, e, NULL) == -1) - goto done; - - env->key = r; - r = NULL; - done: - if (e) - BN_clear_free(e); - if (r) - RSA_free(r); - } - - if (!env->key) { - crypto_log_errors(LOG_WARN, "generating RSA key"); - return -1; - } - - return 0; -} - -/** Read a PEM-encoded private key from the len-byte string s - * into env. Return 0 on success, -1 on failure. If len is -1, - * the string is nul-terminated. - */ -/* Used here, and used for testing. */ -int -crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(s); - tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); - - /* Create a read-only memory BIO, backed by the string 's' */ - b = BIO_new_mem_buf((char*)s, (int)len); - if (!b) - return -1; - - if (env->key) - RSA_free(env->key); - - env->key = PEM_read_bio_RSAPrivateKey(b,NULL,NULL,NULL); - - BIO_free(b); - - if (!env->key) { - crypto_log_errors(LOG_WARN, "Error parsing private key"); - return -1; - } - return 0; -} - -/** Read a PEM-encoded private key from the file named by - * keyfile into env. Return 0 on success, -1 on failure. - */ -int -crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile) -{ - char *contents; - int r; - - /* Read the file into a string. */ - contents = read_file_to_str(keyfile, 0, NULL); - if (!contents) { - log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile); - return -1; - } - - /* Try to parse it. */ - r = crypto_pk_read_private_key_from_string(env, contents, -1); - memwipe(contents, 0, strlen(contents)); - tor_free(contents); - if (r) - return -1; /* read_private_key_from_string already warned, so we don't.*/ - - /* Make sure it's valid. */ - if (crypto_pk_check_key(env) <= 0) - return -1; - - return 0; -} - -/** Helper function to implement crypto_pk_write_*_key_to_string. */ -static int -crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, - size_t *len, int is_public) -{ - BUF_MEM *buf; - BIO *b; - int r; - - tor_assert(env); - tor_assert(env->key); - tor_assert(dest); - - b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ - if (!b) - return -1; - - /* Now you can treat b as if it were a file. Just use the - * PEM_*_bio_* functions instead of the non-bio variants. - */ - if (is_public) - r = PEM_write_bio_RSAPublicKey(b, env->key); - else - r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL); - - if (!r) { - crypto_log_errors(LOG_WARN, "writing RSA key to string"); - BIO_free(b); - return -1; - } - - BIO_get_mem_ptr(b, &buf); - (void)BIO_set_close(b, BIO_NOCLOSE); /* so BIO_free doesn't free buf */ - BIO_free(b); - - *dest = tor_malloc(buf->length+1); - memcpy(*dest, buf->data, buf->length); - (*dest)[buf->length] = 0; /* nul terminate it */ - *len = buf->length; - BUF_MEM_free(buf); - - return 0; -} - -/** PEM-encode the public key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 1); -} - -/** PEM-encode the private key portion of env and write it to a - * newly allocated string. On success, set *dest to the new - * string, *len to the string's length, and return 0. On - * failure, return -1. - */ -int -crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, - size_t *len) -{ - return crypto_pk_write_key_to_string_impl(env, dest, len, 0); -} - -/** Read a PEM-encoded public key from the first len characters of - * src, and store the result in env. Return 0 on success, -1 on - * failure. - */ -int -crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, - size_t len) -{ - BIO *b; - - tor_assert(env); - tor_assert(src); - tor_assert(lenkey) - RSA_free(env->key); - env->key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL); - BIO_free(b); - if (!env->key) { - crypto_log_errors(LOG_WARN, "reading public key from string"); - return -1; - } - - return 0; -} - -/** Write the private key from env into the file named by fname, - * PEM-encoded. Return 0 on success, -1 on failure. - */ -int -crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname) -{ - BIO *bio; - char *cp; - long len; - char *s; - int r; - - tor_assert(PRIVATE_KEY_OK(env)); - - if (!(bio = BIO_new(BIO_s_mem()))) - return -1; - if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL) - == 0) { - crypto_log_errors(LOG_WARN, "writing private key"); - BIO_free(bio); - return -1; - } - len = BIO_get_mem_data(bio, &cp); - tor_assert(len >= 0); - s = tor_malloc(len+1); - memcpy(s, cp, len); - s[len]='\0'; - r = write_str_to_file(fname, s, 0); - BIO_free(bio); - memwipe(s, 0, strlen(s)); - tor_free(s); - return r; -} - -/** Return true iff env has a valid key. - */ -int -crypto_pk_check_key(crypto_pk_t *env) -{ - int r; - tor_assert(env); - - r = RSA_check_key(env->key); - if (r <= 0) - crypto_log_errors(LOG_WARN,"checking RSA key"); - return r; -} - -/** Return true iff key contains the private-key portion of the RSA - * key. */ -int -crypto_pk_key_is_private(const crypto_pk_t *key) -{ - tor_assert(key); - return PRIVATE_KEY_OK(key); -} - -/** Return true iff env contains a public key whose public exponent - * equals 65537. - */ -int -crypto_pk_public_exponent_ok(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - return BN_is_word(RSA_get0_e(env->key), 65537); -} - -/** Compare the public-key components of a and b. Return less than 0 - * if a\b. A NULL key is - * considered to be less than all non-NULL keys, and equal to itself. - * - * Note that this may leak information about the keys through timing. - */ -int -crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) -{ - int result; - char a_is_non_null = (a != NULL) && (a->key != NULL); - char b_is_non_null = (b != NULL) && (b->key != NULL); - char an_argument_is_null = !a_is_non_null | !b_is_non_null; - - result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); - if (an_argument_is_null) - return result; - - tor_assert(PUBLIC_KEY_OK(a)); - tor_assert(PUBLIC_KEY_OK(b)); - result = BN_cmp(RSA_get0_n(a->key), RSA_get0_n(b->key)); - if (result) - return result; - return BN_cmp(RSA_get0_e(a->key), RSA_get0_e(b->key)); -} - -/** Compare the public-key components of a and b. Return non-zero iff - * a==b. A NULL key is considered to be distinct from all non-NULL - * keys, and equal to itself. - * - * Note that this may leak information about the keys through timing. - */ -int -crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b) -{ - return (crypto_pk_cmp_keys(a, b) == 0); -} - -/** Return the size of the public key modulus in env, in bytes. */ -size_t -crypto_pk_keysize(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - return (size_t) RSA_size(env->key); -} - -/** Return the size of the public key modulus of env, in bits. */ -int -crypto_pk_num_bits(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - tor_assert(RSA_get0_n(env->key)); - - return BN_num_bits(RSA_get0_n(env->key)); -} - -/** Increase the reference count of env, and return it. - */ -crypto_pk_t * -crypto_pk_dup_key(crypto_pk_t *env) -{ - tor_assert(env); - tor_assert(env->key); - - env->refs++; - return env; -} - -/** Make a real honest-to-goodness copy of env, and return it. */ -crypto_pk_t * -crypto_pk_copy_full(crypto_pk_t *env) -{ - RSA *new_key; - int privatekey = 0; - tor_assert(env); - tor_assert(env->key); - - if (PRIVATE_KEY_OK(env)) { - new_key = RSAPrivateKey_dup(env->key); - privatekey = 1; - } else { - new_key = RSAPublicKey_dup(env->key); - } - if (!new_key) { - log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", - privatekey?"private":"public"); - crypto_log_errors(LOG_ERR, - privatekey ? "Duplicating a private key" : - "Duplicating a public key"); - tor_fragile_assert(); - return NULL; - } - - return crypto_new_pk_from_rsa_(new_key); -} - -/** Encrypt fromlen bytes from from with the public key - * in env, using the padding method padding. On success, - * write the result to to, and return the number of bytes - * written. On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, int padding) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen= crypto_pk_keysize(env)); - - r = RSA_public_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - if (r<0) { - crypto_log_errors(LOG_WARN, "performing RSA encryption"); - return -1; - } - return r; -} - -/** Decrypt fromlen bytes from from with the private key - * in env, using the padding method padding. On success, - * write the result to to, and return the number of bytes - * written. On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_private_decrypt(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(env->key); - tor_assert(fromlen= crypto_pk_keysize(env)); - if (!RSA_get0_p(env->key)) - /* Not a private key */ - return -1; - - r = RSA_private_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, crypto_get_rsa_padding(padding)); - - if (r<0) { - crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG, - "performing RSA decryption"); - return -1; - } - return r; -} - -/** Check the signature in from (fromlen bytes long) with the - * public key in env, using PKCS1 padding. On success, write the - * signed data to to, and return the number of bytes written. - * On failure, return -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_public_checksig(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - r = RSA_public_decrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); - - if (r<0) { - crypto_log_errors(LOG_WARN, "checking RSA signature"); - return -1; - } - return r; -} - -/** Check a siglen-byte long signature at sig against - * datalen bytes of data at data, using the public key - * in env. Return 0 if sig is a correct signature for - * SHA1(data). Else return -1. - */ -int -crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, - size_t datalen, const char *sig, size_t siglen) -{ - char digest[DIGEST_LEN]; - char *buf; - size_t buflen; - int r; - - tor_assert(env); - tor_assert(data); - tor_assert(sig); - tor_assert(datalen < SIZE_T_CEILING); - tor_assert(siglen < SIZE_T_CEILING); - - if (crypto_digest(digest,data,datalen)<0) { - log_warn(LD_BUG, "couldn't compute digest"); - return -1; - } - buflen = crypto_pk_keysize(env); - buf = tor_malloc(buflen); - r = crypto_pk_public_checksig(env,buf,buflen,sig,siglen); - if (r != DIGEST_LEN) { - log_warn(LD_CRYPTO, "Invalid signature"); - tor_free(buf); - return -1; - } - if (tor_memneq(buf, digest, DIGEST_LEN)) { - log_warn(LD_CRYPTO, "Signature mismatched with digest."); - tor_free(buf); - return -1; - } - tor_free(buf); - - return 0; -} - -/** Sign fromlen bytes of data from from with the private key in - * env, using PKCS1 padding. On success, write the signature to - * to, and return the number of bytes written. On failure, return - * -1. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen) -{ - int r; - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - tor_assert(tolen >= crypto_pk_keysize(env)); - if (!RSA_get0_p(env->key)) - /* Not a private key */ - return -1; - - r = RSA_private_encrypt((int)fromlen, - (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); - if (r<0) { - crypto_log_errors(LOG_WARN, "generating RSA signature"); - return -1; - } - return r; -} - -/** Compute a SHA1 digest of fromlen bytes of data stored at - * from; sign the data with the private key in env, and - * store it in to. Return the number of bytes written on - * success, and -1 on failure. - * - * tolen is the number of writable bytes in to, and must be - * at least the length of the modulus of env. - */ -int -crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen) -{ - int r; - char digest[DIGEST_LEN]; - if (crypto_digest(digest,from,fromlen)<0) - return -1; - r = crypto_pk_private_sign(env,to,tolen,digest,DIGEST_LEN); - memwipe(digest, 0, sizeof(digest)); - return r; -} - -/** Perform a hybrid (public/secret) encryption on fromlen - * bytes of data from from, with padding type 'padding', - * storing the results on to. - * - * Returns the number of bytes written on success, -1 on failure. - * - * The encrypted data consists of: - * - The source data, padded and encrypted with the public key, if the - * padded source data is no longer than the public key, and force - * is false, OR - * - The beginning of the source data prefixed with a 16-byte symmetric key, - * padded and encrypted with the public key; followed by the rest of - * the source data encrypted in AES-CTR mode with the symmetric key. - */ -int -crypto_pk_public_hybrid_encrypt(crypto_pk_t *env, - char *to, size_t tolen, - const char *from, - size_t fromlen, - int padding, int force) -{ - int overhead, outlen, r; - size_t pkeylen, symlen; - crypto_cipher_t *cipher = NULL; - char *buf = NULL; - - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < SIZE_T_CEILING); - - overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding)); - pkeylen = crypto_pk_keysize(env); - - if (!force && fromlen+overhead <= pkeylen) { - /* It all fits in a single encrypt. */ - return crypto_pk_public_encrypt(env,to, - tolen, - from,fromlen,padding); - } - tor_assert(tolen >= fromlen + overhead + CIPHER_KEY_LEN); - tor_assert(tolen >= pkeylen); - - cipher = crypto_cipher_new(NULL); /* generate a new key. */ - - buf = tor_malloc(pkeylen+1); - memcpy(buf, cipher->key, CIPHER_KEY_LEN); - memcpy(buf+CIPHER_KEY_LEN, from, pkeylen-overhead-CIPHER_KEY_LEN); - - /* Length of symmetrically encrypted data. */ - symlen = fromlen-(pkeylen-overhead-CIPHER_KEY_LEN); - - outlen = crypto_pk_public_encrypt(env,to,tolen,buf,pkeylen-overhead,padding); - if (outlen!=(int)pkeylen) { - goto err; - } - r = crypto_cipher_encrypt(cipher, to+outlen, - from+pkeylen-overhead-CIPHER_KEY_LEN, symlen); - - if (r<0) goto err; - memwipe(buf, 0, pkeylen); - tor_free(buf); - crypto_cipher_free(cipher); - tor_assert(outlen+symlen < INT_MAX); - return (int)(outlen + symlen); - err: - - memwipe(buf, 0, pkeylen); - tor_free(buf); - crypto_cipher_free(cipher); - return -1; -} - -/** Invert crypto_pk_public_hybrid_encrypt. */ -int -crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, - char *to, - size_t tolen, - const char *from, - size_t fromlen, - int padding, int warnOnFailure) -{ - int outlen, r; - size_t pkeylen; - crypto_cipher_t *cipher = NULL; - char *buf = NULL; - - tor_assert(fromlen < SIZE_T_CEILING); - pkeylen = crypto_pk_keysize(env); - - if (fromlen <= pkeylen) { - return crypto_pk_private_decrypt(env,to,tolen,from,fromlen,padding, - warnOnFailure); - } - - buf = tor_malloc(pkeylen); - outlen = crypto_pk_private_decrypt(env,buf,pkeylen,from,pkeylen,padding, - warnOnFailure); - if (outlen<0) { - log_fn(warnOnFailure?LOG_WARN:LOG_DEBUG, LD_CRYPTO, - "Error decrypting public-key data"); - goto err; - } - if (outlen < CIPHER_KEY_LEN) { - log_fn(warnOnFailure?LOG_WARN:LOG_INFO, LD_CRYPTO, - "No room for a symmetric key"); - goto err; - } - cipher = crypto_cipher_new(buf); - if (!cipher) { - goto err; - } - memcpy(to,buf+CIPHER_KEY_LEN,outlen-CIPHER_KEY_LEN); - outlen -= CIPHER_KEY_LEN; - tor_assert(tolen - outlen >= fromlen - pkeylen); - r = crypto_cipher_decrypt(cipher, to+outlen, from+pkeylen, fromlen-pkeylen); - if (r<0) - goto err; - memwipe(buf,0,pkeylen); - tor_free(buf); - crypto_cipher_free(cipher); - tor_assert(outlen + fromlen < INT_MAX); - return (int)(outlen + (fromlen-pkeylen)); - err: - memwipe(buf,0,pkeylen); - tor_free(buf); - crypto_cipher_free(cipher); - return -1; -} - -/** ASN.1-encode the public portion of pk into dest. - * Return -1 on error, or the number of characters used on success. - */ -int -crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len) -{ - int len; - unsigned char *buf = NULL; - - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - - if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { - OPENSSL_free(buf); - return -1; - } - /* We don't encode directly into 'dest', because that would be illegal - * type-punning. (C99 is smarter than me, C99 is smarter than me...) - */ - memcpy(dest,buf,len); - OPENSSL_free(buf); - return len; -} - -/** Decode an ASN.1-encoded public key from str; return the result on - * success and NULL on failure. - */ -crypto_pk_t * -crypto_pk_asn1_decode(const char *str, size_t len) -{ - RSA *rsa; - unsigned char *buf; - const unsigned char *cp; - cp = buf = tor_malloc(len); - memcpy(buf,str,len); - rsa = d2i_RSAPublicKey(NULL, &cp, len); - tor_free(buf); - if (!rsa) { - crypto_log_errors(LOG_WARN,"decoding public key"); - return NULL; - } - return crypto_new_pk_from_rsa_(rsa); -} - -/** Given a private or public key pk, put a SHA1 hash of the - * public key into digest_out (must have DIGEST_LEN bytes of space). - * Return 0 on success, -1 on failure. - */ -int -crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) -{ - unsigned char *buf = NULL; - int len; - - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - if (crypto_digest(digest_out, (char*)buf, len) < 0) { - OPENSSL_free(buf); - return -1; - } - OPENSSL_free(buf); - return 0; -} - -/** Compute all digests of the DER encoding of pk, and store them - * in digests_out. Return 0 on success, -1 on failure. */ -int -crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out) -{ - unsigned char *buf = NULL; - int len; - - len = i2d_RSAPublicKey(pk->key, &buf); - if (len < 0 || buf == NULL) - return -1; - if (crypto_digest_all(digests_out, (char*)buf, len) < 0) { - OPENSSL_free(buf); - return -1; - } - OPENSSL_free(buf); - return 0; -} - -/** Copy in to the outlen-byte buffer out, adding spaces - * every four spaces. */ -void -crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) -{ - int n = 0; - char *end = out+outlen; - tor_assert(outlen < SIZE_T_CEILING); - - while (*in && outpk, put a fingerprint of the - * public key into fp_out (must have at least FINGERPRINT_LEN+1 bytes of - * space). Return 0 on success, -1 on failure. - * - * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding - * of the public key, converted to hexadecimal, in upper case, with a - * space after every four digits. - * - * If add_space is false, omit the spaces. - */ -int -crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) -{ - char digest[DIGEST_LEN]; - char hexdigest[HEX_DIGEST_LEN+1]; - if (crypto_pk_get_digest(pk, digest)) { - return -1; - } - base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); - if (add_space) { - crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); - } else { - strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); - } - return 0; -} - -/* symmetric crypto */ - -/** Return a pointer to the key set for the cipher in env. - */ -const char * -crypto_cipher_get_key(crypto_cipher_t *env) -{ - return env->key; -} - -/** Encrypt fromlen bytes from from using the cipher - * env; on success, store the result to to and return 0. - * On failure, return -1. - */ -int -crypto_cipher_encrypt(crypto_cipher_t *env, char *to, - const char *from, size_t fromlen) -{ - tor_assert(env); - tor_assert(env->cipher); - tor_assert(from); - tor_assert(fromlen); - tor_assert(to); - tor_assert(fromlen < SIZE_T_CEILING); - - aes_crypt(env->cipher, from, fromlen, to); - return 0; -} - -/** Decrypt fromlen bytes from from using the cipher - * env; on success, store the result to to and return 0. - * On failure, return -1. - */ -int -crypto_cipher_decrypt(crypto_cipher_t *env, char *to, - const char *from, size_t fromlen) -{ - tor_assert(env); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < SIZE_T_CEILING); - - aes_crypt(env->cipher, from, fromlen, to); - return 0; -} - -/** Encrypt len bytes on from using the cipher in env; - * on success, return 0. On failure, return -1. - */ -int -crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len) -{ - tor_assert(len < SIZE_T_CEILING); - aes_crypt_inplace(env->cipher, buf, len); - return 0; -} - -/** Encrypt fromlen bytes (at least 1) from from with the key in - * key to the buffer in to of length - * tolen. tolen must be at least fromlen plus - * CIPHER_IV_LEN bytes for the initialization vector. On success, return the - * number of bytes written, on failure, return -1. - */ -int -crypto_cipher_encrypt_with_iv(const char *key, - char *to, size_t tolen, - const char *from, size_t fromlen) -{ - crypto_cipher_t *cipher; - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - - if (fromlen < 1) - return -1; - if (tolen < fromlen + CIPHER_IV_LEN) - return -1; - - cipher = crypto_cipher_new_with_iv(key, NULL); - - memcpy(to, cipher->iv, CIPHER_IV_LEN); - crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen); - crypto_cipher_free(cipher); - return (int)(fromlen + CIPHER_IV_LEN); -} - -/** Decrypt fromlen bytes (at least 1+CIPHER_IV_LEN) from from - * with the key in key to the buffer in to of length - * tolen. tolen must be at least fromlen minus - * CIPHER_IV_LEN bytes for the initialization vector. On success, return the - * number of bytes written, on failure, return -1. - */ -int -crypto_cipher_decrypt_with_iv(const char *key, - char *to, size_t tolen, - const char *from, size_t fromlen) -{ - crypto_cipher_t *cipher; - tor_assert(key); - tor_assert(from); - tor_assert(to); - tor_assert(fromlen < INT_MAX); - - if (fromlen <= CIPHER_IV_LEN) - return -1; - if (tolen < fromlen - CIPHER_IV_LEN) - return -1; - - cipher = crypto_cipher_new_with_iv(key, from); - - crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN); - crypto_cipher_free(cipher); - return (int)(fromlen - CIPHER_IV_LEN); -} - -/* SHA-1 */ - -/** Compute the SHA1 digest of the len bytes on data stored in - * m. Write the DIGEST_LEN byte result into digest. - * Return 0 on success, -1 on failure. - */ -int -crypto_digest(char *digest, const char *m, size_t len) -{ - tor_assert(m); - tor_assert(digest); - return (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL); -} - -/** Compute a 256-bit digest of len bytes in data stored in m, - * using the algorithm algorithm. Write the DIGEST_LEN256-byte result - * into digest. Return 0 on success, -1 on failure. */ -int -crypto_digest256(char *digest, const char *m, size_t len, - digest_algorithm_t algorithm) -{ - tor_assert(m); - tor_assert(digest); - tor_assert(algorithm == DIGEST_SHA256); - return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL); -} - -/** Set the digests_t in ds_out to contain every digest on the - * len bytes in m that we know how to compute. Return 0 on - * success, -1 on failure. */ -int -crypto_digest_all(digests_t *ds_out, const char *m, size_t len) -{ - int i; - tor_assert(ds_out); - memset(ds_out, 0, sizeof(*ds_out)); - if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0) - return -1; - for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) { - if (crypto_digest256(ds_out->d[i], m, len, i) < 0) - return -1; - } - return 0; -} - -/** Return the name of an algorithm, as used in directory documents. */ -const char * -crypto_digest_algorithm_get_name(digest_algorithm_t alg) -{ - switch (alg) { - case DIGEST_SHA1: - return "sha1"; - case DIGEST_SHA256: - return "sha256"; - default: - tor_fragile_assert(); - return "??unknown_digest??"; - } -} - -/** Given the name of a digest algorithm, return its integer value, or -1 if - * the name is not recognized. */ -int -crypto_digest_algorithm_parse_name(const char *name) -{ - if (!strcmp(name, "sha1")) - return DIGEST_SHA1; - else if (!strcmp(name, "sha256")) - return DIGEST_SHA256; - else - return -1; -} - -/** Intermediate information about the digest of a stream of data. */ -struct crypto_digest_t { - union { - SHA_CTX sha1; /**< state for SHA1 */ - SHA256_CTX sha2; /**< state for SHA256 */ - } d; /**< State for the digest we're using. Only one member of the - * union is usable, depending on the value of algorithm. */ - ENUM_BF(digest_algorithm_t) algorithm : 8; /**< Which algorithm is in use? */ -}; - -/** Allocate and return a new digest object to compute SHA1 digests. - */ -crypto_digest_t * -crypto_digest_new(void) -{ - crypto_digest_t *r; - r = tor_malloc(sizeof(crypto_digest_t)); - SHA1_Init(&r->d.sha1); - r->algorithm = DIGEST_SHA1; - return r; -} - -/** Allocate and return a new digest object to compute 256-bit digests - * using algorithm. */ -crypto_digest_t * -crypto_digest256_new(digest_algorithm_t algorithm) -{ - crypto_digest_t *r; - tor_assert(algorithm == DIGEST_SHA256); - r = tor_malloc(sizeof(crypto_digest_t)); - SHA256_Init(&r->d.sha2); - r->algorithm = algorithm; - return r; -} - -/** Deallocate a digest object. - */ -void -crypto_digest_free(crypto_digest_t *digest) -{ - if (!digest) - return; - memwipe(digest, 0, sizeof(crypto_digest_t)); - tor_free(digest); -} - -/** Add len bytes from data to the digest object. - */ -void -crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, - size_t len) -{ - tor_assert(digest); - tor_assert(data); - /* Using the SHA*_*() calls directly means we don't support doing - * SHA in hardware. But so far the delay of getting the question - * to the hardware, and hearing the answer, is likely higher than - * just doing it ourselves. Hashes are fast. - */ - switch (digest->algorithm) { - case DIGEST_SHA1: - SHA1_Update(&digest->d.sha1, (void*)data, len); - break; - case DIGEST_SHA256: - SHA256_Update(&digest->d.sha2, (void*)data, len); - break; - default: - tor_fragile_assert(); - break; - } -} - -/** Compute the hash of the data that has been passed to the digest - * object; write the first out_len bytes of the result to out. - * out_len must be \<= DIGEST256_LEN. - */ -void -crypto_digest_get_digest(crypto_digest_t *digest, - char *out, size_t out_len) -{ - unsigned char r[DIGEST256_LEN]; - crypto_digest_t tmpenv; - tor_assert(digest); - tor_assert(out); - /* memcpy into a temporary ctx, since SHA*_Final clears the context */ - memcpy(&tmpenv, digest, sizeof(crypto_digest_t)); - switch (digest->algorithm) { - case DIGEST_SHA1: - tor_assert(out_len <= DIGEST_LEN); - SHA1_Final(r, &tmpenv.d.sha1); - break; - case DIGEST_SHA256: - tor_assert(out_len <= DIGEST256_LEN); - SHA256_Final(r, &tmpenv.d.sha2); - break; - default: - log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm); - /* If fragile_assert is not enabled, then we should at least not - * leak anything. */ - memset(r, 0xff, sizeof(r)); - tor_fragile_assert(); - break; - } - memcpy(out, r, out_len); - memwipe(r, 0, sizeof(r)); -} - -/** Allocate and return a new digest object with the same state as - * digest - */ -crypto_digest_t * -crypto_digest_dup(const crypto_digest_t *digest) -{ - crypto_digest_t *r; - tor_assert(digest); - r = tor_malloc(sizeof(crypto_digest_t)); - memcpy(r,digest,sizeof(crypto_digest_t)); - return r; -} - -/** Replace the state of the digest object into with the state - * of the digest object from. - */ -void -crypto_digest_assign(crypto_digest_t *into, - const crypto_digest_t *from) -{ - tor_assert(into); - tor_assert(from); - memcpy(into,from,sizeof(crypto_digest_t)); -} - -/** Given a list of strings in lst, set the len_out-byte digest - * at digest_out to the hash of the concatenation of those strings, - * plus the optional string append, computed with the algorithm - * alg. - * out_len must be \<= DIGEST256_LEN. */ -void -crypto_digest_smartlist(char *digest_out, size_t len_out, - const smartlist_t *lst, const char *append, - digest_algorithm_t alg) -{ - crypto_digest_t *d; - if (alg == DIGEST_SHA1) - d = crypto_digest_new(); - else - d = crypto_digest256_new(alg); - SMARTLIST_FOREACH(lst, const char *, cp, - crypto_digest_add_bytes(d, cp, strlen(cp))); - if (append) - crypto_digest_add_bytes(d, append, strlen(append)); - crypto_digest_get_digest(d, digest_out, len_out); - crypto_digest_free(d); -} - -/** Compute the HMAC-SHA-256 of the msg_len bytes in msg, using - * the key of length key_len. Store the DIGEST256_LEN-byte - * result in hmac_out. - */ -void -crypto_hmac_sha256(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len) -{ - /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */ - tor_assert(key_len < INT_MAX); - tor_assert(msg_len < INT_MAX); - HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, - (unsigned char*)hmac_out, NULL); -} - -/* DH */ - -/** Our DH 'g' parameter */ -#define DH_GENERATOR 2 - -/** Shared P parameter for our circuit-crypto DH key exchanges. */ -static BIGNUM *dh_param_p = NULL; -/** Shared P parameter for our TLS DH key exchanges. */ -static BIGNUM *dh_param_p_tls = NULL; -/** Shared G parameter for our DH key exchanges. */ -static BIGNUM *dh_param_g = NULL; - -/** Generate and return a reasonable and safe DH parameter p. */ -static BIGNUM * -crypto_generate_dynamic_dh_modulus(void) -{ - BIGNUM *dynamic_dh_modulus; - DH *dh_parameters; - int r, dh_codes; - char *s; - - dynamic_dh_modulus = BN_new(); - tor_assert(dynamic_dh_modulus); - - dh_parameters = DH_generate_parameters(DH_BYTES*8, DH_GENERATOR, NULL, NULL); - tor_assert(dh_parameters); - - r = DH_check(dh_parameters, &dh_codes); - tor_assert(r && !dh_codes); - - BN_copy(dynamic_dh_modulus, DH_get0_p(dh_parameters)); - tor_assert(dynamic_dh_modulus); - - DH_free(dh_parameters); - - { /* log the dynamic DH modulus: */ - s = BN_bn2hex(dynamic_dh_modulus); - tor_assert(s); - log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s); - OPENSSL_free(s); - } - - return dynamic_dh_modulus; -} - -/** Store our dynamic DH modulus (and its group parameters) to - fname for future use. */ -static int -crypto_store_dynamic_dh_modulus(const char *fname) -{ - int len, new_len; - DH *dh = NULL; - unsigned char *dh_string_repr = NULL; - char *base64_encoded_dh = NULL; - char *file_string = NULL; - int retval = -1; - static const char file_header[] = "# This file contains stored Diffie-" - "Hellman parameters for future use.\n# You *do not* need to edit this " - "file.\n\n"; - - tor_assert(fname); - - if (!dh_param_p_tls) { - log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist."); - goto done; - } - - if (!(dh = DH_new())) - goto done; - { - BIGNUM *p_bn = BN_dup(dh_param_p_tls); - BIGNUM *g_bn = BN_new(); - if (!p_bn || !g_bn || !BN_set_word(g_bn, DH_GENERATOR) || - !DH_set0_pqg(dh, p_bn, NULL, g_bn)) { - if (p_bn) BN_free(p_bn); - if (g_bn) BN_free(g_bn); - goto done; - } - /* DH_set0_pqg takes ownership of p_bn and g_bn, do not free them */ - } - - len = i2d_DHparams(dh, &dh_string_repr); - if ((len < 0) || (dh_string_repr == NULL)) { - log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); - goto done; - } - - base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */ - new_len = base64_encode(base64_encoded_dh, len * 2, - (char *)dh_string_repr, len); - if (new_len < 0) { - log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus."); - goto done; - } - - /* concatenate file header and the dh parameters blob */ - new_len = tor_asprintf(&file_string, "%s%s", file_header, base64_encoded_dh); - - /* write to file */ - if (write_bytes_to_new_file(fname, file_string, new_len, 0) < 0) { - log_info(LD_CRYPTO, "'%s' was already occupied.", fname); - goto done; - } - - retval = 0; - - done: - if (dh) - DH_free(dh); - if (dh_string_repr) - OPENSSL_free(dh_string_repr); - tor_free(base64_encoded_dh); - tor_free(file_string); - - return retval; -} - -/** Return the dynamic DH modulus stored in fname. If there is no - dynamic DH modulus stored in fname, return NULL. */ -static BIGNUM * -crypto_get_stored_dynamic_dh_modulus(const char *fname) -{ - int retval; - char *contents = NULL; - const char *contents_tmp = NULL; - int dh_codes; - DH *stored_dh = NULL; - BIGNUM *dynamic_dh_modulus = NULL; - int length = 0; - unsigned char *base64_decoded_dh = NULL; - const unsigned char *cp = NULL; - - tor_assert(fname); - - contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); - if (!contents) { - log_info(LD_CRYPTO, "Could not open file '%s'", fname); - goto done; /*usually means that ENOENT. don't try to move file to broken.*/ - } - - /* skip the file header */ - contents_tmp = eat_whitespace(contents); - if (!*contents_tmp) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus file " - "seems corrupted (eat_whitespace)."); - goto err; - } - - /* 'fname' contains the DH parameters stored in base64-ed DER - * format. We are only interested in the DH modulus. - * NOTE: We allocate more storage here than we need. Since we're already - * doing that, we can also add 1 byte extra to appease Coverity's - * scanner. */ - - cp = base64_decoded_dh = tor_malloc_zero(strlen(contents_tmp) + 1); - length = base64_decode((char *)base64_decoded_dh, strlen(contents_tmp), - contents_tmp, strlen(contents_tmp)); - if (length < 0) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64)."); - goto err; - } - - stored_dh = d2i_DHparams(NULL, &cp, length); - if ((!stored_dh) || (cp - base64_decoded_dh != length)) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i)."); - goto err; - } - - { /* check the cryptographic qualities of the stored dynamic DH modulus: */ - retval = DH_check(stored_dh, &dh_codes); - if (!retval || dh_codes) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime."); - goto err; - } - - retval = DH_size(stored_dh); - if (retval < DH_BYTES) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller " - "than '%d' bits.", DH_BYTES*8); - goto err; - } - - if (!BN_is_word(DH_get0_g(stored_dh), 2)) { - log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' " - "as the group generator."); - goto err; - } - } - - { /* log the dynamic DH modulus: */ - char *s = BN_bn2hex(DH_get0_p(stored_dh)); - tor_assert(s); - log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s); - OPENSSL_free(s); - } - - goto done; - - err: - - { - /* move broken prime to $filename.broken */ - char *fname_new=NULL; - tor_asprintf(&fname_new, "%s.broken", fname); - - log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new); - - if (replace_file(fname, fname_new)) - log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.", - fname, fname_new); - - tor_free(fname_new); - } - - if (stored_dh) { - DH_free(stored_dh); - stored_dh = NULL; - } - - done: - tor_free(contents); - tor_free(base64_decoded_dh); - - if (stored_dh) { - dynamic_dh_modulus = BN_dup(DH_get0_p(stored_dh)); - DH_free(stored_dh); - } - - return dynamic_dh_modulus; -} - -/** Set the global TLS Diffie-Hellman modulus. - * If dynamic_dh_modulus_fname is set, try to read a dynamic DH modulus - * off it and use it as the DH modulus. If that's not possible, - * generate a new dynamic DH modulus. - * If dynamic_dh_modulus_fname is NULL, use the Apache mod_ssl DH - * modulus. */ -void -crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) -{ - BIGNUM *tls_prime = NULL; - int store_dh_prime_afterwards = 0; - int r; - - /* If the space is occupied, free the previous TLS DH prime */ - if (dh_param_p_tls) { - BN_clear_free(dh_param_p_tls); - dh_param_p_tls = NULL; - } - - if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */ - log_info(LD_OR, "Using stored dynamic DH modulus."); - tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname); - - if (!tls_prime) { - log_notice(LD_OR, "Generating fresh dynamic DH modulus. " - "This might take a while..."); - tls_prime = crypto_generate_dynamic_dh_modulus(); - - store_dh_prime_afterwards++; - } - } else { /* use the static DH prime modulus used by Apache in mod_ssl: */ - tls_prime = BN_new(); - tor_assert(tls_prime); - - /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see - * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this - * prime. - */ - r =BN_hex2bn(&tls_prime, - "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" - "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" - "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" - "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" - "B0E7393E0F24218EB3"); - tor_assert(r); - } - - tor_assert(tls_prime); - - dh_param_p_tls = tls_prime; - - if (store_dh_prime_afterwards) - /* save the new dynamic DH modulus to disk. */ - if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) { - log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. " - "Make sure your data directory is sane."); - } -} - -/** Initialize dh_param_p and dh_param_g if they are not already - * set. */ -static void -init_dh_param(void) -{ - BIGNUM *circuit_dh_prime, *generator; - int r; - if (dh_param_p && dh_param_g) - return; - - circuit_dh_prime = BN_new(); - generator = BN_new(); - tor_assert(circuit_dh_prime && generator); - - /* Set our generator for all DH parameters */ - r = BN_set_word(generator, DH_GENERATOR); - tor_assert(r); - - /* This is from rfc2409, section 6.2. It's a safe prime, and - supposedly it equals: - 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. - */ - r = BN_hex2bn(&circuit_dh_prime, - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" - "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" - "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" - "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" - "49286651ECE65381FFFFFFFFFFFFFFFF"); - tor_assert(r); - - /* Set the new values as the global DH parameters. */ - dh_param_p = circuit_dh_prime; - dh_param_g = generator; - - /* Ensure that we have TLS DH parameters set up, too, even if we're - going to change them soon. */ - if (!dh_param_p_tls) { - crypto_set_tls_dh_prime(NULL); - } -} - -/** Number of bits to use when choosing the x or y value in a Diffie-Hellman - * handshake. Since we exponentiate by this value, choosing a smaller one - * lets our handhake go faster. - */ -#define DH_PRIVATE_KEY_BITS 320 - -/** Allocate and return a new DH object for a key exchange. - */ -crypto_dh_t * -crypto_dh_new(int dh_type) -{ - crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t)); - - tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS || - dh_type == DH_TYPE_REND); - - if (!dh_param_p) - init_dh_param(); - - if (!(res->dh = DH_new())) - goto err; - - { - BIGNUM *p_bn, *g_bn; - if (dh_type == DH_TYPE_TLS) { - p_bn = BN_dup(dh_param_p_tls); - } else { - p_bn = BN_dup(dh_param_p); - } - g_bn = BN_dup(dh_param_g); - if (!p_bn || !g_bn || !DH_set0_pqg(res->dh, p_bn, NULL, g_bn)) { - if (p_bn) BN_free(p_bn); - if (g_bn) BN_free(g_bn); - goto err; - } - /* DH_set0_pqg takes ownership of p_bn and g_bn, do not free them */ - } - - DH_set_length(res->dh, DH_PRIVATE_KEY_BITS); - - return res; - err: - crypto_log_errors(LOG_WARN, "creating DH object"); - if (res->dh) DH_free(res->dh); /* frees p and g too */ - tor_free(res); - return NULL; -} - -/** Return a copy of dh, sharing its internal state. */ -crypto_dh_t * -crypto_dh_dup(const crypto_dh_t *dh) -{ - crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t)); - dh_new->dh = dh->dh; - DH_up_ref(dh->dh); - return dh_new; -} - -/** Return the length of the DH key in dh, in bytes. - */ -int -crypto_dh_get_bytes(crypto_dh_t *dh) -{ - tor_assert(dh); - return DH_size(dh->dh); -} - -/** Generate \ for our part of the key exchange. Return 0 on - * success, -1 on failure. - */ -int -crypto_dh_generate_public(crypto_dh_t *dh) -{ - again: - if (!DH_generate_key(dh->dh)) { - crypto_log_errors(LOG_WARN, "generating DH key"); - return -1; - } - if (tor_check_dh_key(LOG_WARN, (BIGNUM*)DH_get0_pub_key(dh->dh))<0) { - log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" - "the-universe chances really do happen. Trying again."); - /* Free and clear the keys, so OpenSSL will actually try again. */ - DH_set0_key(dh->dh, NULL, NULL); - goto again; - } - return 0; -} - -/** Generate g^x as necessary, and write the g^x for the key exchange - * as a pubkey_len-byte value into pubkey. Return 0 on - * success, -1 on failure. pubkey_len must be \>= DH_BYTES. - */ -int -crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len) -{ - int bytes; - tor_assert(dh); - if (!DH_get0_pub_key(dh->dh)) { - if (crypto_dh_generate_public(dh)<0) - return -1; - } - - tor_assert(DH_get0_pub_key(dh->dh)); - bytes = BN_num_bytes(DH_get0_pub_key(dh->dh)); - tor_assert(bytes >= 0); - if (pubkey_len < (size_t)bytes) { - log_warn(LD_CRYPTO, - "Weird! pubkey_len (%d) was smaller than DH_BYTES (%d)", - (int) pubkey_len, bytes); - return -1; - } - - memset(pubkey, 0, pubkey_len); - BN_bn2bin(DH_get0_pub_key(dh->dh), (unsigned char*)(pubkey+(pubkey_len-bytes))); - - return 0; -} - -/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is - * okay (in the subgroup [2,p-2]), or -1 if it's bad. - * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips. - */ -static int -tor_check_dh_key(int severity, BIGNUM *bn) -{ - BIGNUM *x; - char *s; - tor_assert(bn); - x = BN_new(); - tor_assert(x); - if (!dh_param_p) - init_dh_param(); - BN_set_word(x, 1); - if (BN_cmp(bn,x)<=0) { - log_fn(severity, LD_CRYPTO, "DH key must be at least 2."); - goto err; - } - BN_copy(x,dh_param_p); - BN_sub_word(x, 1); - if (BN_cmp(bn,x)>=0) { - log_fn(severity, LD_CRYPTO, "DH key must be at most p-2."); - goto err; - } - BN_clear_free(x); - return 0; - err: - BN_clear_free(x); - s = BN_bn2hex(bn); - log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s); - OPENSSL_free(s); - return -1; -} - -#undef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -/** Given a DH key exchange object, and our peer's value of g^y (as a - * pubkey_len-byte value in pubkey) generate - * secret_bytes_out bytes of shared key material and write them - * to secret_out. Return the number of bytes generated on success, - * or -1 on failure. - * - * (We generate key material by computing - * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... - * where || is concatenation.) - */ -ssize_t -crypto_dh_compute_secret(int severity, crypto_dh_t *dh, - const char *pubkey, size_t pubkey_len, - char *secret_out, size_t secret_bytes_out) -{ - char *secret_tmp = NULL; - BIGNUM *pubkey_bn = NULL; - size_t secret_len=0, secret_tmp_len=0; - int result=0; - tor_assert(dh); - tor_assert(secret_bytes_out/DIGEST_LEN <= 255); - tor_assert(pubkey_len < INT_MAX); - - if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey, - (int)pubkey_len, NULL))) - goto error; - if (tor_check_dh_key(severity, pubkey_bn)<0) { - /* Check for invalid public keys. */ - log_fn(severity, LD_CRYPTO,"Rejected invalid g^x"); - goto error; - } - secret_tmp_len = crypto_dh_get_bytes(dh); - secret_tmp = tor_malloc(secret_tmp_len); - result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh); - if (result < 0) { - log_warn(LD_CRYPTO,"DH_compute_key() failed."); - goto error; - } - secret_len = result; - if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len, - (uint8_t*)secret_out, secret_bytes_out)<0) - goto error; - secret_len = secret_bytes_out; - - goto done; - error: - result = -1; - done: - crypto_log_errors(LOG_WARN, "completing DH handshake"); - if (pubkey_bn) - BN_clear_free(pubkey_bn); - if (secret_tmp) { - memwipe(secret_tmp, 0, secret_tmp_len); - tor_free(secret_tmp); - } - if (result < 0) - return result; - else - return secret_len; -} - -/** Given key_in_len bytes of negotiated randomness in key_in - * ("K"), expand it into key_out_len bytes of negotiated key material in - * key_out by taking the first key_out_len bytes of - * H(K | [00]) | H(K | [01]) | .... - * - * This is the key expansion algorithm used in the "TAP" circuit extension - * mechanism; it shouldn't be used for new protocols. - * - * Return 0 on success, -1 on failure. - */ -int -crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, - uint8_t *key_out, size_t key_out_len) -{ - int i; - uint8_t *cp, *tmp = tor_malloc(key_in_len+1); - uint8_t digest[DIGEST_LEN]; - - /* If we try to get more than this amount of key data, we'll repeat blocks.*/ - tor_assert(key_out_len <= DIGEST_LEN*256); - - memcpy(tmp, key_in, key_in_len); - for (cp = key_out, i=0; cp < key_out+key_out_len; - ++i, cp += DIGEST_LEN) { - tmp[key_in_len] = i; - if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1)) - goto err; - memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out))); - } - memwipe(tmp, 0, key_in_len+1); - tor_free(tmp); - memwipe(digest, 0, sizeof(digest)); - return 0; - - err: - memwipe(tmp, 0, key_in_len+1); - tor_free(tmp); - memwipe(digest, 0, sizeof(digest)); - return -1; -} - -/** Expand some secret key material according to RFC5869, using SHA256 as the - * underlying hash. The key_in_len bytes at key_in are the - * secret key material; the salt_in_len bytes at salt_in and the - * info_in_len bytes in info_in_len are the algorithm's "salt" - * and "info" parameters respectively. On success, write key_out_len - * bytes to key_out and return 0. On failure, return -1. - */ -int -crypto_expand_key_material_rfc5869_sha256( - const uint8_t *key_in, size_t key_in_len, - const uint8_t *salt_in, size_t salt_in_len, - const uint8_t *info_in, size_t info_in_len, - uint8_t *key_out, size_t key_out_len) -{ - uint8_t prk[DIGEST256_LEN]; - uint8_t tmp[DIGEST256_LEN + 128 + 1]; - uint8_t mac[DIGEST256_LEN]; - int i; - uint8_t *outp; - size_t tmp_len; - - crypto_hmac_sha256((char*)prk, - (const char*)salt_in, salt_in_len, - (const char*)key_in, key_in_len); - - /* If we try to get more than this amount of key data, we'll repeat blocks.*/ - tor_assert(key_out_len <= DIGEST256_LEN * 256); - tor_assert(info_in_len <= 128); - memset(tmp, 0, sizeof(tmp)); - outp = key_out; - i = 1; - - while (key_out_len) { - size_t n; - if (i > 1) { - memcpy(tmp, mac, DIGEST256_LEN); - memcpy(tmp+DIGEST256_LEN, info_in, info_in_len); - tmp[DIGEST256_LEN+info_in_len] = i; - tmp_len = DIGEST256_LEN + info_in_len + 1; - } else { - memcpy(tmp, info_in, info_in_len); - tmp[info_in_len] = i; - tmp_len = info_in_len + 1; - } - crypto_hmac_sha256((char*)mac, - (const char*)prk, DIGEST256_LEN, - (const char*)tmp, tmp_len); - n = key_out_len < DIGEST256_LEN ? key_out_len : DIGEST256_LEN; - memcpy(outp, mac, n); - key_out_len -= n; - outp += n; - ++i; - } - - memwipe(tmp, 0, sizeof(tmp)); - memwipe(mac, 0, sizeof(mac)); - return 0; -} - -/** Free a DH key exchange object. - */ -void -crypto_dh_free(crypto_dh_t *dh) -{ - if (!dh) - return; - tor_assert(dh->dh); - DH_free(dh->dh); - tor_free(dh); -} - -/* random numbers */ - -/** How many bytes of entropy we add at once. - * - * This is how much entropy OpenSSL likes to add right now, so maybe it will - * work for us too. */ -#define ADD_ENTROPY 32 - -/** True iff it's safe to use RAND_poll after setup. - * - * Versions of OpenSSL prior to 0.9.7k and 0.9.8c had a bug where RAND_poll - * would allocate an fd_set on the stack, open a new file, and try to FD_SET - * that fd without checking whether it fit in the fd_set. Thus, if the - * system has not just been started up, it is unsafe to call */ -#define RAND_POLL_IS_SAFE \ - (OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c')) - -/** Set the seed of the weak RNG to a random value. */ -void -crypto_seed_weak_rng(tor_weak_rng_t *rng) -{ - unsigned seed; - crypto_rand((void*)&seed, sizeof(seed)); - tor_init_weak_random(rng, seed); -} - -/** Try to get out_len bytes of the strongest entropy we can generate, - * storing it into out. - */ -int -crypto_strongest_rand(uint8_t *out, size_t out_len) -{ -#ifdef _WIN32 - static int provider_set = 0; - static HCRYPTPROV provider; -#else - static const char *filenames[] = { - "/dev/srandom", "/dev/urandom", "/dev/random", NULL - }; - int fd, i; - size_t n; -#endif - -#ifdef _WIN32 - if (!provider_set) { - if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET) { - log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]"); - return -1; - } - } - provider_set = 1; - } - if (!CryptGenRandom(provider, out_len, out)) { - log_warn(LD_CRYPTO, "Can't get entropy from CryptoAPI."); - return -1; - } - - return 0; -#else - for (i = 0; filenames[i]; ++i) { - fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0); - if (fd<0) continue; - log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]); - n = read_all(fd, (char*)out, out_len, 0); - close(fd); - if (n != out_len) { - log_warn(LD_CRYPTO, - "Error reading from entropy source (read only %lu bytes).", - (unsigned long)n); - return -1; - } - - return 0; - } - - log_warn(LD_CRYPTO, "Cannot get strong entropy: no entropy source found."); - return -1; -#endif -} - -/** Seed OpenSSL's random number generator with bytes from the operating - * system. startup should be true iff we have just started Tor and - * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. - */ -int -crypto_seed_rng(int startup) -{ - int rand_poll_ok = 0, load_entropy_ok = 0; - uint8_t buf[ADD_ENTROPY]; - - /* OpenSSL has a RAND_poll function that knows about more kinds of - * entropy than we do. We'll try calling that, *and* calling our own entropy - * functions. If one succeeds, we'll accept the RNG as seeded. */ - if (startup || RAND_POLL_IS_SAFE) { - rand_poll_ok = RAND_poll(); - if (rand_poll_ok == 0) - log_warn(LD_CRYPTO, "RAND_poll() failed."); - } - - load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf)); - if (load_entropy_ok) { - RAND_seed(buf, sizeof(buf)); - } - - memwipe(buf, 0, sizeof(buf)); - - if (rand_poll_ok || load_entropy_ok) - return 0; - else - return -1; -} - -/** Write n bytes of strong random data to to. Return 0 on - * success, -1 on failure. - */ -MOCK_IMPL(int, -crypto_rand, (char *to, size_t n)) -{ - int r; - tor_assert(n < INT_MAX); - tor_assert(to); - r = RAND_bytes((unsigned char*)to, (int)n); - if (r == 0) - crypto_log_errors(LOG_WARN, "generating random data"); - return (r == 1) ? 0 : -1; -} - -/** Return a pseudorandom integer, chosen uniformly from the values - * between 0 and max-1 inclusive. max must be between 1 and - * INT_MAX+1, inclusive. */ -int -crypto_rand_int(unsigned int max) -{ - unsigned int val; - unsigned int cutoff; - tor_assert(max <= ((unsigned int)INT_MAX)+1); - tor_assert(max > 0); /* don't div by 0 */ - - /* We ignore any values that are >= 'cutoff,' to avoid biasing the - * distribution with clipping at the upper end of unsigned int's - * range. - */ - cutoff = UINT_MAX - (UINT_MAX%max); - while (1) { - crypto_rand((char*)&val, sizeof(val)); - if (val < cutoff) - return val % max; - } -} - -/** Return a pseudorandom 64-bit integer, chosen uniformly from the values - * between 0 and max-1. */ -uint64_t -crypto_rand_uint64(uint64_t max) -{ - uint64_t val; - uint64_t cutoff; - tor_assert(max < UINT64_MAX); - tor_assert(max > 0); /* don't div by 0 */ - - /* We ignore any values that are >= 'cutoff,' to avoid biasing the - * distribution with clipping at the upper end of unsigned int's - * range. - */ - cutoff = UINT64_MAX - (UINT64_MAX%max); - while (1) { - crypto_rand((char*)&val, sizeof(val)); - if (val < cutoff) - return val % max; - } -} - -/** Return a pseudorandom double d, chosen uniformly from the range - * 0.0 <= d < 1.0. - */ -double -crypto_rand_double(void) -{ - /* We just use an unsigned int here; we don't really care about getting - * more than 32 bits of resolution */ - unsigned int uint; - crypto_rand((char*)&uint, sizeof(uint)); -#if SIZEOF_INT == 4 -#define UINT_MAX_AS_DOUBLE 4294967296.0 -#elif SIZEOF_INT == 8 -#define UINT_MAX_AS_DOUBLE 1.8446744073709552e+19 -#else -#error SIZEOF_INT is neither 4 nor 8 -#endif - return ((double)uint) / UINT_MAX_AS_DOUBLE; -} - -/** Generate and return a new random hostname starting with prefix, - * ending with suffix, and containing no fewer than - * min_rand_len and no more than max_rand_len random base32 - * characters between. - * - * Clip max_rand_len to MAX_DNS_LABEL_SIZE. - **/ -char * -crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix, - const char *suffix) -{ - char *result, *rand_bytes; - int randlen, rand_bytes_len; - size_t resultlen, prefixlen; - - if (max_rand_len > MAX_DNS_LABEL_SIZE) - max_rand_len = MAX_DNS_LABEL_SIZE; - if (min_rand_len > max_rand_len) - min_rand_len = max_rand_len; - - randlen = min_rand_len + crypto_rand_int(max_rand_len - min_rand_len + 1); - - prefixlen = strlen(prefix); - resultlen = prefixlen + strlen(suffix) + randlen + 16; - - rand_bytes_len = ((randlen*5)+7)/8; - if (rand_bytes_len % 5) - rand_bytes_len += 5 - (rand_bytes_len%5); - rand_bytes = tor_malloc(rand_bytes_len); - crypto_rand(rand_bytes, rand_bytes_len); - - result = tor_malloc(resultlen); - memcpy(result, prefix, prefixlen); - base32_encode(result+prefixlen, resultlen-prefixlen, - rand_bytes, rand_bytes_len); - tor_free(rand_bytes); - strlcpy(result+prefixlen+randlen, suffix, resultlen-(prefixlen+randlen)); - - return result; -} - -/** Return a randomly chosen element of sl; or NULL if sl - * is empty. */ -void * -smartlist_choose(const smartlist_t *sl) -{ - int len = smartlist_len(sl); - if (len) - return smartlist_get(sl,crypto_rand_int(len)); - return NULL; /* no elements to choose from */ -} - -/** Scramble the elements of sl into a random order. */ -void -smartlist_shuffle(smartlist_t *sl) -{ - int i; - /* From the end of the list to the front, choose at random from the - positions we haven't looked at yet, and swap that position into the - current position. Remember to give "no swap" the same probability as - any other swap. */ - for (i = smartlist_len(sl)-1; i > 0; --i) { - int j = crypto_rand_int(i+1); - smartlist_swap(sl, i, j); - } -} - -/** Base64 encode srclen bytes of data from src. Write - * the result into dest, if it will fit within destlen - * bytes. Return the number of bytes written on success; -1 if - * destlen is too short, or other failure. - */ -int -base64_encode(char *dest, size_t destlen, const char *src, size_t srclen) -{ - /* FFFF we might want to rewrite this along the lines of base64_decode, if - * it ever shows up in the profile. */ - EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); - int len, ret; - tor_assert(srclen < INT_MAX); - - /* 48 bytes of input -> 64 bytes of output plus newline. - Plus one more byte, in case I'm wrong. - */ - if (destlen < ((srclen/48)+1)*66) { - EVP_ENCODE_CTX_free(ctx); - return -1; - } - if (destlen > SIZE_T_CEILING) { - EVP_ENCODE_CTX_free(ctx); - return -1; - } - - EVP_EncodeInit(ctx); - EVP_EncodeUpdate(ctx, (unsigned char*)dest, &len, - (unsigned char*)src, (int)srclen); - EVP_EncodeFinal(ctx, (unsigned char*)(dest+len), &ret); - ret += len; - EVP_ENCODE_CTX_free(ctx); - return ret; -} - -/** @{ */ -/** Special values used for the base64_decode_table */ -#define X 255 -#define SP 64 -#define PAD 65 -/** @} */ -/** Internal table mapping byte values to what they represent in base64. - * Numbers 0..63 are 6-bit integers. SPs are spaces, and should be - * skipped. Xs are invalid and must not appear in base64. PAD indicates - * end-of-string. */ -static const uint8_t base64_decode_table[256] = { - X, X, X, X, X, X, X, X, X, SP, SP, SP, X, SP, X, X, /* */ - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - SP, X, X, X, X, X, X, X, X, X, X, 62, X, X, X, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, X, X, X, PAD, X, X, - X, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, X, X, X, X, X, - X, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, - X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, -}; - -/** Base64 decode srclen bytes of data from src. Write - * the result into dest, if it will fit within destlen - * bytes. Return the number of bytes written on success; -1 if - * destlen is too short, or other failure. - * - * NOTE 1: destlen is checked conservatively, as though srclen contained no - * spaces or padding. - * - * NOTE 2: This implementation does not check for the correct number of - * padding "=" characters at the end of the string, and does not check - * for internal padding characters. - */ -int -base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) -{ -#ifdef USE_OPENSSL_BASE64 - EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new(); - int len, ret; - /* 64 bytes of input -> *up to* 48 bytes of output. - Plus one more byte, in case I'm wrong. - */ - if (destlen < ((srclen/64)+1)*49) { - EVP_ENCODE_CTX_free(ctx); - return -1; - } - if (destlen > SIZE_T_CEILING) { - EVP_ENCODE_CTX_free(ctx); - return -1; - } - - EVP_DecodeInit(ctx); - EVP_DecodeUpdate(ctx, (unsigned char*)dest, &len, - (unsigned char*)src, srclen); - EVP_DecodeFinal(ctx, (unsigned char*)dest, &ret); - ret += len; - EVP_ENCODE_CTX_free(ctx); - return ret; -#else - const char *eos = src+srclen; - uint32_t n=0; - int n_idx=0; - char *dest_orig = dest; - - /* Max number of bits == srclen*6. - * Number of bytes required to hold all bits == (srclen*6)/8. - * Yes, we want to round down: anything that hangs over the end of a - * byte is padding. */ - if (destlen < (srclen*3)/4) - return -1; - if (destlen > SIZE_T_CEILING) - return -1; - - /* Iterate over all the bytes in src. Each one will add 0 or 6 bits to the - * value we're decoding. Accumulate bits in n, and whenever we have - * 24 bits, batch them into 3 bytes and flush those bytes to dest. - */ - for ( ; src < eos; ++src) { - unsigned char c = (unsigned char) *src; - uint8_t v = base64_decode_table[c]; - switch (v) { - case X: - /* This character isn't allowed in base64. */ - return -1; - case SP: - /* This character is whitespace, and has no effect. */ - continue; - case PAD: - /* We've hit an = character: the data is over. */ - goto end_of_loop; - default: - /* We have an actual 6-bit value. Append it to the bits in n. */ - n = (n<<6) | v; - if ((++n_idx) == 4) { - /* We've accumulated 24 bits in n. Flush them. */ - *dest++ = (n>>16); - *dest++ = (n>>8) & 0xff; - *dest++ = (n) & 0xff; - n_idx = 0; - n = 0; - } - } - } - end_of_loop: - /* If we have leftover bits, we need to cope. */ - switch (n_idx) { - case 0: - default: - /* No leftover bits. We win. */ - break; - case 1: - /* 6 leftover bits. That's invalid; we can't form a byte out of that. */ - return -1; - case 2: - /* 12 leftover bits: The last 4 are padding and the first 8 are data. */ - *dest++ = n >> 4; - break; - case 3: - /* 18 leftover bits: The last 2 are padding and the first 16 are data. */ - *dest++ = n >> 10; - *dest++ = n >> 2; - } - - tor_assert((dest-dest_orig) <= (ssize_t)destlen); - tor_assert((dest-dest_orig) <= INT_MAX); - - return (int)(dest-dest_orig); -#endif -} -#undef X -#undef SP -#undef PAD - -/** Base64 encode DIGEST_LINE bytes from digest, remove the trailing = - * and newline characters, and store the nul-terminated result in the first - * BASE64_DIGEST_LEN+1 bytes of d64. */ -int -digest_to_base64(char *d64, const char *digest) -{ - char buf[256]; - base64_encode(buf, sizeof(buf), digest, DIGEST_LEN); - buf[BASE64_DIGEST_LEN] = '\0'; - memcpy(d64, buf, BASE64_DIGEST_LEN+1); - return 0; -} - -/** Given a base64 encoded, nul-terminated digest in d64 (without - * trailing newline or = characters), decode it and store the result in the - * first DIGEST_LEN bytes at digest. */ -int -digest_from_base64(char *digest, const char *d64) -{ -#ifdef USE_OPENSSL_BASE64 - char buf_in[BASE64_DIGEST_LEN+3]; - char buf[256]; - if (strlen(d64) != BASE64_DIGEST_LEN) - return -1; - memcpy(buf_in, d64, BASE64_DIGEST_LEN); - memcpy(buf_in+BASE64_DIGEST_LEN, "=\n\0", 3); - if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST_LEN) - return -1; - memcpy(digest, buf, DIGEST_LEN); - return 0; -#else - if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN) - return 0; - else - return -1; -#endif -} - -/** Base64 encode DIGEST256_LINE bytes from digest, remove the - * trailing = and newline characters, and store the nul-terminated result in - * the first BASE64_DIGEST256_LEN+1 bytes of d64. */ -int -digest256_to_base64(char *d64, const char *digest) -{ - char buf[256]; - base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN); - buf[BASE64_DIGEST256_LEN] = '\0'; - memcpy(d64, buf, BASE64_DIGEST256_LEN+1); - return 0; -} - -/** Given a base64 encoded, nul-terminated digest in d64 (without - * trailing newline or = characters), decode it and store the result in the - * first DIGEST256_LEN bytes at digest. */ -int -digest256_from_base64(char *digest, const char *d64) -{ -#ifdef USE_OPENSSL_BASE64 - char buf_in[BASE64_DIGEST256_LEN+3]; - char buf[256]; - if (strlen(d64) != BASE64_DIGEST256_LEN) - return -1; - memcpy(buf_in, d64, BASE64_DIGEST256_LEN); - memcpy(buf_in+BASE64_DIGEST256_LEN, "=\n\0", 3); - if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST256_LEN) - return -1; - memcpy(digest, buf, DIGEST256_LEN); - return 0; -#else - if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN) - return 0; - else - return -1; -#endif -} - -/** Implements base32 encoding as in RFC 4648. Limitation: Requires - * that srclen*8 is a multiple of 5. - */ -void -base32_encode(char *dest, size_t destlen, const char *src, size_t srclen) -{ - unsigned int i, v, u; - size_t nbits = srclen * 8, bit; - - tor_assert(srclen < SIZE_T_CEILING/8); - tor_assert((nbits%5) == 0); /* We need an even multiple of 5 bits. */ - tor_assert((nbits/5)+1 <= destlen); /* We need enough space. */ - tor_assert(destlen < SIZE_T_CEILING); - - for (i=0,bit=0; bit < nbits; ++i, bit+=5) { - /* set v to the 16-bit value starting at src[bits/8], 0-padded. */ - v = ((uint8_t)src[bit/8]) << 8; - if (bit+5> (11-(bit%8))) & 0x1F; - dest[i] = BASE32_CHARS[u]; - } - dest[i] = '\0'; -} - -/** Implements base32 decoding as in RFC 4648. Limitation: Requires - * that srclen*5 is a multiple of 8. Returns 0 if successful, -1 otherwise. - */ -int -base32_decode(char *dest, size_t destlen, const char *src, size_t srclen) -{ - /* XXXX we might want to rewrite this along the lines of base64_decode, if - * it ever shows up in the profile. */ - unsigned int i; - size_t nbits, j, bit; - char *tmp; - nbits = srclen * 5; - - tor_assert(srclen < SIZE_T_CEILING / 5); - tor_assert((nbits%8) == 0); /* We need an even multiple of 8 bits. */ - tor_assert((nbits/8) <= destlen); /* We need enough space. */ - tor_assert(destlen < SIZE_T_CEILING); - - /* Convert base32 encoded chars to the 5-bit values that they represent. */ - tmp = tor_malloc_zero(srclen); - for (j = 0; j < srclen; ++j) { - if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61; - else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18; - else if (src[j] > 0x40 && src[j] < 0x5B) tmp[j] = src[j] - 0x41; - else { - log_warn(LD_BUG, "illegal character in base32 encoded string"); - tor_free(tmp); - return -1; - } - } - - /* Assemble result byte-wise by applying five possible cases. */ - for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) { - switch (bit % 40) { - case 0: - dest[i] = (((uint8_t)tmp[(bit/5)]) << 3) + - (((uint8_t)tmp[(bit/5)+1]) >> 2); - break; - case 8: - dest[i] = (((uint8_t)tmp[(bit/5)]) << 6) + - (((uint8_t)tmp[(bit/5)+1]) << 1) + - (((uint8_t)tmp[(bit/5)+2]) >> 4); - break; - case 16: - dest[i] = (((uint8_t)tmp[(bit/5)]) << 4) + - (((uint8_t)tmp[(bit/5)+1]) >> 1); - break; - case 24: - dest[i] = (((uint8_t)tmp[(bit/5)]) << 7) + - (((uint8_t)tmp[(bit/5)+1]) << 2) + - (((uint8_t)tmp[(bit/5)+2]) >> 3); - break; - case 32: - dest[i] = (((uint8_t)tmp[(bit/5)]) << 5) + - ((uint8_t)tmp[(bit/5)+1]); - break; - } - } - - memwipe(tmp, 0, srclen); - tor_free(tmp); - tmp = NULL; - return 0; -} - -/** Implement RFC2440-style iterated-salted S2K conversion: convert the - * secret_len-byte secret into a key_out_len byte - * key_out. As in RFC2440, the first 8 bytes of s2k_specifier - * are a salt; the 9th byte describes how much iteration to do. - * Does not support key_out_len > DIGEST_LEN. - */ -void -secret_to_key(char *key_out, size_t key_out_len, const char *secret, - size_t secret_len, const char *s2k_specifier) -{ - crypto_digest_t *d; - uint8_t c; - size_t count, tmplen; - char *tmp; - tor_assert(key_out_len < SIZE_T_CEILING); - -#define EXPBIAS 6 - c = s2k_specifier[8]; - count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS); -#undef EXPBIAS - - tor_assert(key_out_len <= DIGEST_LEN); - - d = crypto_digest_new(); - tmplen = 8+secret_len; - tmp = tor_malloc(tmplen); - memcpy(tmp,s2k_specifier,8); - memcpy(tmp+8,secret,secret_len); - secret_len += 8; - while (count) { - if (count >= secret_len) { - crypto_digest_add_bytes(d, tmp, secret_len); - count -= secret_len; - } else { - crypto_digest_add_bytes(d, tmp, count); - count = 0; - } - } - crypto_digest_get_digest(d, key_out, key_out_len); - memwipe(tmp, 0, tmplen); - tor_free(tmp); - crypto_digest_free(d); -} - -/** - * Destroy the sz bytes of data stored at mem, setting them to - * the value byte. - * - * This function is preferable to memset, since many compilers will happily - * optimize out memset() when they can convince themselves that the data being - * cleared will never be read. - * - * Right now, our convention is to use this function when we are wiping data - * that's about to become inaccessible, such as stack buffers that are about - * to go out of scope or structures that are about to get freed. (In - * practice, it appears that the compilers we're currently using will optimize - * out the memset()s for stack-allocated buffers, but not those for - * about-to-be-freed structures. That could change, though, so we're being - * wary.) If there are live reads for the data, then you can just use - * memset(). - */ -void -memwipe(void *mem, uint8_t byte, size_t sz) -{ - /* Because whole-program-optimization exists, we may not be able to just - * have this function call "memset". A smart compiler could inline it, then - * eliminate dead memsets, and declare itself to be clever. */ - - /* This is a slow and ugly function from OpenSSL that fills 'mem' with junk - * based on the pointer value, then uses that junk to update a global - * variable. It's an elaborate ruse to trick the compiler into not - * optimizing out the "wipe this memory" code. Read it if you like zany - * programming tricks! In later versions of Tor, we should look for better - * not-optimized-out memory wiping stuff. */ - OPENSSL_cleanse(mem, sz); - /* Just in case some caller of memwipe() is relying on getting a buffer - * filled with a particular value, fill the buffer. - * - * If this function gets inlined, this memset might get eliminated, but - * that's okay: We only care about this particular memset in the case where - * the caller should have been using memset(), and the memset() wouldn't get - * eliminated. In other words, this is here so that we won't break anything - * if somebody accidentally calls memwipe() instead of memset(). - **/ - memset(mem, byte, sz); -} - -#ifdef TOR_IS_MULTITHREADED - -#ifndef OPENSSL_THREADS -#error OpenSSL has been built without thread support. Tor requires an \ - OpenSSL library with thread support enabled. -#endif - -/** Helper: OpenSSL uses this callback to manipulate mutexes. */ -static void -openssl_locking_cb_(int mode, int n, const char *file, int line) -{ - (void)file; - (void)line; - if (!openssl_mutexes_) - /* This is not a really good fix for the - * "release-freed-lock-from-separate-thread-on-shutdown" problem, but - * it can't hurt. */ - return; - if (mode & CRYPTO_LOCK) - tor_mutex_acquire(openssl_mutexes_[n]); - else - tor_mutex_release(openssl_mutexes_[n]); -} - -/** OpenSSL helper type: wraps a Tor mutex so that OpenSSL can use it - * as a lock. */ -struct CRYPTO_dynlock_value { - tor_mutex_t *lock; -}; - -/** OpenSSL callback function to allocate a lock: see CRYPTO_set_dynlock_* - * documentation in OpenSSL's docs for more info. */ -static struct CRYPTO_dynlock_value * -openssl_dynlock_create_cb_(const char *file, int line) -{ - struct CRYPTO_dynlock_value *v; - (void)file; - (void)line; - v = tor_malloc(sizeof(struct CRYPTO_dynlock_value)); - v->lock = tor_mutex_new(); - return v; -} - -/** OpenSSL callback function to acquire or release a lock: see - * CRYPTO_set_dynlock_* documentation in OpenSSL's docs for more info. */ -static void -openssl_dynlock_lock_cb_(int mode, struct CRYPTO_dynlock_value *v, - const char *file, int line) -{ - (void)file; - (void)line; - if (mode & CRYPTO_LOCK) - tor_mutex_acquire(v->lock); - else - tor_mutex_release(v->lock); -} - -/** OpenSSL callback function to free a lock: see CRYPTO_set_dynlock_* - * documentation in OpenSSL's docs for more info. */ -static void -openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v, - const char *file, int line) -{ - (void)file; - (void)line; - tor_mutex_free(v->lock); - tor_free(v); -} - -/** @{ */ -/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being - * multithreaded. */ -static int -setup_openssl_threading(void) -{ - int i; - int n = CRYPTO_num_locks(); - n_openssl_mutexes_ = n; - openssl_mutexes_ = tor_malloc(n*sizeof(tor_mutex_t *)); - for (i=0; i < n; ++i) - openssl_mutexes_[i] = tor_mutex_new(); - CRYPTO_set_locking_callback(openssl_locking_cb_); - CRYPTO_set_id_callback(tor_get_thread_id); - CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_); - CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_); - CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_); - return 0; -} -#else -static int -setup_openssl_threading(void) -{ - return 0; -} -#endif - -/** Uninitialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_global_cleanup(void) -{ - EVP_cleanup(); - ERR_remove_state(0); - ERR_free_strings(); - - if (dh_param_p) - BN_clear_free(dh_param_p); - if (dh_param_p_tls) - BN_clear_free(dh_param_p_tls); - if (dh_param_g) - BN_clear_free(dh_param_g); - -#ifndef DISABLE_ENGINES - ENGINE_cleanup(); -#endif - - CONF_modules_unload(1); - CRYPTO_cleanup_all_ex_data(); -#ifdef TOR_IS_MULTITHREADED - if (n_openssl_mutexes_) { - int n = n_openssl_mutexes_; - tor_mutex_t **ms = openssl_mutexes_; - int i; - openssl_mutexes_ = NULL; - n_openssl_mutexes_ = 0; - for (i=0;i -#include "torint.h" -#include "testsupport.h" - -/* - Macro to create an arbitrary OpenSSL version number as used by - OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard - to read. - - Don't use this directly, instead use one of the other OPENSSL_V macros - below. - - The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit - status. - */ -#define OPENSSL_VER(a,b,c,d,e) \ - (((a)<<28) | \ - ((b)<<20) | \ - ((c)<<12) | \ - ((d)<< 4) | \ - (e)) -/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the - * version for the released version of 0.9.8j */ -#define OPENSSL_V(a,b,c,d) \ - OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf) -/** An openssl release number for the first release in the series. For - * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL - * 1.0.0. */ -#define OPENSSL_V_NOPATCH(a,b,c) \ - OPENSSL_VER((a),(b),(c),0,0xf) -/** The first version that would occur for any alpha or beta in an openssl - * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released - * 0.9.7, and less than any released 0.9.8. */ -#define OPENSSL_V_SERIES(a,b,c) \ - OPENSSL_VER((a),(b),(c),0,0) - -/** Length of the output of our message digest. */ -#define DIGEST_LEN 20 -/** Length of the output of our second (improved) message digests. (For now - * this is just sha256, but it could be any other 256-bit digest.) */ -#define DIGEST256_LEN 32 -/** Length of our symmetric cipher's keys. */ -#define CIPHER_KEY_LEN 16 -/** Length of our symmetric cipher's IV. */ -#define CIPHER_IV_LEN 16 -/** Length of our public keys. */ -#define PK_BYTES (1024/8) -/** Length of our DH keys. */ -#define DH_BYTES (1024/8) - -/** Length of a sha1 message digest when encoded in base64 with trailing = - * signs removed. */ -#define BASE64_DIGEST_LEN 27 -/** Length of a sha256 message digest when encoded in base64 with trailing = - * signs removed. */ -#define BASE64_DIGEST256_LEN 43 - -/** Constant used to indicate OAEP padding for public-key encryption */ -#define PK_PKCS1_OAEP_PADDING 60002 - -/** Number of bytes added for PKCS1-OAEP padding. */ -#define PKCS1_OAEP_PADDING_OVERHEAD 42 - -/** Length of encoded public key fingerprints, including space; but not - * including terminating NUL. */ -#define FINGERPRINT_LEN 49 -/** Length of hex encoding of SHA1 digest, not including final NUL. */ -#define HEX_DIGEST_LEN 40 -/** Length of hex encoding of SHA256 digest, not including final NUL. */ -#define HEX_DIGEST256_LEN 64 - -typedef enum { - DIGEST_SHA1 = 0, - DIGEST_SHA256 = 1, -} digest_algorithm_t; -#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1) - -/** A set of all the digests we know how to compute, taken on a single - * string. Any digests that are shorter than 256 bits are right-padded - * with 0 bits. - * - * Note that this representation wastes 12 bytes for the SHA1 case, so - * don't use it for anything where we need to allocate a whole bunch at - * once. - **/ -typedef struct { - char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN]; -} digests_t; - -typedef struct crypto_pk_t crypto_pk_t; -typedef struct crypto_cipher_t crypto_cipher_t; -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); -const char * crypto_openssl_get_header_version_str(void); -int crypto_global_init(int hardwareAccel, - const char *accelName, - const char *accelPath); -void crypto_thread_cleanup(void); -int crypto_global_cleanup(void); - -/* environment setup */ -crypto_pk_t *crypto_pk_new(void); -void crypto_pk_free(crypto_pk_t *env); - -void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname); - -crypto_cipher_t *crypto_cipher_new(const char *key); -crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv); -void crypto_cipher_free(crypto_cipher_t *env); - -/* public key crypto */ -int crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits); -#define crypto_pk_generate_key(env) \ - crypto_pk_generate_key_with_bits((env), (PK_BYTES*8)) - -int crypto_pk_read_private_key_from_filename(crypto_pk_t *env, - const char *keyfile); -int crypto_pk_write_public_key_to_string(crypto_pk_t *env, - char **dest, size_t *len); -int crypto_pk_write_private_key_to_string(crypto_pk_t *env, - char **dest, size_t *len); -int crypto_pk_read_public_key_from_string(crypto_pk_t *env, - const char *src, size_t len); -int crypto_pk_read_private_key_from_string(crypto_pk_t *env, - const char *s, ssize_t len); -int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, - const char *fname); - -int crypto_pk_check_key(crypto_pk_t *env); -int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b); -int crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b); -size_t crypto_pk_keysize(crypto_pk_t *env); -int crypto_pk_num_bits(crypto_pk_t *env); -crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); -crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); -int crypto_pk_key_is_private(const crypto_pk_t *key); -int crypto_pk_public_exponent_ok(crypto_pk_t *env); - -int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, int padding); -int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure); -int crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen); -int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, - size_t datalen, const char *sig, size_t siglen); -int crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen); -int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen); -int crypto_pk_public_hybrid_encrypt(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen, - int padding, int force); -int crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen, - int padding, int warnOnFailure); - -int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len); -crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); -int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out); -int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out); -int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); - -/* symmetric crypto */ -const char *crypto_cipher_get_key(crypto_cipher_t *env); - -int crypto_cipher_encrypt(crypto_cipher_t *env, char *to, - const char *from, size_t fromlen); -int crypto_cipher_decrypt(crypto_cipher_t *env, char *to, - const char *from, size_t fromlen); -int crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *d, size_t len); - -int crypto_cipher_encrypt_with_iv(const char *key, - char *to, size_t tolen, - const char *from, size_t fromlen); -int crypto_cipher_decrypt_with_iv(const char *key, - char *to, size_t tolen, - const char *from, size_t fromlen); - -/* SHA-1 and other digests. */ -int crypto_digest(char *digest, const char *m, size_t len); -int crypto_digest256(char *digest, const char *m, size_t len, - digest_algorithm_t algorithm); -int crypto_digest_all(digests_t *ds_out, const char *m, size_t len); -struct smartlist_t; -void crypto_digest_smartlist(char *digest_out, size_t len_out, - const struct smartlist_t *lst, const char *append, - digest_algorithm_t alg); -const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg); -int crypto_digest_algorithm_parse_name(const char *name); -crypto_digest_t *crypto_digest_new(void); -crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm); -void crypto_digest_free(crypto_digest_t *digest); -void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, - size_t len); -void crypto_digest_get_digest(crypto_digest_t *digest, - char *out, size_t out_len); -crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest); -void crypto_digest_assign(crypto_digest_t *into, - const crypto_digest_t *from); -void crypto_hmac_sha256(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len); - -/* Key negotiation */ -#define DH_TYPE_CIRCUIT 1 -#define DH_TYPE_REND 2 -#define DH_TYPE_TLS 3 -crypto_dh_t *crypto_dh_new(int dh_type); -crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh); -int crypto_dh_get_bytes(crypto_dh_t *dh); -int crypto_dh_generate_public(crypto_dh_t *dh); -int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, - size_t pubkey_out_len); -ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, - const char *pubkey, size_t pubkey_len, - char *secret_out, size_t secret_out_len); -void crypto_dh_free(crypto_dh_t *dh); - -int crypto_expand_key_material_TAP(const uint8_t *key_in, - size_t key_in_len, - uint8_t *key_out, size_t key_out_len); -int crypto_expand_key_material_rfc5869_sha256( - const uint8_t *key_in, size_t key_in_len, - const uint8_t *salt_in, size_t salt_in_len, - const uint8_t *info_in, size_t info_in_len, - uint8_t *key_out, size_t key_out_len); - -/* random numbers */ -int crypto_seed_rng(int startup); -MOCK_DECL(int,crypto_rand,(char *to, size_t n)); -int crypto_strongest_rand(uint8_t *out, size_t out_len); -int crypto_rand_int(unsigned int max); -uint64_t crypto_rand_uint64(uint64_t max); -double crypto_rand_double(void); -struct tor_weak_rng_t; -void crypto_seed_weak_rng(struct tor_weak_rng_t *rng); - -char *crypto_random_hostname(int min_rand_len, int max_rand_len, - const char *prefix, const char *suffix); - -struct smartlist_t; -void *smartlist_choose(const struct smartlist_t *sl); -void smartlist_shuffle(struct smartlist_t *sl); - -int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen); -int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen); -/** Characters that can appear (case-insensitively) in a base32 encoding. */ -#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" -void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen); -int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen); - -int digest_to_base64(char *d64, const char *digest); -int digest_from_base64(char *digest, const char *d64); -int digest256_to_base64(char *d64, const char *digest); -int digest256_from_base64(char *digest, const char *d64); - -/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the - * 9th describes how much iteration to do. */ -#define S2K_SPECIFIER_LEN 9 -void secret_to_key(char *key_out, size_t key_out_len, const char *secret, - size_t secret_len, const char *s2k_specifier); - -/** OpenSSL-based utility functions. */ -void memwipe(void *mem, uint8_t byte, size_t sz); - -/* Prototypes for private functions only used by tortls.c, crypto.c, and the - * unit tests. */ -struct rsa_st; -struct evp_pkey_st; -struct dh_st; -struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env); -crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); -struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env, - int private); -struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); - -void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); - -#endif - diff --git a/src/tor/crypto_curve25519.c b/src/tor/crypto_curve25519.c deleted file mode 100644 index 8fe1e1b..0000000 --- a/src/tor/crypto_curve25519.c +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/* Wrapper code for a curve25519 implementation. */ - -#define CRYPTO_CURVE25519_PRIVATE -#include "orconfig.h" -#ifdef HAVE_SYS_STAT_H -#include -#endif -#include "crypto.h" -#include "crypto_curve25519.h" -#include "tor_util.h" -#include "torlog.h" - -/* ============================== - Part 1: wrap a suitable curve25519 implementation as curve25519_impl - ============================== */ - -#ifdef USE_CURVE25519_DONNA -int curve25519_donna(uint8_t *mypublic, - const uint8_t *secret, const uint8_t *basepoint); -#endif -#ifdef USE_CURVE25519_NACL -#ifdef HAVE_CRYPTO_SCALARMULT_CURVE25519_H -#include -#elif defined(HAVE_NACL_CRYPTO_SCALARMULT_CURVE25519_H) -#include -#endif -#endif - -STATIC int -curve25519_impl(uint8_t *output, const uint8_t *secret, - const uint8_t *basepoint) -{ - uint8_t bp[CURVE25519_PUBKEY_LEN]; - int r; - memcpy(bp, basepoint, CURVE25519_PUBKEY_LEN); - /* Clear the high bit, in case our backend foolishly looks at it. */ - bp[31] &= 0x7f; -#ifdef USE_CURVE25519_DONNA - r = curve25519_donna(output, secret, bp); -#elif defined(USE_CURVE25519_NACL) - r = crypto_scalarmult_curve25519(output, secret, bp); -#else -#error "No implementation of curve25519 is available." -#endif - memwipe(bp, 0, sizeof(bp)); - return r; -} - -/* ============================== - Part 2: Wrap curve25519_impl with some convenience types and functions. - ============================== */ - -/** - * Return true iff a curve25519_public_key_t seems valid. (It's not necessary - * to see if the point is on the curve, since the twist is also secure, but we - * do need to make sure that it isn't the point at infinity.) */ -int -curve25519_public_key_is_ok(const curve25519_public_key_t *key) -{ - return !safe_mem_is_zero(key->public_key, CURVE25519_PUBKEY_LEN); -} - -/** Generate a new keypair and return the secret key. If extra_strong - * is true, this key is possibly going to get used more than once, so - * use a better-than-usual RNG. Return 0 on success, -1 on failure. */ -int -curve25519_secret_key_generate(curve25519_secret_key_t *key_out, - int extra_strong) -{ - uint8_t k_tmp[CURVE25519_SECKEY_LEN]; - - if (crypto_rand((char*)key_out->secret_key, CURVE25519_SECKEY_LEN) < 0) - return -1; - if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) { - /* If they asked for extra-strong entropy and we have some, use it as an - * HMAC key to improve not-so-good entropy rather than using it directly, - * just in case the extra-strong entropy is less amazing than we hoped. */ - crypto_hmac_sha256((char *)key_out->secret_key, - (const char *)k_tmp, sizeof(k_tmp), - (const char *)key_out->secret_key, CURVE25519_SECKEY_LEN); - } - memwipe(k_tmp, 0, sizeof(k_tmp)); - key_out->secret_key[0] &= 248; - key_out->secret_key[31] &= 127; - key_out->secret_key[31] |= 64; - - return 0; -} - -void -curve25519_public_key_generate(curve25519_public_key_t *key_out, - const curve25519_secret_key_t *seckey) -{ - static const uint8_t basepoint[32] = {9}; - - curve25519_impl(key_out->public_key, seckey->secret_key, basepoint); -} - -int -curve25519_keypair_generate(curve25519_keypair_t *keypair_out, - int extra_strong) -{ - if (curve25519_secret_key_generate(&keypair_out->seckey, extra_strong) < 0) - return -1; - curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); - return 0; -} - -int -curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair, - const char *fname, - const char *tag) -{ - char contents[32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN]; - int r; - - memset(contents, 0, sizeof(contents)); - tor_snprintf(contents, sizeof(contents), "== c25519v1: %s ==", tag); - tor_assert(strlen(contents) <= 32); - memcpy(contents+32, keypair->seckey.secret_key, CURVE25519_SECKEY_LEN); - memcpy(contents+32+CURVE25519_SECKEY_LEN, - keypair->pubkey.public_key, CURVE25519_PUBKEY_LEN); - - r = write_bytes_to_file(fname, contents, sizeof(contents), 1); - - memwipe(contents, 0, sizeof(contents)); - return r; -} - -int -curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, - char **tag_out, - const char *fname) -{ - char prefix[33]; - char *content; - struct stat st; - int r = -1; - - *tag_out = NULL; - - st.st_size = 0; - content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); - if (! content) - goto end; - if (st.st_size != 32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN) - goto end; - - memcpy(prefix, content, 32); - prefix[32] = '\0'; - if (strcmpstart(prefix, "== c25519v1: ") || - strcmpend(prefix, " ==")) - goto end; - - *tag_out = tor_strndup(prefix+strlen("== c25519v1: "), - strlen(prefix) - strlen("== c25519v1: ==")); - - memcpy(keypair_out->seckey.secret_key, content+32, CURVE25519_SECKEY_LEN); - curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey); - if (tor_memneq(keypair_out->pubkey.public_key, - content + 32 + CURVE25519_SECKEY_LEN, - CURVE25519_PUBKEY_LEN)) - goto end; - - r = 0; - - end: - if (content) { - memwipe(content, 0, (size_t) st.st_size); - tor_free(content); - } - if (r != 0) { - memset(keypair_out, 0, sizeof(*keypair_out)); - tor_free(*tag_out); - } - return r; -} - -/** Perform the curve25519 ECDH handshake with skey and pkey, - * writing CURVE25519_OUTPUT_LEN bytes of output into output. */ -void -curve25519_handshake(uint8_t *output, - const curve25519_secret_key_t *skey, - const curve25519_public_key_t *pkey) -{ - curve25519_impl(output, skey->secret_key, pkey->public_key); -} - diff --git a/src/tor/crypto_curve25519.h b/src/tor/crypto_curve25519.h deleted file mode 100644 index 57018ac..0000000 --- a/src/tor/crypto_curve25519.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_CRYPTO_CURVE25519_H -#define TOR_CRYPTO_CURVE25519_H - -#include "testsupport.h" -#include "torint.h" - -/** Length of a curve25519 public key when encoded. */ -#define CURVE25519_PUBKEY_LEN 32 -/** Length of a curve25519 secret key when encoded. */ -#define CURVE25519_SECKEY_LEN 32 -/** Length of the result of a curve25519 handshake. */ -#define CURVE25519_OUTPUT_LEN 32 - -/** Wrapper type for a curve25519 public key */ -typedef struct curve25519_public_key_t { - uint8_t public_key[CURVE25519_PUBKEY_LEN]; -} curve25519_public_key_t; - -/** Wrapper type for a curve25519 secret key */ -typedef struct curve25519_secret_key_t { - uint8_t secret_key[CURVE25519_SECKEY_LEN]; -} curve25519_secret_key_t; - -/** A paired public and private key for curve25519. **/ -typedef struct curve25519_keypair_t { - curve25519_public_key_t pubkey; - curve25519_secret_key_t seckey; -} curve25519_keypair_t; - -#ifdef CURVE25519_ENABLED -/* These functions require that we actually know how to use curve25519 keys. - * The other data structures and functions in this header let us parse them, - * store them, and move them around. - */ - -int curve25519_public_key_is_ok(const curve25519_public_key_t *); - -int curve25519_secret_key_generate(curve25519_secret_key_t *key_out, - int extra_strong); -void curve25519_public_key_generate(curve25519_public_key_t *key_out, - const curve25519_secret_key_t *seckey); -int curve25519_keypair_generate(curve25519_keypair_t *keypair_out, - int extra_strong); - -void curve25519_handshake(uint8_t *output, - const curve25519_secret_key_t *, - const curve25519_public_key_t *); - -int curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair, - const char *fname, - const char *tag); - -int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, - char **tag_out, - const char *fname); - -#ifdef CRYPTO_CURVE25519_PRIVATE -STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, - const uint8_t *basepoint); -#endif -#endif - -#define CURVE25519_BASE64_PADDED_LEN 44 - -int curve25519_public_from_base64(curve25519_public_key_t *pkey, - const char *input); -int curve25519_public_to_base64(char *output, - const curve25519_public_key_t *pkey); - -#endif - diff --git a/src/tor/crypto_format.c b/src/tor/crypto_format.c deleted file mode 100644 index acb3463..0000000 --- a/src/tor/crypto_format.c +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/* Formatting and parsing code for crypto-related data structures. */ - -#include "orconfig.h" -#ifdef HAVE_SYS_STAT_H -#include -#endif -#include "crypto.h" -#include "crypto_curve25519.h" -#include "tor_util.h" -#include "torlog.h" - -int -curve25519_public_to_base64(char *output, - const curve25519_public_key_t *pkey) -{ - char buf[128]; - base64_encode(buf, sizeof(buf), - (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN); - buf[CURVE25519_BASE64_PADDED_LEN] = '\0'; - memcpy(output, buf, CURVE25519_BASE64_PADDED_LEN+1); - return 0; -} - -int -curve25519_public_from_base64(curve25519_public_key_t *pkey, - const char *input) -{ - size_t len = strlen(input); - if (len == CURVE25519_BASE64_PADDED_LEN - 1) { - /* not padded */ - return digest256_from_base64((char*)pkey->public_key, input); - } else if (len == CURVE25519_BASE64_PADDED_LEN) { - char buf[128]; - if (base64_decode(buf, sizeof(buf), input, len) != CURVE25519_PUBKEY_LEN) - return -1; - memcpy(pkey->public_key, buf, CURVE25519_PUBKEY_LEN); - return 0; - } else { - return -1; - } -} - diff --git a/src/tor/curve25519-donna.c b/src/tor/curve25519-donna.c deleted file mode 100644 index 5c6821c..0000000 --- a/src/tor/curve25519-donna.c +++ /dev/null @@ -1,732 +0,0 @@ -/* Copyright 2008, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * curve25519-donna: Curve25519 elliptic curve, public key function - * - * http://code.google.com/p/curve25519-donna/ - * - * Adam Langley - * - * Derived from public domain C code by Daniel J. Bernstein - * - * More information about curve25519 can be found here - * http://cr.yp.to/ecdh.html - * - * djb's sample implementation of curve25519 is written in a special assembly - * language called qhasm and uses the floating point registers. - * - * This is, almost, a clean room reimplementation from the curve25519 paper. It - * uses many of the tricks described therein. Only the crecip function is taken - * from the sample implementation. - */ - -#include "orconfig.h" - -#include -#include "torint.h" - -typedef uint8_t u8; -typedef int32_t s32; -typedef int64_t limb; - -/* Field element representation: - * - * Field elements are written as an array of signed, 64-bit limbs, least - * significant first. The value of the field element is: - * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... - * - * i.e. the limbs are 26, 25, 26, 25, ... bits wide. - */ - -/* Sum two numbers: output += in */ -static void fsum(limb *output, const limb *in) { - unsigned i; - for (i = 0; i < 10; i += 2) { - output[0+i] = (output[0+i] + in[0+i]); - output[1+i] = (output[1+i] + in[1+i]); - } -} - -/* Find the difference of two numbers: output = in - output - * (note the order of the arguments!) - */ -static void fdifference(limb *output, const limb *in) { - unsigned i; - for (i = 0; i < 10; ++i) { - output[i] = (in[i] - output[i]); - } -} - -/* Multiply a number by a scalar: output = in * scalar */ -static void fscalar_product(limb *output, const limb *in, const limb scalar) { - unsigned i; - for (i = 0; i < 10; ++i) { - output[i] = in[i] * scalar; - } -} - -/* Multiply two numbers: output = in2 * in - * - * output must be distinct to both inputs. The inputs are reduced coefficient - * form, the output is not. - */ -static void fproduct(limb *output, const limb *in2, const limb *in) { - output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); - output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + - ((limb) ((s32) in2[1])) * ((s32) in[0]); - output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + - ((limb) ((s32) in2[0])) * ((s32) in[2]) + - ((limb) ((s32) in2[2])) * ((s32) in[0]); - output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + - ((limb) ((s32) in2[2])) * ((s32) in[1]) + - ((limb) ((s32) in2[0])) * ((s32) in[3]) + - ((limb) ((s32) in2[3])) * ((s32) in[0]); - output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + - 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + - ((limb) ((s32) in2[3])) * ((s32) in[1])) + - ((limb) ((s32) in2[0])) * ((s32) in[4]) + - ((limb) ((s32) in2[4])) * ((s32) in[0]); - output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + - ((limb) ((s32) in2[3])) * ((s32) in[2]) + - ((limb) ((s32) in2[1])) * ((s32) in[4]) + - ((limb) ((s32) in2[4])) * ((s32) in[1]) + - ((limb) ((s32) in2[0])) * ((s32) in[5]) + - ((limb) ((s32) in2[5])) * ((s32) in[0]); - output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + - ((limb) ((s32) in2[1])) * ((s32) in[5]) + - ((limb) ((s32) in2[5])) * ((s32) in[1])) + - ((limb) ((s32) in2[2])) * ((s32) in[4]) + - ((limb) ((s32) in2[4])) * ((s32) in[2]) + - ((limb) ((s32) in2[0])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[0]); - output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + - ((limb) ((s32) in2[4])) * ((s32) in[3]) + - ((limb) ((s32) in2[2])) * ((s32) in[5]) + - ((limb) ((s32) in2[5])) * ((s32) in[2]) + - ((limb) ((s32) in2[1])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[1]) + - ((limb) ((s32) in2[0])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[0]); - output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + - 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + - ((limb) ((s32) in2[5])) * ((s32) in[3]) + - ((limb) ((s32) in2[1])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[1])) + - ((limb) ((s32) in2[2])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[2]) + - ((limb) ((s32) in2[0])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[0]); - output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + - ((limb) ((s32) in2[5])) * ((s32) in[4]) + - ((limb) ((s32) in2[3])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[3]) + - ((limb) ((s32) in2[2])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[2]) + - ((limb) ((s32) in2[1])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[1]) + - ((limb) ((s32) in2[0])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[0]); - output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + - ((limb) ((s32) in2[3])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[3]) + - ((limb) ((s32) in2[1])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[1])) + - ((limb) ((s32) in2[4])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[4]) + - ((limb) ((s32) in2[2])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[2]); - output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + - ((limb) ((s32) in2[6])) * ((s32) in[5]) + - ((limb) ((s32) in2[4])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[4]) + - ((limb) ((s32) in2[3])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[3]) + - ((limb) ((s32) in2[2])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[2]); - output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + - 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[5]) + - ((limb) ((s32) in2[3])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[3])) + - ((limb) ((s32) in2[4])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[4]); - output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + - ((limb) ((s32) in2[7])) * ((s32) in[6]) + - ((limb) ((s32) in2[5])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[5]) + - ((limb) ((s32) in2[4])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[4]); - output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + - ((limb) ((s32) in2[5])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[5])) + - ((limb) ((s32) in2[6])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[6]); - output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + - ((limb) ((s32) in2[8])) * ((s32) in[7]) + - ((limb) ((s32) in2[6])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[6]); - output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + - 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[7])); - output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + - ((limb) ((s32) in2[9])) * ((s32) in[8]); - output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); -} - -/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */ -static void freduce_degree(limb *output) { - /* Each of these shifts and adds ends up multiplying the value by 19. */ - output[8] += output[18] << 4; - output[8] += output[18] << 1; - output[8] += output[18]; - output[7] += output[17] << 4; - output[7] += output[17] << 1; - output[7] += output[17]; - output[6] += output[16] << 4; - output[6] += output[16] << 1; - output[6] += output[16]; - output[5] += output[15] << 4; - output[5] += output[15] << 1; - output[5] += output[15]; - output[4] += output[14] << 4; - output[4] += output[14] << 1; - output[4] += output[14]; - output[3] += output[13] << 4; - output[3] += output[13] << 1; - output[3] += output[13]; - output[2] += output[12] << 4; - output[2] += output[12] << 1; - output[2] += output[12]; - output[1] += output[11] << 4; - output[1] += output[11] << 1; - output[1] += output[11]; - output[0] += output[10] << 4; - output[0] += output[10] << 1; - output[0] += output[10]; -} - -#if (-1 & 3) != 3 -#error "This code only works on a two's complement system" -#endif - -/* return v / 2^26, using only shifts and adds. */ -static inline limb -div_by_2_26(const limb v) -{ - /* High word of v; no shift needed*/ - const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); - /* Set to all 1s if v was negative; else set to 0s. */ - const int32_t sign = ((int32_t) highword) >> 31; - /* Set to 0x3ffffff if v was negative; else set to 0. */ - const int32_t roundoff = ((uint32_t) sign) >> 6; - /* Should return v / (1<<26) */ - return (v + roundoff) >> 26; -} - -/* return v / (2^25), using only shifts and adds. */ -static inline limb -div_by_2_25(const limb v) -{ - /* High word of v; no shift needed*/ - const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); - /* Set to all 1s if v was negative; else set to 0s. */ - const int32_t sign = ((int32_t) highword) >> 31; - /* Set to 0x1ffffff if v was negative; else set to 0. */ - const int32_t roundoff = ((uint32_t) sign) >> 7; - /* Should return v / (1<<25) */ - return (v + roundoff) >> 25; -} - -static inline s32 -div_s32_by_2_25(const s32 v) -{ - const s32 roundoff = ((uint32_t)(v >> 31)) >> 7; - return (v + roundoff) >> 25; -} - -/* Reduce all coefficients of the short form input so that |x| < 2^26. - * - * On entry: |output[i]| < 2^62 - */ -static void freduce_coefficients(limb *output) { - unsigned i; - - output[10] = 0; - - for (i = 0; i < 10; i += 2) { - limb over = div_by_2_26(output[i]); - output[i] -= over << 26; - output[i+1] += over; - - over = div_by_2_25(output[i+1]); - output[i+1] -= over << 25; - output[i+2] += over; - } - /* Now |output[10]| < 2 ^ 38 and all other coefficients are reduced. */ - output[0] += output[10] << 4; - output[0] += output[10] << 1; - output[0] += output[10]; - - output[10] = 0; - - /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19 * 2^38 - * So |over| will be no more than 77825 */ - { - limb over = div_by_2_26(output[0]); - output[0] -= over << 26; - output[1] += over; - } - - /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 77825 - * So |over| will be no more than 1. */ - { - /* output[1] fits in 32 bits, so we can use div_s32_by_2_25 here. */ - s32 over32 = div_s32_by_2_25((s32) output[1]); - output[1] -= over32 << 25; - output[2] += over32; - } - - /* Finally, output[0,1,3..9] are reduced, and output[2] is "nearly reduced": - * we have |output[2]| <= 2^26. This is good enough for all of our math, - * but it will require an extra freduce_coefficients before fcontract. */ -} - -/* A helpful wrapper around fproduct: output = in * in2. - * - * output must be distinct to both inputs. The output is reduced degree and - * reduced coefficient. - */ -static void -fmul(limb *output, const limb *in, const limb *in2) { - limb t[19]; - fproduct(t, in, in2); - freduce_degree(t); - freduce_coefficients(t); - memcpy(output, t, sizeof(limb) * 10); -} - -static void fsquare_inner(limb *output, const limb *in) { - output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); - output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); - output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + - ((limb) ((s32) in[0])) * ((s32) in[2])); - output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + - ((limb) ((s32) in[0])) * ((s32) in[3])); - output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + - 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + - 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); - output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + - ((limb) ((s32) in[1])) * ((s32) in[4]) + - ((limb) ((s32) in[0])) * ((s32) in[5])); - output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + - ((limb) ((s32) in[2])) * ((s32) in[4]) + - ((limb) ((s32) in[0])) * ((s32) in[6]) + - 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); - output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + - ((limb) ((s32) in[2])) * ((s32) in[5]) + - ((limb) ((s32) in[1])) * ((s32) in[6]) + - ((limb) ((s32) in[0])) * ((s32) in[7])); - output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + - 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + - ((limb) ((s32) in[0])) * ((s32) in[8]) + - 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + - ((limb) ((s32) in[3])) * ((s32) in[5]))); - output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + - ((limb) ((s32) in[3])) * ((s32) in[6]) + - ((limb) ((s32) in[2])) * ((s32) in[7]) + - ((limb) ((s32) in[1])) * ((s32) in[8]) + - ((limb) ((s32) in[0])) * ((s32) in[9])); - output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + - ((limb) ((s32) in[4])) * ((s32) in[6]) + - ((limb) ((s32) in[2])) * ((s32) in[8]) + - 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + - ((limb) ((s32) in[1])) * ((s32) in[9]))); - output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + - ((limb) ((s32) in[4])) * ((s32) in[7]) + - ((limb) ((s32) in[3])) * ((s32) in[8]) + - ((limb) ((s32) in[2])) * ((s32) in[9])); - output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + - 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + - 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + - ((limb) ((s32) in[3])) * ((s32) in[9]))); - output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + - ((limb) ((s32) in[5])) * ((s32) in[8]) + - ((limb) ((s32) in[4])) * ((s32) in[9])); - output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + - ((limb) ((s32) in[6])) * ((s32) in[8]) + - 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); - output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + - ((limb) ((s32) in[6])) * ((s32) in[9])); - output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + - 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); - output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); - output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); -} - -static void -fsquare(limb *output, const limb *in) { - limb t[19]; - fsquare_inner(t, in); - freduce_degree(t); - freduce_coefficients(t); - memcpy(output, t, sizeof(limb) * 10); -} - -/* Take a little-endian, 32-byte number and expand it into polynomial form */ -static void -fexpand(limb *output, const u8 *input) { -#define F(n,start,shift,mask) \ - output[n] = ((((limb) input[start + 0]) | \ - ((limb) input[start + 1]) << 8 | \ - ((limb) input[start + 2]) << 16 | \ - ((limb) input[start + 3]) << 24) >> shift) & mask; - F(0, 0, 0, 0x3ffffff); - F(1, 3, 2, 0x1ffffff); - F(2, 6, 3, 0x3ffffff); - F(3, 9, 5, 0x1ffffff); - F(4, 12, 6, 0x3ffffff); - F(5, 16, 0, 0x1ffffff); - F(6, 19, 1, 0x3ffffff); - F(7, 22, 3, 0x1ffffff); - F(8, 25, 4, 0x3ffffff); - F(9, 28, 6, 0x1ffffff); -#undef F -} - -#if (-32 >> 1) != -16 -#error "This code only works when >> does sign-extension on negative numbers" -#endif - -/* Take a fully reduced polynomial form number and contract it into a - * little-endian, 32-byte array - */ -static void -fcontract(u8 *output, limb *input) { - int i; - int j; - - for (j = 0; j < 2; ++j) { - for (i = 0; i < 9; ++i) { - if ((i & 1) == 1) { - /* This calculation is a time-invariant way to make input[i] positive - by borrowing from the next-larger limb. - */ - const s32 mask = (s32)(input[i]) >> 31; - const s32 carry = -(((s32)(input[i]) & mask) >> 25); - input[i] = (s32)(input[i]) + (carry << 25); - input[i+1] = (s32)(input[i+1]) - carry; - } else { - const s32 mask = (s32)(input[i]) >> 31; - const s32 carry = -(((s32)(input[i]) & mask) >> 26); - input[i] = (s32)(input[i]) + (carry << 26); - input[i+1] = (s32)(input[i+1]) - carry; - } - } - { - const s32 mask = (s32)(input[9]) >> 31; - const s32 carry = -(((s32)(input[9]) & mask) >> 25); - input[9] = (s32)(input[9]) + (carry << 25); - input[0] = (s32)(input[0]) - (carry * 19); - } - } - - /* The first borrow-propagation pass above ended with every limb - except (possibly) input[0] non-negative. - - Since each input limb except input[0] is decreased by at most 1 - by a borrow-propagation pass, the second borrow-propagation pass - could only have wrapped around to decrease input[0] again if the - first pass left input[0] negative *and* input[1] through input[9] - were all zero. In that case, input[1] is now 2^25 - 1, and this - last borrow-propagation step will leave input[1] non-negative. - */ - { - const s32 mask = (s32)(input[0]) >> 31; - const s32 carry = -(((s32)(input[0]) & mask) >> 26); - input[0] = (s32)(input[0]) + (carry << 26); - input[1] = (s32)(input[1]) - carry; - } - - /* Both passes through the above loop, plus the last 0-to-1 step, are - necessary: if input[9] is -1 and input[0] through input[8] are 0, - negative values will remain in the array until the end. - */ - - input[1] <<= 2; - input[2] <<= 3; - input[3] <<= 5; - input[4] <<= 6; - input[6] <<= 1; - input[7] <<= 3; - input[8] <<= 4; - input[9] <<= 6; -#define F(i, s) \ - output[s+0] |= input[i] & 0xff; \ - output[s+1] = (input[i] >> 8) & 0xff; \ - output[s+2] = (input[i] >> 16) & 0xff; \ - output[s+3] = (input[i] >> 24) & 0xff; - output[0] = 0; - output[16] = 0; - F(0,0); - F(1,3); - F(2,6); - F(3,9); - F(4,12); - F(5,16); - F(6,19); - F(7,22); - F(8,25); - F(9,28); -#undef F -} - -/* Input: Q, Q', Q-Q' - * Output: 2Q, Q+Q' - * - * x2 z3: long form - * x3 z3: long form - * x z: short form, destroyed - * xprime zprime: short form, destroyed - * qmqp: short form, preserved - */ -static void fmonty(limb *x2, limb *z2, /* output 2Q */ - limb *x3, limb *z3, /* output Q + Q' */ - limb *x, limb *z, /* input Q */ - limb *xprime, limb *zprime, /* input Q' */ - const limb *qmqp /* input Q - Q' */) { - limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], - zzprime[19], zzzprime[19], xxxprime[19]; - - memcpy(origx, x, 10 * sizeof(limb)); - fsum(x, z); - fdifference(z, origx); // does x - z - - memcpy(origxprime, xprime, sizeof(limb) * 10); - fsum(xprime, zprime); - fdifference(zprime, origxprime); - fproduct(xxprime, xprime, z); - fproduct(zzprime, x, zprime); - freduce_degree(xxprime); - freduce_coefficients(xxprime); - freduce_degree(zzprime); - freduce_coefficients(zzprime); - memcpy(origxprime, xxprime, sizeof(limb) * 10); - fsum(xxprime, zzprime); - fdifference(zzprime, origxprime); - fsquare(xxxprime, xxprime); - fsquare(zzzprime, zzprime); - fproduct(zzprime, zzzprime, qmqp); - freduce_degree(zzprime); - freduce_coefficients(zzprime); - memcpy(x3, xxxprime, sizeof(limb) * 10); - memcpy(z3, zzprime, sizeof(limb) * 10); - - fsquare(xx, x); - fsquare(zz, z); - fproduct(x2, xx, zz); - freduce_degree(x2); - freduce_coefficients(x2); - fdifference(zz, xx); // does zz = xx - zz - memset(zzz + 10, 0, sizeof(limb) * 9); - fscalar_product(zzz, zz, 121665); - /* No need to call freduce_degree here: - fscalar_product doesn't increase the degree of its input. */ - freduce_coefficients(zzz); - fsum(zzz, xx); - fproduct(z2, zz, zzz); - freduce_degree(z2); - freduce_coefficients(z2); -} - -/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave - * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid - * side-channel attacks. - * - * NOTE that this function requires that 'iswap' be 1 or 0; other values give - * wrong results. Also, the two limb arrays must be in reduced-coefficient, - * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, - * and all all values in a[0..9],b[0..9] must have magnitude less than - * INT32_MAX. - */ -static void -swap_conditional(limb a[19], limb b[19], limb iswap) { - unsigned i; - const s32 swap = (s32) -iswap; - - for (i = 0; i < 10; ++i) { - const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); - a[i] = ((s32)a[i]) ^ x; - b[i] = ((s32)b[i]) ^ x; - } -} - -/* Calculates nQ where Q is the x-coordinate of a point on the curve - * - * resultx/resultz: the x coordinate of the resulting curve point (short form) - * n: a little endian, 32-byte number - * q: a point of the curve (short form) - */ -static void -cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { - limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; - limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; - limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; - limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; - - unsigned i, j; - - memcpy(nqpqx, q, sizeof(limb) * 10); - - for (i = 0; i < 32; ++i) { - u8 byte = n[31 - i]; - for (j = 0; j < 8; ++j) { - const limb bit = byte >> 7; - - swap_conditional(nqx, nqpqx, bit); - swap_conditional(nqz, nqpqz, bit); - fmonty(nqx2, nqz2, - nqpqx2, nqpqz2, - nqx, nqz, - nqpqx, nqpqz, - q); - swap_conditional(nqx2, nqpqx2, bit); - swap_conditional(nqz2, nqpqz2, bit); - - t = nqx; - nqx = nqx2; - nqx2 = t; - t = nqz; - nqz = nqz2; - nqz2 = t; - t = nqpqx; - nqpqx = nqpqx2; - nqpqx2 = t; - t = nqpqz; - nqpqz = nqpqz2; - nqpqz2 = t; - - byte <<= 1; - } - } - - memcpy(resultx, nqx, sizeof(limb) * 10); - memcpy(resultz, nqz, sizeof(limb) * 10); -} - -// ----------------------------------------------------------------------------- -// Shamelessly copied from djb's code -// ----------------------------------------------------------------------------- -static void -crecip(limb *out, const limb *z) { - limb z2[10]; - limb z9[10]; - limb z11[10]; - limb z2_5_0[10]; - limb z2_10_0[10]; - limb z2_20_0[10]; - limb z2_50_0[10]; - limb z2_100_0[10]; - limb t0[10]; - limb t1[10]; - int i; - - /* 2 */ fsquare(z2,z); - /* 4 */ fsquare(t1,z2); - /* 8 */ fsquare(t0,t1); - /* 9 */ fmul(z9,t0,z); - /* 11 */ fmul(z11,z9,z2); - /* 22 */ fsquare(t0,z11); - /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); - - /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); - /* 2^7 - 2^2 */ fsquare(t1,t0); - /* 2^8 - 2^3 */ fsquare(t0,t1); - /* 2^9 - 2^4 */ fsquare(t1,t0); - /* 2^10 - 2^5 */ fsquare(t0,t1); - /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); - - /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); - /* 2^12 - 2^2 */ fsquare(t1,t0); - /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } - /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); - - /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); - /* 2^22 - 2^2 */ fsquare(t1,t0); - /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } - /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); - - /* 2^41 - 2^1 */ fsquare(t1,t0); - /* 2^42 - 2^2 */ fsquare(t0,t1); - /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } - /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); - - /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); - /* 2^52 - 2^2 */ fsquare(t1,t0); - /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } - /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); - - /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); - /* 2^102 - 2^2 */ fsquare(t0,t1); - /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } - /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); - - /* 2^201 - 2^1 */ fsquare(t0,t1); - /* 2^202 - 2^2 */ fsquare(t1,t0); - /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } - /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); - - /* 2^251 - 2^1 */ fsquare(t1,t0); - /* 2^252 - 2^2 */ fsquare(t0,t1); - /* 2^253 - 2^3 */ fsquare(t1,t0); - /* 2^254 - 2^4 */ fsquare(t0,t1); - /* 2^255 - 2^5 */ fsquare(t1,t0); - /* 2^255 - 21 */ fmul(out,t1,z11); -} - -int curve25519_donna(u8 *, const u8 *, const u8 *); - -int -curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { - limb bp[10], x[10], z[11], zmone[10]; - uint8_t e[32]; - int i; - - for (i = 0; i < 32; ++i) e[i] = secret[i]; - e[0] &= 248; - e[31] &= 127; - e[31] |= 64; - - fexpand(bp, basepoint); - cmult(x, z, e, bp); - crecip(zmone, z); - fmul(z, x, zmone); - freduce_coefficients(z); - fcontract(mypublic, z); - return 0; -} diff --git a/src/tor/di_ops.c b/src/tor/di_ops.c deleted file mode 100644 index eeb7a16..0000000 --- a/src/tor/di_ops.c +++ /dev/null @@ -1,222 +0,0 @@ -/* Copyright (c) 2011-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file di_ops.c - * \brief Functions for data-independent operations. - **/ - -#include "orconfig.h" -#include "di_ops.h" -#include "torlog.h" -#include "tor_util.h" - -/** - * Timing-safe version of memcmp. As memcmp, compare the sz bytes at - * a with the sz bytes at b, and return less than 0 if - * the bytes at a lexically precede those at b, 0 if the byte - * ranges are equal, and greater than zero if the bytes at a lexically - * follow those at b. - * - * This implementation differs from memcmp in that its timing behavior is not - * data-dependent: it should return in the same amount of time regardless of - * the contents of a and b. - */ -int -tor_memcmp(const void *a, const void *b, size_t len) -{ - const uint8_t *x = a; - const uint8_t *y = b; - size_t i = len; - int retval = 0; - - /* This loop goes from the end of the arrays to the start. At the - * start of every iteration, before we decrement i, we have set - * "retval" equal to the result of memcmp(a+i,b+i,len-i). During the - * loop, we update retval by leaving it unchanged if x[i]==y[i] and - * setting it to x[i]-y[i] if x[i]!= y[i]. - * - * The following assumes we are on a system with two's-complement - * arithmetic. We check for this at configure-time with the check - * that sets USING_TWOS_COMPLEMENT. If we aren't two's complement, then - * torint.h will stop compilation with an error. - */ - while (i--) { - int v1 = x[i]; - int v2 = y[i]; - int equal_p = v1 ^ v2; - - /* The following sets bits 8 and above of equal_p to 'equal_p == - * 0', and thus to v1 == v2. (To see this, note that if v1 == - * v2, then v1^v2 == equal_p == 0, so equal_p-1 == -1, which is the - * same as ~0 on a two's-complement machine. Then note that if - * v1 != v2, then 0 < v1 ^ v2 < 256, so 0 <= equal_p - 1 < 255.) - */ - --equal_p; - - equal_p >>= 8; - /* Thanks to (sign-preserving) arithmetic shift, equal_p is now - * equal to -(v1 == v2), which is exactly what we need below. - * (Since we're assuming two's-complement arithmetic, -1 is the - * same as ~0 (all bits set).) - * - * (The result of an arithmetic shift on a negative value is - * actually implementation-defined in standard C. So how do we - * get away with assuming it? Easy. We check.) */ -#if ((-60 >> 8) != -1) -#error "According to cpp, right-shift doesn't perform sign-extension." -#endif -#ifndef RSHIFT_DOES_SIGN_EXTEND -#error "According to configure, right-shift doesn't perform sign-extension." -#endif - - /* If v1 == v2, equal_p is ~0, so this will leave retval - * unchanged; otherwise, equal_p is 0, so this will zero it. */ - retval &= equal_p; - - /* If v1 == v2, then this adds 0, and leaves retval unchanged. - * Otherwise, we just zeroed retval, so this sets it to v1 - v2. */ - retval += (v1 - v2); - - /* There. Now retval is equal to its previous value if v1 == v2, and - * equal to v1 - v2 if v1 != v2. */ - } - - return retval; -} - -/** - * Timing-safe memory comparison. Return true if the sz bytes at - * a are the same as the sz bytes at b, and 0 otherwise. - * - * This implementation differs from !memcmp(a,b,sz) in that its timing - * behavior is not data-dependent: it should return in the same amount of time - * regardless of the contents of a and b. It differs from - * !tor_memcmp(a,b,sz) by being faster. - */ -int -tor_memeq(const void *a, const void *b, size_t sz) -{ - /* Treat a and b as byte ranges. */ - const uint8_t *ba = a, *bb = b; - uint32_t any_difference = 0; - while (sz--) { - /* Set byte_diff to all of those bits that are different in *ba and *bb, - * and advance both ba and bb. */ - const uint8_t byte_diff = *ba++ ^ *bb++; - - /* Set bits in any_difference if they are set in byte_diff. */ - any_difference |= byte_diff; - } - - /* Now any_difference is 0 if there are no bits different between - * a and b, and is nonzero if there are bits different between a - * and b. Now for paranoia's sake, let's convert it to 0 or 1. - * - * (If we say "!any_difference", the compiler might get smart enough - * to optimize-out our data-independence stuff above.) - * - * To unpack: - * - * If any_difference == 0: - * any_difference - 1 == ~0 - * (any_difference - 1) >> 8 == 0x00ffffff - * 1 & ((any_difference - 1) >> 8) == 1 - * - * If any_difference != 0: - * 0 < any_difference < 256, so - * 0 <= any_difference - 1 < 255 - * (any_difference - 1) >> 8 == 0 - * 1 & ((any_difference - 1) >> 8) == 0 - */ - - return 1 & ((any_difference - 1) >> 8); -} - -/* Implement di_digest256_map_t as a linked list of entries. */ -struct di_digest256_map_t { - struct di_digest256_map_t *next; - uint8_t key[32]; - void *val; -}; - -/** Release all storage held in map, calling free_fn on each value - * as we go. */ -void -dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn) -{ - while (map) { - di_digest256_map_t *victim = map; - map = map->next; - if (free_fn) - free_fn(victim->val); - tor_free(victim); - } -} - -/** Adjust the map at *map, adding an entry for key -> - * val, where key is a DIGEST256_LEN-byte key. - * - * The caller MUST NOT add a key that already appears in the map. - */ -void -dimap_add_entry(di_digest256_map_t **map, - const uint8_t *key, void *val) -{ - di_digest256_map_t *new_ent; - { - void *old_val = dimap_search(*map, key, NULL); - tor_assert(! old_val); - tor_assert(val); - } - new_ent = tor_malloc_zero(sizeof(di_digest256_map_t)); - new_ent->next = *map; - memcpy(new_ent->key, key, 32); - new_ent->val = val; - *map = new_ent; -} - -/** Search the map at map for an entry whose key is key (a - * DIGEST256_LEN-byte key) returning the corresponding value if we found one, - * and returning dflt_val if the key wasn't found. - * - * This operation takes an amount of time dependent only on the length of - * map, not on the position or presence of key within map. - */ -void * -dimap_search(const di_digest256_map_t *map, const uint8_t *key, - void *dflt_val) -{ - uintptr_t result = (uintptr_t)dflt_val; - - while (map) { - uintptr_t r = (uintptr_t) tor_memeq(map->key, key, 32); - r -= 1; /* Now r is (uintptr_t)-1 if memeq returned false, and - * 0 if memeq returned true. */ - - result &= r; - result |= ((uintptr_t)(map->val)) & ~r; - - map = map->next; - } - - return (void *)result; -} - -/** - * Return true iff the sz bytes at mem are all zero. Runs in - * time independent of the contents of mem. - */ -int -safe_mem_is_zero(const void *mem, size_t sz) -{ - uint32_t total = 0; - const uint8_t *ptr = mem; - - while (sz--) { - total |= *ptr++; - } - - return 1 & ((total - 1) >> 8); -} - diff --git a/src/tor/di_ops.h b/src/tor/di_ops.h deleted file mode 100644 index d93534b..0000000 --- a/src/tor/di_ops.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file di_ops.h - * \brief Headers for di_ops.c - **/ - -#ifndef TOR_DI_OPS_H -#define TOR_DI_OPS_H - -#include "orconfig.h" -#include "torint.h" - -int tor_memcmp(const void *a, const void *b, size_t sz); -int tor_memeq(const void *a, const void *b, size_t sz); -#define tor_memneq(a,b,sz) (!tor_memeq((a),(b),(sz))) - -/** Alias for the platform's memcmp() function. This function is - * not data-independent: we define this alias so that we can - * mark cases where we are deliberately using a data-dependent memcmp() - * implementation. - */ -#define fast_memcmp(a,b,c) (memcmp((a),(b),(c))) -#define fast_memeq(a,b,c) (0==memcmp((a),(b),(c))) -#define fast_memneq(a,b,c) (0!=memcmp((a),(b),(c))) - -int safe_mem_is_zero(const void *mem, size_t sz); - -/** A type for a map from DIGEST256_LEN-byte blobs to void*, such that - * data lookups take an amount of time proportional only to the size - * of the map, and not to the position or presence of the item in the map. - * - * Not efficient for large maps! */ -typedef struct di_digest256_map_t di_digest256_map_t; -typedef void (*dimap_free_fn)(void *); - -void dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn); -void dimap_add_entry(di_digest256_map_t **map, - const uint8_t *key, void *val); -void *dimap_search(const di_digest256_map_t *map, const uint8_t *key, - void *dflt_val); - -#endif - diff --git a/src/tor/directory.c b/src/tor/directory.c deleted file mode 100644 index 4c4fd7b..0000000 --- a/src/tor/directory.c +++ /dev/null @@ -1,3894 +0,0 @@ -/* Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "buffers.h" -#include "circuitbuild.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "control.h" -#include "directory.h" -#include "dirserv.h" -#include "dirvote.h" -#include "entrynodes.h" -#include "geoip.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" -#include "routerset.h" - -#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) -#ifndef OPENBSD -#include -#endif -#endif - -/** - * \file directory.c - * \brief Code to send and fetch directories and router - * descriptors via HTTP. Directories use dirserv.c to generate the - * results; clients use routers.c to parse them. - **/ - -/* In-points to directory.c: - * - * - directory_post_to_dirservers(), called from - * router_upload_dir_desc_to_dirservers() in router.c - * upload_service_descriptor() in rendservice.c - * - directory_get_from_dirserver(), called from - * rend_client_refetch_renddesc() in rendclient.c - * run_scheduled_events() in onion_main.c - * do_hup() in onion_main.c - * - connection_dir_process_inbuf(), called from - * connection_process_inbuf() in connection.c - * - connection_dir_finished_flushing(), called from - * connection_finished_flushing() in connection.c - * - connection_dir_finished_connecting(), called from - * connection_finished_connecting() in connection.c - */ -static void directory_send_command(dir_connection_t *conn, - int purpose, int direct, const char *resource, - const char *payload, size_t payload_len, - 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); -static int purpose_needs_anonymity(uint8_t dir_purpose, - uint8_t router_purpose); -static char *http_get_header(const char *headers, const char *which); -static void http_set_address_origin(const char *headers, connection_t *conn); -static void connection_dir_download_routerdesc_failed(dir_connection_t *conn); -static void connection_dir_bridge_routerdesc_failed(dir_connection_t *conn); -static void connection_dir_download_cert_failed( - dir_connection_t *conn, int status_code); -static void connection_dir_retry_bridges(smartlist_t *descs); -static void dir_routerdesc_download_failed(smartlist_t *failed, - int status_code, - int router_purpose, - int was_extrainfo, - int was_descriptor_digests); -static void dir_microdesc_download_failed(smartlist_t *failed, - int status_code); -static void note_client_request(int purpose, int compressed, size_t bytes); -static int client_likes_consensus(networkstatus_t *v, const char *want_url); - -static void directory_initiate_command_rend(const char *address, - const tor_addr_t *addr, - uint16_t or_port, - uint16_t dir_port, - const char *digest, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - const rend_data_t *rend_query); - -/********* START VARIABLES **********/ - -/** How far in the future do we allow a directory server to tell us it is - * before deciding that one of us has the wrong time? */ -#define ALLOW_DIRECTORY_TIME_SKEW (30*60) - -#define X_ADDRESS_HEADER "X-Your-Address-Is: " - -/** HTTP cache control: how long do we tell proxies they can cache each - * kind of document we serve? */ -#define FULL_DIR_CACHE_LIFETIME (60*60) -#define RUNNINGROUTERS_CACHE_LIFETIME (20*60) -#define DIRPORTFRONTPAGE_CACHE_LIFETIME (20*60) -#define NETWORKSTATUS_CACHE_LIFETIME (5*60) -#define ROUTERDESC_CACHE_LIFETIME (30*60) -#define ROUTERDESC_BY_DIGEST_CACHE_LIFETIME (48*60*60) -#define ROBOTS_CACHE_LIFETIME (24*60*60) -#define MICRODESC_CACHE_LIFETIME (48*60*60) - -/********* END VARIABLES ************/ - -/** Return true iff the directory purpose dir_purpose (and if it's - * fetching descriptors, it's fetching them for router_purpose) - * must use an anonymous connection to a directory. */ -static int -purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose) -{ - if (get_options()->AllDirActionsPrivate) - return 1; - if (router_purpose == ROUTER_PURPOSE_BRIDGE) - return 1; /* if no circuits yet, this might break bootstrapping, but it's - * needed to be safe. */ - if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR || - dir_purpose == DIR_PURPOSE_UPLOAD_VOTE || - dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES || - dir_purpose == DIR_PURPOSE_FETCH_STATUS_VOTE || - dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES || - dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS || - dir_purpose == DIR_PURPOSE_FETCH_CERTIFICATE || - dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC || - dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO || - dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) - return 0; - return 1; -} - -/** Return a newly allocated string describing auth. Only describes - * authority features. */ -static char * -authdir_type_to_string(dirinfo_type_t auth) -{ - char *result; - smartlist_t *lst = smartlist_new(); - if (auth & V1_DIRINFO) - smartlist_add(lst, (void*)"V1"); - if (auth & V3_DIRINFO) - smartlist_add(lst, (void*)"V3"); - if (auth & BRIDGE_DIRINFO) - smartlist_add(lst, (void*)"Bridge"); - if (auth & HIDSERV_DIRINFO) - smartlist_add(lst, (void*)"Hidden service"); - if (smartlist_len(lst)) { - result = smartlist_join_strings(lst, ", ", 0, NULL); - } else { - result = tor_strdup("[Not an authority]"); - } - smartlist_free(lst); - return result; -} - -/** Return a string describing a given directory connection purpose. */ -static const char * -dir_conn_purpose_to_string(int purpose) -{ - switch (purpose) - { - case DIR_PURPOSE_FETCH_RENDDESC: - return "hidden-service descriptor fetch"; - case DIR_PURPOSE_UPLOAD_DIR: - return "server descriptor upload"; - case DIR_PURPOSE_UPLOAD_RENDDESC: - return "hidden-service descriptor upload"; - case DIR_PURPOSE_UPLOAD_VOTE: - return "server vote upload"; - case DIR_PURPOSE_UPLOAD_SIGNATURES: - return "consensus signature upload"; - case DIR_PURPOSE_FETCH_SERVERDESC: - return "server descriptor fetch"; - case DIR_PURPOSE_FETCH_EXTRAINFO: - return "extra-info fetch"; - case DIR_PURPOSE_FETCH_CONSENSUS: - return "consensus network-status fetch"; - case DIR_PURPOSE_FETCH_CERTIFICATE: - return "authority cert fetch"; - case DIR_PURPOSE_FETCH_STATUS_VOTE: - return "status vote fetch"; - case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: - return "consensus signature fetch"; - case DIR_PURPOSE_FETCH_RENDDESC_V2: - return "hidden-service v2 descriptor fetch"; - case DIR_PURPOSE_UPLOAD_RENDDESC_V2: - return "hidden-service v2 descriptor upload"; - case DIR_PURPOSE_FETCH_MICRODESC: - return "microdescriptor fetch"; - } - - log_warn(LD_BUG, "Called with unknown purpose %d", purpose); - return "(unknown)"; -} - -/** Return true iff identity_digest is the digest of a router we - * believe to support extrainfo downloads. (If is_authority we do - * additional checking that's only valid for authorities.) */ -int -router_supports_extrainfo(const char *identity_digest, int is_authority) -{ - const node_t *node = node_get_by_id(identity_digest); - - if (node && node->ri) { - if (node->ri->caches_extra_info) - return 1; - } - if (is_authority) { - return 1; - } - return 0; -} - -/** Return true iff any trusted directory authority has accepted our - * server descriptor. - * - * We consider any authority sufficient because waiting for all of - * them means it never happens while any authority is down; we don't - * go for something more complex in the middle (like \>1/3 or \>1/2 or - * \>=1/2) because that doesn't seem necessary yet. - */ -int -directories_have_accepted_server_descriptor(void) -{ - const smartlist_t *servers = router_get_trusted_dir_servers(); - const or_options_t *options = get_options(); - SMARTLIST_FOREACH(servers, dir_server_t *, d, { - if ((d->type & options->PublishServerDescriptor_) && - d->has_accepted_serverdesc) { - return 1; - } - }); - return 0; -} - -/** Start a connection to every suitable directory authority, using - * connection purpose dir_purpose and uploading payload - * (of length payload_len). The dir_purpose should be one of - * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'. - * - * router_purpose describes the type of descriptor we're - * publishing, if we're publishing a descriptor -- e.g. general or bridge. - * - * type specifies what sort of dir authorities (V1, V3, - * HIDSERV, BRIDGE, etc) we should upload to. - * - * If extrainfo_len is nonzero, the first payload_len bytes of - * payload hold a router descriptor, and the next extrainfo_len - * bytes of payload hold an extra-info document. Upload the descriptor - * to all authorities, and the extra-info document to all authorities that - * support it. - */ -void -directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, - dirinfo_type_t type, - const char *payload, - size_t payload_len, size_t extrainfo_len) -{ - const or_options_t *options = get_options(); - int post_via_tor; - const smartlist_t *dirservers = router_get_trusted_dir_servers(); - int found = 0; - const int exclude_self = (dir_purpose == DIR_PURPOSE_UPLOAD_VOTE || - dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES); - tor_assert(dirservers); - /* This tries dirservers which we believe to be down, but ultimately, that's - * harmless, and we may as well err on the side of getting things uploaded. - */ - SMARTLIST_FOREACH_BEGIN(dirservers, dir_server_t *, ds) { - routerstatus_t *rs = &(ds->fake_status); - size_t upload_len = payload_len; - tor_addr_t ds_addr; - - if ((type & ds->type) == 0) - continue; - - if (exclude_self && router_digest_is_me(ds->digest)) - continue; - - if (options->StrictNodes && - routerset_contains_routerstatus(options->ExcludeNodes, rs, -1)) { - log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but " - "it's in our ExcludedNodes list and StrictNodes is set. " - "Skipping.", - ds->nickname, - dir_conn_purpose_to_string(dir_purpose)); - continue; - } - - found = 1; /* at least one authority of this type was listed */ - if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR) - ds->has_accepted_serverdesc = 0; - - if (extrainfo_len && router_supports_extrainfo(ds->digest, 1)) { - upload_len += extrainfo_len; - log_info(LD_DIR, "Uploading an extrainfo too (length %d)", - (int) extrainfo_len); - } - tor_addr_from_ipv4h(&ds_addr, ds->addr); - post_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose) || - !fascist_firewall_allows_address_dir(&ds_addr, ds->dir_port); - directory_initiate_command_routerstatus(rs, dir_purpose, - router_purpose, - post_via_tor, - NULL, payload, upload_len, 0); - } SMARTLIST_FOREACH_END(ds); - if (!found) { - char *s = authdir_type_to_string(type); - log_warn(LD_DIR, "Publishing server descriptor to directory authorities " - "of type '%s', but no authorities of that type listed!", s); - tor_free(s); - } -} - -/** Return true iff, according to the values in options, we should be - * using directory guards for direct downloads of directory information. */ -static int -should_use_directory_guards(const or_options_t *options) -{ - /* Public (non-bridge) servers never use directory guards. */ - if (public_server_mode(options)) - return 0; - /* If guards are disabled, or directory guards are disabled, we can't - * use directory guards. - */ - if (!options->UseEntryGuards || !options->UseEntryGuardsAsDirGuards) - return 0; - /* If we're configured to fetch directory info aggressively or of a - * nonstandard type, don't use directory guards. */ - if (options->DownloadExtraInfo || options->FetchDirInfoEarly || - options->FetchDirInfoExtraEarly || options->FetchUselessDescriptors) - return 0; - if (! options->PreferTunneledDirConns) - return 0; - return 1; -} - -/** Pick an unconsetrained directory server from among our guards, the latest - * networkstatus, or the fallback dirservers, for use in downloading - * information of type type, and return its routerstatus. */ -static const routerstatus_t * -directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags, - uint8_t dir_purpose) -{ - const routerstatus_t *rs = NULL; - const or_options_t *options = get_options(); - - if (options->UseBridges) - log_warn(LD_BUG, "Called when we have UseBridges set."); - - if (should_use_directory_guards(options)) { - const node_t *node = choose_random_dirguard(type); - if (node) - rs = node->rs; - } else { - /* anybody with a non-zero dirport will do */ - rs = router_pick_directory_server(type, pds_flags); - } - if (!rs) { - log_info(LD_DIR, "No router found for %s; falling back to " - "dirserver list.", dir_conn_purpose_to_string(dir_purpose)); - rs = router_pick_fallback_dirserver(type, pds_flags); - } - - return rs; -} - -/** Start a connection to a random running directory server, using - * connection purpose dir_purpose, intending to fetch descriptors - * of purpose router_purpose, and requesting resource. - * Use pds_flags as arguments to router_pick_directory_server() - * or router_pick_trusteddirserver(). - */ -void -directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, - const char *resource, int pds_flags) -{ - const routerstatus_t *rs = NULL; - const or_options_t *options = get_options(); - int prefer_authority = directory_fetches_from_authorities(options); - int require_authority = 0; - int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose); - dirinfo_type_t type; - time_t if_modified_since = 0; - - /* FFFF we could break this switch into its own function, and call - * it elsewhere in directory.c. -RD */ - switch (dir_purpose) { - case DIR_PURPOSE_FETCH_EXTRAINFO: - type = EXTRAINFO_DIRINFO | - (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO : - V3_DIRINFO); - break; - case DIR_PURPOSE_FETCH_SERVERDESC: - type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO : - V3_DIRINFO); - break; - case DIR_PURPOSE_FETCH_RENDDESC: - type = HIDSERV_DIRINFO; - break; - case DIR_PURPOSE_FETCH_STATUS_VOTE: - case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: - case DIR_PURPOSE_FETCH_CERTIFICATE: - type = V3_DIRINFO; - break; - case DIR_PURPOSE_FETCH_CONSENSUS: - type = V3_DIRINFO; - if (resource && !strcmp(resource,"microdesc")) - type |= MICRODESC_DIRINFO; - break; - case DIR_PURPOSE_FETCH_MICRODESC: - type = MICRODESC_DIRINFO; - break; - default: - log_warn(LD_BUG, "Unexpected purpose %d", (int)dir_purpose); - return; - } - - if (dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS) { - int flav = FLAV_NS; - networkstatus_t *v; - if (resource) - flav = networkstatus_parse_flavor_name(resource); - - if (flav != -1) { - /* IF we have a parsed consensus of this type, we can do an - * if-modified-time based on it. */ - v = networkstatus_get_latest_consensus_by_flavor(flav); - if (v) - if_modified_since = v->valid_after + 180; - } else { - /* Otherwise it might be a consensus we don't parse, but which we - * do cache. Look at the cached copy, perhaps. */ - cached_dir_t *cd = dirserv_get_consensus(resource); - if (cd) - if_modified_since = cd->published + 180; - } - } - - if (!options->FetchServerDescriptors && type != HIDSERV_DIRINFO) - return; - - if (!get_via_tor) { - if (options->UseBridges && type != BRIDGE_DIRINFO) { - /* We want to ask a running bridge for which we have a descriptor. - * - * When we ask choose_random_entry() for a bridge, we specify what - * sort of dir fetch we'll be doing, so it won't return a bridge - * that can't answer our question. - */ - /* XXX024 Not all bridges handle conditional consensus downloading, - * so, for now, never assume the server supports that. -PP */ - const node_t *node = choose_random_dirguard(type); - if (node && node->ri) { - /* every bridge has a routerinfo. */ - tor_addr_t addr; - routerinfo_t *ri = node->ri; - node_get_addr(node, &addr); - directory_initiate_command(ri->address, &addr, - ri->or_port, 0/*no dirport*/, - ri->cache_info.identity_digest, - dir_purpose, - router_purpose, - DIRIND_ONEHOP, - resource, NULL, 0, if_modified_since); - } else - log_notice(LD_DIR, "Ignoring directory request, since no bridge " - "nodes are available yet."); - return; - } else { - if (prefer_authority || type == BRIDGE_DIRINFO) { - /* only ask authdirservers, and don't ask myself */ - rs = router_pick_trusteddirserver(type, pds_flags); - if (rs == NULL && (pds_flags & (PDS_NO_EXISTING_SERVERDESC_FETCH| - PDS_NO_EXISTING_MICRODESC_FETCH))) { - /* We don't want to fetch from any authorities that we're currently - * fetching server descriptors from, and we got no match. Did we - * get no match because all the authorities have connections - * fetching server descriptors (in which case we should just - * return,) or because all the authorities are down or on fire or - * unreachable or something (in which case we should go on with - * our fallback code)? */ - pds_flags &= ~(PDS_NO_EXISTING_SERVERDESC_FETCH| - PDS_NO_EXISTING_MICRODESC_FETCH); - rs = router_pick_trusteddirserver(type, pds_flags); - if (rs) { - log_debug(LD_DIR, "Deferring serverdesc fetch: all authorities " - "are in use."); - return; - } - } - if (rs == NULL && require_authority) { - log_info(LD_DIR, "No authorities were available for %s: will try " - "later.", dir_conn_purpose_to_string(dir_purpose)); - return; - } - } - if (!rs && type != BRIDGE_DIRINFO) { - /* */ - rs = directory_pick_generic_dirserver(type, pds_flags, - dir_purpose); - if (!rs) { - /*XXXX024 I'm pretty sure this can never do any good, since - * rs isn't set. */ - get_via_tor = 1; /* last resort: try routing it via Tor */ - } - } - } - } else { /* get_via_tor */ - /* Never use fascistfirewall; we're going via Tor. */ - if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) { - /* only ask hidserv authorities, any of them will do */ - pds_flags |= PDS_IGNORE_FASCISTFIREWALL|PDS_ALLOW_SELF; - rs = router_pick_trusteddirserver(HIDSERV_DIRINFO, pds_flags); - } else { - /* anybody with a non-zero dirport will do. Disregard firewalls. */ - pds_flags |= PDS_IGNORE_FASCISTFIREWALL; - rs = router_pick_directory_server(type, pds_flags); - /* If we have any hope of building an indirect conn, we know some router - * descriptors. If (rs==NULL), we can't build circuits anyway, so - * there's no point in falling back to the authorities in this case. */ - } - } - - if (rs) { - const dir_indirection_t indirection = - get_via_tor ? DIRIND_ANONYMOUS : DIRIND_ONEHOP; - directory_initiate_command_routerstatus(rs, dir_purpose, - router_purpose, - indirection, - resource, NULL, 0, - if_modified_since); - } else { - log_notice(LD_DIR, - "While fetching directory info, " - "no running dirservers known. Will try again later. " - "(purpose %d)", dir_purpose); - if (!purpose_needs_anonymity(dir_purpose, router_purpose)) { - /* remember we tried them all and failed. */ - directory_all_unreachable(time(NULL)); - } - } -} - -/** As directory_get_from_dirserver, but initiates a request to every - * directory authority other than ourself. Only for use by authorities when - * searching for missing information while voting. */ -void -directory_get_from_all_authorities(uint8_t dir_purpose, - uint8_t router_purpose, - const char *resource) -{ - tor_assert(dir_purpose == DIR_PURPOSE_FETCH_STATUS_VOTE || - dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES); - - SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(), - dir_server_t *, ds) { - routerstatus_t *rs; - if (router_digest_is_me(ds->digest)) - continue; - if (!(ds->type & V3_DIRINFO)) - continue; - rs = &ds->fake_status; - directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose, - DIRIND_ONEHOP, resource, NULL, - 0, 0); - } SMARTLIST_FOREACH_END(ds); -} - -/** Return true iff ind requires a multihop circuit. */ -static int -dirind_is_anon(dir_indirection_t ind) -{ - return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS; -} - -/** Same as directory_initiate_command_routerstatus(), but accepts - * rendezvous data to fetch a hidden service descriptor. */ -void -directory_initiate_command_routerstatus_rend(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - const rend_data_t *rend_query) -{ - const or_options_t *options = get_options(); - const node_t *node; - char address_buf[INET_NTOA_BUF_LEN+1]; - struct in_addr in; - const char *address; - tor_addr_t addr; - const int anonymized_connection = dirind_is_anon(indirection); - node = node_get_by_id(status->identity_digest); - - if (!node && anonymized_connection) { - log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we " - "don't have its router descriptor.", - routerstatus_describe(status)); - return; - } else if (node) { - node_get_address_string(node, address_buf, sizeof(address_buf)); - address = address_buf; - } else { - in.s_addr = htonl(status->addr); - tor_inet_ntoa(&in, address_buf, sizeof(address_buf)); - address = address_buf; - } - tor_addr_from_ipv4h(&addr, status->addr); - - if (options->ExcludeNodes && options->StrictNodes && - routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) { - log_warn(LD_DIR, "Wanted to contact directory mirror %s for %s, but " - "it's in our ExcludedNodes list and StrictNodes is set. " - "Skipping. This choice might make your Tor not work.", - routerstatus_describe(status), - dir_conn_purpose_to_string(dir_purpose)); - return; - } - - directory_initiate_command_rend(address, &addr, - status->or_port, status->dir_port, - status->identity_digest, - dir_purpose, router_purpose, - indirection, resource, - payload, payload_len, if_modified_since, - rend_query); -} - -/** Launch a new connection to the directory server status to - * upload or download a server or rendezvous - * descriptor. dir_purpose determines what - * kind of directory connection we're launching, and must be one of - * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}. router_purpose - * specifies the descriptor purposes we have in mind (currently only - * used for FETCH_DIR). - * - * When uploading, payload and payload_len determine the content - * of the HTTP post. Otherwise, payload should be NULL. - * - * When fetching a rendezvous descriptor, resource is the service ID we - * want to fetch. - */ -void -directory_initiate_command_routerstatus(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since) -{ - directory_initiate_command_routerstatus_rend(status, dir_purpose, - router_purpose, - indirection, resource, - payload, payload_len, - if_modified_since, NULL); -} - -/** Return true iff conn is the client side of a directory connection - * we launched to ourself in order to determine the reachability of our - * dir_port. */ -static int -directory_conn_is_self_reachability_test(dir_connection_t *conn) -{ - if (conn->requested_resource && - !strcmpstart(conn->requested_resource,"authority")) { - const routerinfo_t *me = router_get_my_routerinfo(); - if (me && - router_digest_is_me(conn->identity_digest) && - tor_addr_eq_ipv4h(&conn->base_.addr, me->addr) && /*XXXX prop 118*/ - me->dir_port == conn->base_.port) - return 1; - } - return 0; -} - -/** Called when we are unable to complete the client's request to a directory - * server due to a network error: Mark the router as down and try again if - * possible. - */ -static void -connection_dir_request_failed(dir_connection_t *conn) -{ - if (directory_conn_is_self_reachability_test(conn)) { - return; /* this was a test fetch. don't retry. */ - } - if (!entry_list_is_constrained(get_options())) - router_set_status(conn->identity_digest, 0); /* don't try him again */ - if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) { - log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from " - "directory server at '%s'; retrying", - conn->base_.address); - if (conn->router_purpose == ROUTER_PURPOSE_BRIDGE) - connection_dir_bridge_routerdesc_failed(conn); - connection_dir_download_routerdesc_failed(conn); - } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_CONSENSUS) { - if (conn->requested_resource) - networkstatus_consensus_download_failed(0, conn->requested_resource); - } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) { - log_info(LD_DIR, "Giving up on certificate fetch from directory server " - "at '%s'; retrying", - conn->base_.address); - connection_dir_download_cert_failed(conn, 0); - } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) { - log_info(LD_DIR, "Giving up downloading detached signatures from '%s'", - conn->base_.address); - } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { - log_info(LD_DIR, "Giving up downloading votes from '%s'", - conn->base_.address); - } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) { - log_info(LD_DIR, "Giving up on downloading microdescriptors from " - "directory server at '%s'; will retry", conn->base_.address); - connection_dir_download_routerdesc_failed(conn); - } -} - -/** Helper: Attempt to fetch directly the descriptors of each bridge - * listed in failed. - */ -static void -connection_dir_retry_bridges(smartlist_t *descs) -{ - char digest[DIGEST_LEN]; - SMARTLIST_FOREACH(descs, const char *, cp, - { - if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp))<0) { - log_warn(LD_BUG, "Malformed fingerprint in list: %s", - escaped(cp)); - continue; - } - retry_bridge_descriptor_fetch_directly(digest); - }); -} - -/** Called when an attempt to download one or more router descriptors - * or extra-info documents on connection conn failed. - */ -static void -connection_dir_download_routerdesc_failed(dir_connection_t *conn) -{ - /* No need to increment the failure count for routerdescs, since - * it's not their fault. */ - - /* No need to relaunch descriptor downloads here: we already do it - * every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in onion_main.c. */ - tor_assert(conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO || - conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC); - - (void) conn; -} - -/** Called when an attempt to download a bridge's routerdesc from - * one of the authorities failed due to a network error. If - * possible attempt to download descriptors from the bridge directly. - */ -static void -connection_dir_bridge_routerdesc_failed(dir_connection_t *conn) -{ - smartlist_t *which = NULL; - - /* Requests for bridge descriptors are in the form 'fp/', so ignore - anything else. */ - if (!conn->requested_resource || strcmpstart(conn->requested_resource,"fp/")) - return; - - which = smartlist_new(); - dir_split_resource_into_fingerprints(conn->requested_resource - + strlen("fp/"), - which, NULL, 0); - - tor_assert(conn->base_.purpose != DIR_PURPOSE_FETCH_EXTRAINFO); - if (smartlist_len(which)) { - connection_dir_retry_bridges(which); - SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); - } - smartlist_free(which); -} - -/** Called when an attempt to fetch a certificate fails. */ -static void -connection_dir_download_cert_failed(dir_connection_t *conn, int status) -{ - const char *fp_pfx = "fp/"; - const char *fpsk_pfx = "fp-sk/"; - smartlist_t *failed; - tor_assert(conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE); - - if (!conn->requested_resource) - return; - failed = smartlist_new(); - /* - * We have two cases download by fingerprint (resource starts - * with "fp/") or download by fingerprint/signing key pair - * (resource starts with "fp-sk/"). - */ - if (!strcmpstart(conn->requested_resource, fp_pfx)) { - /* Download by fingerprint case */ - dir_split_resource_into_fingerprints(conn->requested_resource + - strlen(fp_pfx), - failed, NULL, DSR_HEX); - SMARTLIST_FOREACH_BEGIN(failed, char *, cp) { - /* Null signing key digest indicates download by fp only */ - authority_cert_dl_failed(cp, NULL, status); - tor_free(cp); - } SMARTLIST_FOREACH_END(cp); - } else if (!strcmpstart(conn->requested_resource, fpsk_pfx)) { - /* Download by (fp,sk) pairs */ - dir_split_resource_into_fingerprint_pairs(conn->requested_resource + - strlen(fpsk_pfx), failed); - SMARTLIST_FOREACH_BEGIN(failed, fp_pair_t *, cp) { - authority_cert_dl_failed(cp->first, cp->second, status); - tor_free(cp); - } SMARTLIST_FOREACH_END(cp); - } else { - log_warn(LD_DIR, - "Don't know what to do with failure for cert fetch %s", - conn->requested_resource); - } - - smartlist_free(failed); - - update_certificate_downloads(time(NULL)); -} - -/** Evaluate the situation and decide if we should use an encrypted - * "begindir-style" connection for this directory request. - * 1) If or_port is 0, or it's a direct conn and or_port is firewalled - * or we're a dir mirror, no. - * 2) If we prefer to avoid begindir conns, and we're not fetching or - * publishing a bridge relay descriptor, no. - * 3) Else yes. - */ -static int -directory_command_should_use_begindir(const or_options_t *options, - const tor_addr_t *addr, - int or_port, uint8_t router_purpose, - dir_indirection_t indirection) -{ - if (!or_port) - return 0; /* We don't know an ORPort -- no chance. */ - if (indirection == DIRIND_DIRECT_CONN || indirection == DIRIND_ANON_DIRPORT) - return 0; - if (indirection == DIRIND_ONEHOP) - if (!fascist_firewall_allows_address_or(addr, or_port) || - directory_fetches_from_authorities(options)) - return 0; /* We're firewalled or are acting like a relay -- also no. */ - if (!options->TunnelDirConns && - router_purpose != ROUTER_PURPOSE_BRIDGE) - return 0; /* We prefer to avoid using begindir conns. Fine. */ - return 1; -} - -/** Helper for directory_initiate_command_routerstatus: send the - * command to a server whose address is address, whose IP is - * addr, whose directory port is dir_port, whose tor version - * supports_begindir, and whose identity key digest is - * digest. */ -void -directory_initiate_command(const char *address, const tor_addr_t *_addr, - uint16_t or_port, uint16_t dir_port, - const char *digest, - uint8_t dir_purpose, uint8_t router_purpose, - dir_indirection_t indirection, 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, - digest, dir_purpose, - router_purpose, indirection, - resource, payload, payload_len, - if_modified_since, NULL); -} - -/** Return non-zero iff a directory connection with purpose - * dir_purpose reveals sensitive information about a Tor - * instance's client activities. (Such connections must be performed - * through normal three-hop Tor circuits.) */ -static int -is_sensitive_dir_purpose(uint8_t dir_purpose) -{ - return ((dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) || - (dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC) || - (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC) || - (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) || - (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)); -} - -/** Same as directory_initiate_command(), but accepts rendezvous data to - * fetch a hidden service descriptor. */ -static void -directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, - uint16_t or_port, uint16_t dir_port, - const char *digest, - uint8_t dir_purpose, uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, size_t payload_len, - time_t if_modified_since, - const rend_data_t *rend_query) -{ - dir_connection_t *conn; - const or_options_t *options = get_options(); - int socket_error = 0; - int use_begindir = directory_command_should_use_begindir(options, _addr, - or_port, router_purpose, indirection); - const int anonymized_connection = dirind_is_anon(indirection); - tor_addr_t addr; - - tor_assert(address); - tor_assert(_addr); - tor_assert(or_port || dir_port); - tor_assert(digest); - - tor_addr_copy(&addr, _addr); - - log_debug(LD_DIR, "anonymized %d, use_begindir %d.", - anonymized_connection, use_begindir); - - log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose)); - -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(is_sensitive_dir_purpose(dir_purpose) && - !anonymized_connection)); -#else - (void)is_sensitive_dir_purpose; -#endif - - /* ensure that we don't make direct connections when a SOCKS server is - * configured. */ - if (!anonymized_connection && !use_begindir && !options->HTTPProxy && - (options->Socks4Proxy || options->Socks5Proxy)) { - log_warn(LD_DIR, "Cannot connect to a directory server through a " - "SOCKS proxy!"); - return; - } - - conn = dir_connection_new(tor_addr_family(&addr)); - - /* set up conn so it's got all the data we need to remember */ - tor_addr_copy(&conn->base_.addr, &addr); - conn->base_.port = use_begindir ? or_port : dir_port; - conn->base_.address = tor_strdup(address); - memcpy(conn->identity_digest, digest, DIGEST_LEN); - - conn->base_.purpose = dir_purpose; - conn->router_purpose = router_purpose; - - /* give it an initial state */ - conn->base_.state = DIR_CONN_STATE_CONNECTING; - - /* decide whether we can learn our IP address from this conn */ - /* XXXX This is a bad name for this field now. */ - conn->dirconn_direct = !anonymized_connection; - - /* copy rendezvous data, if any */ - if (rend_query) - conn->rend_data = rend_data_dup(rend_query); - - if (!anonymized_connection && !use_begindir) { - /* then we want to connect to dirport directly */ - - if (options->HTTPProxy) { - tor_addr_copy(&addr, &options->HTTPProxyAddr); - dir_port = options->HTTPProxyPort; - } - - switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr, - dir_port, &socket_error)) { - case -1: - connection_dir_request_failed(conn); /* retry if we want */ - /* XXX we only pass 'conn' above, not 'resource', 'payload', - * etc. So in many situations it can't retry! -RD */ - connection_free(TO_CONN(conn)); - return; - case 1: - /* start flushing conn */ - conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; - /* fall through */ - case 0: - /* queue the command on the outbuf */ - directory_send_command(conn, dir_purpose, 1, resource, - payload, payload_len, - if_modified_since); - connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); - /* writable indicates finish, readable indicates broken link, - error indicates broken link in windowsland. */ - } - } else { /* we want to connect via a tor connection */ - entry_connection_t *linked_conn; - /* Anonymized tunneled connections can never share a circuit. - * One-hop directory connections can share circuits with each other - * but nothing else. */ - int iso_flags = anonymized_connection ? ISO_STREAM : ISO_SESSIONGRP; - - /* If it's an anonymized connection, remember the fact that we - * wanted it for later: maybe we'll want it again soon. */ - if (anonymized_connection && use_begindir) - rep_hist_note_used_internal(time(NULL), 0, 1); - else if (anonymized_connection && !use_begindir) - rep_hist_note_used_port(time(NULL), conn->base_.port); - - /* make an AP connection - * populate it and add it at the right state - * hook up both sides - */ - linked_conn = - connection_ap_make_link(TO_CONN(conn), - conn->base_.address, conn->base_.port, - digest, - SESSION_GROUP_DIRCONN, iso_flags, - use_begindir, conn->dirconn_direct); - if (!linked_conn) { - log_warn(LD_NET,"Making tunnel to dirserver failed."); - connection_mark_for_close(TO_CONN(conn)); - return; - } - - if (connection_add(TO_CONN(conn)) < 0) { - log_warn(LD_NET,"Unable to add connection for link to dirserver."); - connection_mark_for_close(TO_CONN(conn)); - return; - } - conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; - /* queue the command on the outbuf */ - directory_send_command(conn, dir_purpose, 0, resource, - payload, payload_len, - if_modified_since); - - connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); - IF_HAS_BUFFEREVENT(ENTRY_TO_CONN(linked_conn), { - connection_watch_events(ENTRY_TO_CONN(linked_conn), - READ_EVENT|WRITE_EVENT); - }) ELSE_IF_NO_BUFFEREVENT - connection_start_reading(ENTRY_TO_CONN(linked_conn)); - } -} - -/** Return true iff anything we say on conn is being encrypted before - * we send it to the client/server. */ -int -connection_dir_is_encrypted(dir_connection_t *conn) -{ - /* Right now it's sufficient to see if conn is or has been linked, since - * the only thing it could be linked to is an edge connection on a - * circuit, and the only way it could have been unlinked is at the edge - * connection getting closed. - */ - return TO_CONN(conn)->linked; -} - -/** Helper for sorting - * - * sort strings alphabetically - */ -static int -compare_strs_(const void **a, const void **b) -{ - const char *s1 = *a, *s2 = *b; - return strcmp(s1, s2); -} - -#define CONDITIONAL_CONSENSUS_FPR_LEN 3 -#if (CONDITIONAL_CONSENSUS_FPR_LEN > DIGEST_LEN) -#error "conditional consensus fingerprint length is larger than digest length" -#endif - -/** Return the URL we should use for a consensus download. - * - * This url depends on whether or not the server we go to - * is sufficiently new to support conditional consensus downloading, - * i.e. GET .../consensus/fpr+fpr+fpr - * - * If 'resource' is provided, it is the name of a consensus flavor to request. - */ -static char * -directory_get_consensus_url(const char *resource) -{ - char *url = NULL; - const char *hyphen, *flavor; - if (resource==NULL || strcmp(resource, "ns")==0) { - flavor = ""; /* Request ns consensuses as "", so older servers will work*/ - hyphen = ""; - } else { - flavor = resource; - hyphen = "-"; - } - - { - char *authority_id_list; - smartlist_t *authority_digests = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(), - dir_server_t *, ds) { - char *hex; - if (!(ds->type & V3_DIRINFO)) - continue; - - hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1); - base16_encode(hex, 2*CONDITIONAL_CONSENSUS_FPR_LEN+1, - ds->v3_identity_digest, CONDITIONAL_CONSENSUS_FPR_LEN); - smartlist_add(authority_digests, hex); - } SMARTLIST_FOREACH_END(ds); - smartlist_sort(authority_digests, compare_strs_); - authority_id_list = smartlist_join_strings(authority_digests, - "+", 0, NULL); - - tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s/%s.z", - hyphen, flavor, authority_id_list); - - SMARTLIST_FOREACH(authority_digests, char *, cp, tor_free(cp)); - smartlist_free(authority_digests); - tor_free(authority_id_list); - } - return url; -} - -/** Queue an appropriate HTTP command on conn-\>outbuf. The other args - * are as in directory_initiate_command(). - */ -static void -directory_send_command(dir_connection_t *conn, - int purpose, int direct, const char *resource, - const char *payload, size_t payload_len, - time_t if_modified_since) -{ - char proxystring[256]; - char hoststring[128]; - smartlist_t *headers = smartlist_new(); - char *url; - char request[8192]; - const char *httpcommand = NULL; - - tor_assert(conn); - tor_assert(conn->base_.type == CONN_TYPE_DIR); - - tor_free(conn->requested_resource); - if (resource) - conn->requested_resource = tor_strdup(resource); - - /* come up with a string for which Host: we want */ - if (conn->base_.port == 80) { - strlcpy(hoststring, conn->base_.address, sizeof(hoststring)); - } else { - tor_snprintf(hoststring, sizeof(hoststring),"%s:%d", - conn->base_.address, conn->base_.port); - } - - /* Format if-modified-since */ - if (if_modified_since) { - char b[RFC1123_TIME_LEN+1]; - format_rfc1123_time(b, if_modified_since); - smartlist_add_asprintf(headers, "If-Modified-Since: %s\r\n", b); - } - - /* come up with some proxy lines, if we're using one. */ - if (direct && get_options()->HTTPProxy) { - char *base64_authenticator=NULL; - const char *authenticator = get_options()->HTTPProxyAuthenticator; - - tor_snprintf(proxystring, sizeof(proxystring),"http://%s", hoststring); - if (authenticator) { - base64_authenticator = alloc_http_authenticator(authenticator); - if (!base64_authenticator) - log_warn(LD_BUG, "Encoding http authenticator failed"); - } - if (base64_authenticator) { - smartlist_add_asprintf(headers, - "Proxy-Authorization: Basic %s\r\n", - base64_authenticator); - tor_free(base64_authenticator); - } - } else { - proxystring[0] = 0; - } - - switch (purpose) { - case DIR_PURPOSE_FETCH_CONSENSUS: - /* resource is optional. If present, it's a flavor name */ - tor_assert(!payload); - httpcommand = "GET"; - url = directory_get_consensus_url(resource); - log_info(LD_DIR, "Downloading consensus from %s using %s", - hoststring, url); - break; - case DIR_PURPOSE_FETCH_CERTIFICATE: - tor_assert(resource); - tor_assert(!payload); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/keys/%s", resource); - break; - case DIR_PURPOSE_FETCH_STATUS_VOTE: - tor_assert(resource); - tor_assert(!payload); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/status-vote/next/%s.z", resource); - break; - case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: - tor_assert(!resource); - tor_assert(!payload); - httpcommand = "GET"; - url = tor_strdup("/tor/status-vote/next/consensus-signatures.z"); - break; - case DIR_PURPOSE_FETCH_SERVERDESC: - tor_assert(resource); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/server/%s", resource); - break; - case DIR_PURPOSE_FETCH_EXTRAINFO: - tor_assert(resource); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/extra/%s", resource); - break; - case DIR_PURPOSE_FETCH_MICRODESC: - tor_assert(resource); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/micro/%s", resource); - break; - case DIR_PURPOSE_UPLOAD_DIR: { - const char *why = router_get_descriptor_gen_reason(); - tor_assert(!resource); - tor_assert(payload); - httpcommand = "POST"; - url = tor_strdup("/tor/"); - if (why) { - smartlist_add_asprintf(headers, "X-Desc-Gen-Reason: %s\r\n", why); - } - break; - } - case DIR_PURPOSE_UPLOAD_VOTE: - tor_assert(!resource); - tor_assert(payload); - httpcommand = "POST"; - url = tor_strdup("/tor/post/vote"); - break; - case DIR_PURPOSE_UPLOAD_SIGNATURES: - tor_assert(!resource); - tor_assert(payload); - httpcommand = "POST"; - url = tor_strdup("/tor/post/consensus-signature"); - break; - case DIR_PURPOSE_FETCH_RENDDESC_V2: - tor_assert(resource); - tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32); - tor_assert(!payload); - httpcommand = "GET"; - tor_asprintf(&url, "/tor/rendezvous2/%s", resource); - break; - case DIR_PURPOSE_UPLOAD_RENDDESC: - tor_assert(!resource); - tor_assert(payload); - httpcommand = "POST"; - url = tor_strdup("/tor/rendezvous/publish"); - break; - case DIR_PURPOSE_UPLOAD_RENDDESC_V2: - tor_assert(!resource); - tor_assert(payload); - httpcommand = "POST"; - url = tor_strdup("/tor/rendezvous2/publish"); - break; - default: - tor_assert(0); - return; - } - - if (strlen(proxystring) + strlen(url) >= 4096) { - log_warn(LD_BUG, - "Squid does not like URLs longer than 4095 bytes, and this " - "one is %d bytes long: %s%s", - (int)(strlen(proxystring) + strlen(url)), proxystring, url); - } - - tor_snprintf(request, sizeof(request), "%s %s", httpcommand, proxystring); - connection_write_to_buf(request, strlen(request), TO_CONN(conn)); - connection_write_to_buf(url, strlen(url), TO_CONN(conn)); - tor_free(url); - - if (!strcmp(httpcommand, "POST") || payload) { - smartlist_add_asprintf(headers, "Content-Length: %lu\r\n", - payload ? (unsigned long)payload_len : 0); - } - - { - char *header = smartlist_join_strings(headers, "", 0, NULL); - tor_snprintf(request, sizeof(request), " HTTP/1.0\r\nHost: %s\r\n%s\r\n", - hoststring, header); - tor_free(header); - } - - connection_write_to_buf(request, strlen(request), TO_CONN(conn)); - - if (payload) { - /* then send the payload afterwards too */ - connection_write_to_buf(payload, payload_len, TO_CONN(conn)); - } - - SMARTLIST_FOREACH(headers, char *, h, tor_free(h)); - smartlist_free(headers); -} - -/** Parse an HTTP request string headers of the form - * \verbatim - * "\%s [http[s]://]\%s HTTP/1..." - * \endverbatim - * If it's well-formed, strdup the second \%s into *url, and - * nul-terminate it. If the url doesn't start with "/tor/", rewrite it - * so it does. Return 0. - * Otherwise, return -1. - */ -STATIC int -parse_http_url(const char *headers, char **url) -{ - char *s, *start, *tmp; - - s = (char *)eat_whitespace_no_nl(headers); - if (!*s) return -1; - s = (char *)find_whitespace(s); /* get past GET/POST */ - if (!*s) return -1; - s = (char *)eat_whitespace_no_nl(s); - if (!*s) return -1; - start = s; /* this is it, assuming it's valid */ - s = (char *)find_whitespace(start); - if (!*s) return -1; - - /* tolerate the http[s] proxy style of putting the hostname in the url */ - if (s-start >= 4 && !strcmpstart(start,"http")) { - tmp = start + 4; - if (*tmp == 's') - tmp++; - if (s-tmp >= 3 && !strcmpstart(tmp,"://")) { - tmp = strchr(tmp+3, '/'); - if (tmp && tmp < s) { - log_debug(LD_DIR,"Skipping over 'http[s]://hostname/' string"); - start = tmp; - } - } - } - - /* Check if the header is well formed (next sequence - * should be HTTP/1.X\r\n). Assumes we're supporting 1.0? */ - { - unsigned minor_ver; - char ch; - char *e = (char *)eat_whitespace_no_nl(s); - if (2 != tor_sscanf(e, "HTTP/1.%u%c", &minor_ver, &ch)) { - return -1; - } - if (ch != '\r') - return -1; - } - - if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */ - *url = tor_malloc(s - start + 5); - strlcpy(*url,"/tor", s-start+5); - strlcat((*url)+4, start, s-start+1); - } else { - *url = tor_strndup(start, s-start); - } - return 0; -} - -/** Return a copy of the first HTTP header in headers whose key is - * which. The key should be given with a terminating colon and space; - * this function copies everything after, up to but not including the - * following \\r\\n. */ -static char * -http_get_header(const char *headers, const char *which) -{ - const char *cp = headers; - while (cp) { - if (!strcasecmpstart(cp, which)) { - char *eos; - cp += strlen(which); - if ((eos = strchr(cp,'\r'))) - return tor_strndup(cp, eos-cp); - else - return tor_strdup(cp); - } - cp = strchr(cp, '\n'); - if (cp) - ++cp; - } - return NULL; -} - -/** If headers indicates that a proxy was involved, then rewrite - * conn-\>address to describe our best guess of the address that - * originated this HTTP request. */ -static void -http_set_address_origin(const char *headers, connection_t *conn) -{ - char *fwd; - - fwd = http_get_header(headers, "Forwarded-For: "); - if (!fwd) - fwd = http_get_header(headers, "X-Forwarded-For: "); - if (fwd) { - struct in_addr in; - if (!tor_inet_aton(fwd, &in) || is_internal_IP(ntohl(in.s_addr), 0)) { - log_debug(LD_DIR, "Ignoring unrecognized or internal IP %s", - escaped(fwd)); - tor_free(fwd); - return; - } - tor_free(conn->address); - conn->address = tor_strdup(fwd); - tor_free(fwd); - } -} - -/** Parse an HTTP response string headers of the form - * \verbatim - * "HTTP/1.\%d \%d\%s\r\n...". - * \endverbatim - * - * If it's well-formed, assign the status code to *code and - * return 0. Otherwise, return -1. - * - * On success: If date is provided, set *date to the Date - * header in the http headers, or 0 if no such header is found. If - * compression is provided, set *compression to the - * compression method given in the Content-Encoding header, or 0 if no - * such header is found, or -1 if the value of the header is not - * recognized. If reason is provided, strdup the reason string - * into it. - */ -int -parse_http_response(const char *headers, int *code, time_t *date, - compress_method_t *compression, char **reason) -{ - unsigned n1, n2; - char datestr[RFC1123_TIME_LEN+1]; - smartlist_t *parsed_headers; - tor_assert(headers); - tor_assert(code); - - while (TOR_ISSPACE(*headers)) headers++; /* tolerate leading whitespace */ - - if (tor_sscanf(headers, "HTTP/1.%u %u", &n1, &n2) < 2 || - (n1 != 0 && n1 != 1) || - (n2 < 100 || n2 >= 600)) { - log_warn(LD_HTTP,"Failed to parse header %s",escaped(headers)); - return -1; - } - *code = n2; - - parsed_headers = smartlist_new(); - smartlist_split_string(parsed_headers, headers, "\n", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - if (reason) { - smartlist_t *status_line_elements = smartlist_new(); - tor_assert(smartlist_len(parsed_headers)); - smartlist_split_string(status_line_elements, - smartlist_get(parsed_headers, 0), - " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3); - tor_assert(smartlist_len(status_line_elements) <= 3); - if (smartlist_len(status_line_elements) == 3) { - *reason = smartlist_get(status_line_elements, 2); - smartlist_set(status_line_elements, 2, NULL); /* Prevent free */ - } - SMARTLIST_FOREACH(status_line_elements, char *, cp, tor_free(cp)); - smartlist_free(status_line_elements); - } - if (date) { - *date = 0; - SMARTLIST_FOREACH(parsed_headers, const char *, s, - if (!strcmpstart(s, "Date: ")) { - strlcpy(datestr, s+6, sizeof(datestr)); - /* This will do nothing on failure, so we don't need to check - the result. We shouldn't warn, since there are many other valid - date formats besides the one we use. */ - parse_rfc1123_time(datestr, date); - break; - }); - } - if (compression) { - const char *enc = NULL; - SMARTLIST_FOREACH(parsed_headers, const char *, s, - if (!strcmpstart(s, "Content-Encoding: ")) { - enc = s+18; break; - }); - if (!enc || !strcmp(enc, "identity")) { - *compression = NO_METHOD; - } else if (!strcmp(enc, "deflate") || !strcmp(enc, "x-deflate")) { - *compression = ZLIB_METHOD; - } else if (!strcmp(enc, "gzip") || !strcmp(enc, "x-gzip")) { - *compression = GZIP_METHOD; - } else { - log_info(LD_HTTP, "Unrecognized content encoding: %s. Trying to deal.", - escaped(enc)); - *compression = UNKNOWN_METHOD; - } - } - SMARTLIST_FOREACH(parsed_headers, char *, s, tor_free(s)); - smartlist_free(parsed_headers); - - return 0; -} - -/** Return true iff body doesn't start with a plausible router or - * running-list or directory opening. This is a sign of possible compression. - **/ -static int -body_is_plausible(const char *body, size_t len, int purpose) -{ - int i; - if (len == 0) - return 1; /* empty bodies don't need decompression */ - if (len < 32) - return 0; - if (purpose == DIR_PURPOSE_FETCH_MICRODESC) { - return (!strcmpstart(body,"onion-key")); - } - if (purpose != DIR_PURPOSE_FETCH_RENDDESC) { - if (!strcmpstart(body,"router") || - !strcmpstart(body,"signed-directory") || - !strcmpstart(body,"network-status") || - !strcmpstart(body,"running-routers")) - return 1; - for (i=0;i<32;++i) { - if (!TOR_ISPRINT(body[i]) && !TOR_ISSPACE(body[i])) - return 0; - } - return 1; - } else { - return 1; - } -} - -/** Called when we've just fetched a bunch of router descriptors in - * body. The list which, if present, holds digests for - * descriptors we requested: descriptor digests if descriptor_digests - * is true, or identity digests otherwise. Parse the descriptors, validate - * them, and annotate them as having purpose purpose and as having been - * downloaded from source. - * - * Return the number of routers actually added. */ -static int -load_downloaded_routers(const char *body, smartlist_t *which, - int descriptor_digests, - int router_purpose, - const char *source) -{ - char buf[256]; - char time_buf[ISO_TIME_LEN+1]; - int added = 0; - int general = router_purpose == ROUTER_PURPOSE_GENERAL; - format_iso_time(time_buf, time(NULL)); - tor_assert(source); - - if (tor_snprintf(buf, sizeof(buf), - "@downloaded-at %s\n" - "@source %s\n" - "%s%s%s", time_buf, escaped(source), - !general ? "@purpose " : "", - !general ? router_purpose_to_string(router_purpose) : "", - !general ? "\n" : "")<0) - return added; - - added = router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which, - descriptor_digests, buf); - if (general) - control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, - count_loading_descriptors_progress()); - return added; -} - -/** We are a client, and we've finished reading the server's - * response. Parse it and act appropriately. - * - * If we're still happy with using this directory server in the future, return - * 0. Otherwise return -1; and the caller should consider trying the request - * again. - * - * The caller will take care of marking the connection for close. - */ -static int -connection_dir_client_reached_eof(dir_connection_t *conn) -{ - char *body; - char *headers; - char *reason = NULL; - size_t body_len = 0, orig_len = 0; - int status_code; - time_t date_header = 0; - long delta; - compress_method_t compression; - int plausible; - int skewed = 0; - int allow_partial = (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO || - conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC); - int was_compressed = 0; - time_t now = time(NULL); - int src_code; - - switch (connection_fetch_from_buf_http(TO_CONN(conn), - &headers, MAX_HEADERS_SIZE, - &body, &body_len, MAX_DIR_DL_SIZE, - allow_partial)) { - case -1: /* overflow */ - log_warn(LD_PROTOCOL, - "'fetch' response too large (server '%s:%d'). Closing.", - conn->base_.address, conn->base_.port); - return -1; - case 0: - log_info(LD_HTTP, - "'fetch' response not all here, but we're at eof. Closing."); - return -1; - /* case 1, fall through */ - } - orig_len = body_len; - - if (parse_http_response(headers, &status_code, &date_header, - &compression, &reason) < 0) { - log_warn(LD_HTTP,"Unparseable headers (server '%s:%d'). Closing.", - conn->base_.address, conn->base_.port); - tor_free(body); tor_free(headers); - return -1; - } - if (!reason) reason = tor_strdup("[no reason given]"); - - log_debug(LD_DIR, - "Received response from directory server '%s:%d': %d %s " - "(purpose: %d)", - conn->base_.address, conn->base_.port, status_code, - escaped(reason), - conn->base_.purpose); - - /* now check if it's got any hints for us about our IP address. */ - if (conn->dirconn_direct) { - char *guess = http_get_header(headers, X_ADDRESS_HEADER); - if (guess) { - router_new_address_suggestion(guess, conn); - tor_free(guess); - } - } - - if (date_header > 0) { - /* The date header was written very soon after we sent our request, - * so compute the skew as the difference between sending the request - * and the date header. (We used to check now-date_header, but that's - * inaccurate if we spend a lot of time downloading.) - */ - delta = conn->base_.timestamp_lastwritten - date_header; - if (labs(delta)>ALLOW_DIRECTORY_TIME_SKEW) { - char dbuf[64]; - int trusted = router_digest_is_trusted_dir(conn->identity_digest); - format_time_interval(dbuf, sizeof(dbuf), delta); - log_fn(trusted ? LOG_WARN : LOG_INFO, - LD_HTTP, - "Received directory with skewed time (server '%s:%d'): " - "It seems that our clock is %s by %s, or that theirs is %s. " - "Tor requires an accurate clock to work: please check your time, " - "timezone, and date settings.", - conn->base_.address, conn->base_.port, - delta>0 ? "ahead" : "behind", dbuf, - delta>0 ? "behind" : "ahead"); - skewed = 1; /* don't check the recommended-versions line */ - if (trusted) - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d", - delta, conn->base_.address, conn->base_.port); - } else { - log_debug(LD_HTTP, "Time on received directory is within tolerance; " - "we are %ld seconds skewed. (That's okay.)", delta); - } - } - (void) skewed; /* skewed isn't used yet. */ - - if (status_code == 503) { - routerstatus_t *rs; - dir_server_t *ds; - const char *id_digest = conn->identity_digest; - log_info(LD_DIR,"Received http status code %d (%s) from server " - "'%s:%d'. I'll try again soon.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - if ((rs = router_get_mutable_consensus_status_by_id(id_digest))) - rs->last_dir_503_at = now; - if ((ds = router_get_fallback_dirserver_by_digest(id_digest))) - ds->fake_status.last_dir_503_at = now; - - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - - plausible = body_is_plausible(body, body_len, conn->base_.purpose); - if (compression != NO_METHOD || !plausible) { - char *new_body = NULL; - size_t new_len = 0; - compress_method_t guessed = detect_compression_method(body, body_len); - if (compression == UNKNOWN_METHOD || guessed != compression) { - /* Tell the user if we don't believe what we're told about compression.*/ - const char *description1, *description2; - if (compression == ZLIB_METHOD) - description1 = "as deflated"; - else if (compression == GZIP_METHOD) - description1 = "as gzipped"; - else if (compression == NO_METHOD) - description1 = "as uncompressed"; - else - description1 = "with an unknown Content-Encoding"; - if (guessed == ZLIB_METHOD) - description2 = "deflated"; - else if (guessed == GZIP_METHOD) - description2 = "gzipped"; - else if (!plausible) - description2 = "confusing binary junk"; - else - description2 = "uncompressed"; - - log_info(LD_HTTP, "HTTP body from server '%s:%d' was labeled %s, " - "but it seems to be %s.%s", - conn->base_.address, conn->base_.port, description1, - description2, - (compression>0 && guessed>0)?" Trying both.":""); - } - /* Try declared compression first if we can. */ - if (compression == GZIP_METHOD || compression == ZLIB_METHOD) - tor_gzip_uncompress(&new_body, &new_len, body, body_len, compression, - !allow_partial, LOG_PROTOCOL_WARN); - /* Okay, if that didn't work, and we think that it was compressed - * differently, try that. */ - if (!new_body && - (guessed == GZIP_METHOD || guessed == ZLIB_METHOD) && - compression != guessed) - tor_gzip_uncompress(&new_body, &new_len, body, body_len, guessed, - !allow_partial, LOG_PROTOCOL_WARN); - /* If we're pretty sure that we have a compressed directory, and - * we didn't manage to uncompress it, then warn and bail. */ - if (!plausible && !new_body) { - log_fn(LOG_PROTOCOL_WARN, LD_HTTP, - "Unable to decompress HTTP body (server '%s:%d').", - conn->base_.address, conn->base_.port); - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - if (new_body) { - tor_free(body); - body = new_body; - body_len = new_len; - was_compressed = 1; - } - } - - if (conn->base_.purpose == DIR_PURPOSE_FETCH_CONSENSUS) { - int r; - const char *flavname = conn->requested_resource; - if (status_code != 200) { - int severity = (status_code == 304) ? LOG_INFO : LOG_WARN; - tor_log(severity, LD_DIR, - "Received http status code %d (%s) from server " - "'%s:%d' while fetching consensus directory.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - tor_free(body); tor_free(headers); tor_free(reason); - networkstatus_consensus_download_failed(status_code, flavname); - return -1; - } - log_info(LD_DIR,"Received consensus directory (size %d) from server " - "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); - if ((r=networkstatus_set_current_consensus(body, flavname, 0))<0) { - log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR, - "Unable to load %s consensus directory downloaded from " - "server '%s:%d'. I'll try again soon.", - flavname, conn->base_.address, conn->base_.port); - tor_free(body); tor_free(headers); tor_free(reason); - networkstatus_consensus_download_failed(0, flavname); - return -1; - } - /* launches router downloads as needed */ - routers_update_all_from_networkstatus(now, 3); - update_microdescs_from_networkstatus(now); - update_microdesc_downloads(now); - directory_info_has_arrived(now, 0); - log_info(LD_DIR, "Successfully loaded consensus."); - } - - if (conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) { - if (status_code != 200) { - log_warn(LD_DIR, - "Received http status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/keys/%s\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); - connection_dir_download_cert_failed(conn, status_code); - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - log_info(LD_DIR,"Received authority certificates (size %d) from server " - "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); - - /* - * Tell trusted_dirs_load_certs_from_string() whether it was by fp - * or fp-sk pair. - */ - src_code = -1; - if (!strcmpstart(conn->requested_resource, "fp/")) { - src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST; - } else if (!strcmpstart(conn->requested_resource, "fp-sk/")) { - src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST; - } - - if (src_code != -1) { - if (trusted_dirs_load_certs_from_string(body, src_code, 1)<0) { - log_warn(LD_DIR, "Unable to parse fetched certificates"); - /* if we fetched more than one and only some failed, the successful - * ones got flushed to disk so it's safe to call this on them */ - connection_dir_download_cert_failed(conn, status_code); - } else { - directory_info_has_arrived(now, 0); - log_info(LD_DIR, "Successfully loaded certificates from fetch."); - } - } else { - log_warn(LD_DIR, - "Couldn't figure out what to do with fetched certificates for " - "unknown resource %s", - conn->requested_resource); - connection_dir_download_cert_failed(conn, status_code); - } - } - if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { - const char *msg; - int st; - log_info(LD_DIR,"Got votes (size %d) from server %s:%d", - (int)body_len, conn->base_.address, conn->base_.port); - if (status_code != 200) { - log_warn(LD_DIR, - "Received http status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/status-vote/next/%s.z\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - dirvote_add_vote(body, &msg, &st); - if (st > 299) { - log_warn(LD_DIR, "Error adding retrieved vote: %s", msg); - } else { - log_info(LD_DIR, "Added vote(s) successfully [msg: %s]", msg); - } - } - if (conn->base_.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) { - const char *msg = NULL; - log_info(LD_DIR,"Got detached signatures (size %d) from server %s:%d", - (int)body_len, conn->base_.address, conn->base_.port); - if (status_code != 200) { - log_warn(LD_DIR, - "Received http status code %d (%s) from server '%s:%d' while fetching " - "\"/tor/status-vote/next/consensus-signatures.z\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - if (dirvote_add_signatures(body, conn->base_.address, &msg)<0) { - log_warn(LD_DIR, "Problem adding detached signatures from %s:%d: %s", - conn->base_.address, conn->base_.port, msg?msg:"???"); - } - } - - if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) { - int was_ei = conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO; - smartlist_t *which = NULL; - int n_asked_for = 0; - int descriptor_digests = conn->requested_resource && - !strcmpstart(conn->requested_resource,"d/"); - log_info(LD_DIR,"Received %s (size %d) from server '%s:%d'", - was_ei ? "extra server info" : "server info", - (int)body_len, conn->base_.address, conn->base_.port); - if (conn->requested_resource && - (!strcmpstart(conn->requested_resource,"d/") || - !strcmpstart(conn->requested_resource,"fp/"))) { - which = smartlist_new(); - dir_split_resource_into_fingerprints(conn->requested_resource + - (descriptor_digests ? 2 : 3), - which, NULL, 0); - n_asked_for = smartlist_len(which); - } - if (status_code != 200) { - int dir_okay = status_code == 404 || - (status_code == 400 && !strcmp(reason, "Servers unavailable.")); - /* 404 means that it didn't have them; no big deal. - * Older (pre-0.1.1.8) servers said 400 Servers unavailable instead. */ - log_fn(dir_okay ? LOG_INFO : LOG_WARN, LD_DIR, - "Received http status code %d (%s) from server '%s:%d' " - "while fetching \"/tor/server/%s\". I'll try again soon.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); - if (!which) { - connection_dir_download_routerdesc_failed(conn); - } else { - dir_routerdesc_download_failed(which, status_code, - conn->router_purpose, - was_ei, descriptor_digests); - SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); - smartlist_free(which); - } - tor_free(body); tor_free(headers); tor_free(reason); - return dir_okay ? 0 : -1; - } - /* Learn the routers, assuming we requested by fingerprint or "all" - * or "authority". - * - * We use "authority" to fetch our own descriptor for - * testing, and to fetch bridge descriptors for bootstrapping. Ignore - * the output of "authority" requests unless we are using bridges, - * since otherwise they'll be the response from reachability tests, - * and we don't really want to add that to our routerlist. */ - if (which || (conn->requested_resource && - (!strcmpstart(conn->requested_resource, "all") || - (!strcmpstart(conn->requested_resource, "authority") && - get_options()->UseBridges)))) { - /* as we learn from them, we remove them from 'which' */ - if (was_ei) { - router_load_extrainfo_from_string(body, NULL, SAVED_NOWHERE, which, - descriptor_digests); - } else { - //router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which, - // descriptor_digests, conn->router_purpose); - if (load_downloaded_routers(body, which, descriptor_digests, - conn->router_purpose, - conn->base_.address)) - directory_info_has_arrived(now, 0); - } - } - if (which) { /* mark remaining ones as failed */ - log_info(LD_DIR, "Received %d/%d %s requested from %s:%d", - n_asked_for-smartlist_len(which), n_asked_for, - was_ei ? "extra-info documents" : "router descriptors", - conn->base_.address, (int)conn->base_.port); - if (smartlist_len(which)) { - dir_routerdesc_download_failed(which, status_code, - conn->router_purpose, - was_ei, descriptor_digests); - } - SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); - smartlist_free(which); - } - if (directory_conn_is_self_reachability_test(conn)) - router_dirport_found_reachable(); - } - if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) { - smartlist_t *which = NULL; - log_info(LD_DIR,"Received answer to microdescriptor request (status %d, " - "size %d) from server '%s:%d'", - status_code, (int)body_len, conn->base_.address, - conn->base_.port); - tor_assert(conn->requested_resource && - !strcmpstart(conn->requested_resource, "d/")); - which = smartlist_new(); - dir_split_resource_into_fingerprints(conn->requested_resource+2, - which, NULL, - DSR_DIGEST256|DSR_BASE64); - if (status_code != 200) { - log_info(LD_DIR, "Received status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/micro/%s\". I'll try again " - "soon.", - status_code, escaped(reason), conn->base_.address, - (int)conn->base_.port, conn->requested_resource); - dir_microdesc_download_failed(which, status_code); - SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); - smartlist_free(which); - tor_free(body); tor_free(headers); tor_free(reason); - return 0; - } else { - smartlist_t *mds; - mds = microdescs_add_to_cache(get_microdesc_cache(), - body, body+body_len, SAVED_NOWHERE, 0, - now, which); - if (smartlist_len(which)) { - /* Mark remaining ones as failed. */ - dir_microdesc_download_failed(which, status_code); - } - control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, - count_loading_descriptors_progress()); - SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); - smartlist_free(which); - smartlist_free(mds); - } - } - - if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_DIR) { - switch (status_code) { - case 200: { - dir_server_t *ds = - router_get_trusteddirserver_by_digest(conn->identity_digest); - char *rejected_hdr = http_get_header(headers, - "X-Descriptor-Not-New: "); - if (rejected_hdr) { - if (!strcmp(rejected_hdr, "Yes")) { - log_info(LD_GENERAL, - "Authority '%s' declined our descriptor (not new)", - ds->nickname); - /* XXXX use this information; be sure to upload next one - * sooner. -NM */ - /* XXXX023 On further thought, the task above implies that we're - * basing our regenerate-descriptor time on when we uploaded the - * last descriptor, not on the published time of the last - * descriptor. If those are different, that's a bad thing to - * do. -NM */ - } - tor_free(rejected_hdr); - } - log_info(LD_GENERAL,"eof (status 200) after uploading server " - "descriptor: finished."); - control_event_server_status( - LOG_NOTICE, "ACCEPTED_SERVER_DESCRIPTOR DIRAUTH=%s:%d", - conn->base_.address, conn->base_.port); - - ds->has_accepted_serverdesc = 1; - if (directories_have_accepted_server_descriptor()) - control_event_server_status(LOG_NOTICE, "GOOD_SERVER_DESCRIPTOR"); - } - break; - case 400: - log_warn(LD_GENERAL,"http status 400 (%s) response from " - "dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); - control_event_server_status(LOG_WARN, - "BAD_SERVER_DESCRIPTOR DIRAUTH=%s:%d REASON=\"%s\"", - conn->base_.address, conn->base_.port, escaped(reason)); - break; - default: - log_warn(LD_GENERAL, - "http status %d (%s) reason unexpected while uploading " - "descriptor to server '%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - break; - } - /* return 0 in all cases, since we don't want to mark any - * dirservers down just because they don't like us. */ - } - - if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_VOTE) { - switch (status_code) { - case 200: { - log_notice(LD_DIR,"Uploaded a vote to dirserver %s:%d", - conn->base_.address, conn->base_.port); - } - break; - case 400: - log_warn(LD_DIR,"http status 400 (%s) response after uploading " - "vote to dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); - break; - default: - log_warn(LD_GENERAL, - "http status %d (%s) reason unexpected while uploading " - "vote to server '%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - break; - } - /* return 0 in all cases, since we don't want to mark any - * dirservers down just because they don't like us. */ - } - - if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_SIGNATURES) { - switch (status_code) { - case 200: { - log_notice(LD_DIR,"Uploaded signature(s) to dirserver %s:%d", - conn->base_.address, conn->base_.port); - } - break; - case 400: - log_warn(LD_DIR,"http status 400 (%s) response after uploading " - "signatures to dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); - break; - default: - log_warn(LD_GENERAL, - "http status %d (%s) reason unexpected while uploading " - "signatures to server '%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - break; - } - /* return 0 in all cases, since we don't want to mark any - * dirservers down just because they don't like us. */ - } - - if (conn->base_.purpose == DIR_PURPOSE_FETCH_RENDDESC) { - tor_assert(conn->rend_data); - log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " - "(%s))", - (int)body_len, status_code, escaped(reason)); - switch (status_code) { - case 200: - if (rend_cache_store(body, body_len, 0, - conn->rend_data->onion_address) < -1) { - log_warn(LD_REND,"Failed to parse rendezvous descriptor."); - /* Any pending rendezvous attempts will notice when - * connection_about_to_close_connection() - * cleans this dir conn up. */ - /* We could retry. But since v0 descriptors are going out of - * style, it isn't worth the hassle. We'll do better in v2. */ - } else { - /* Success, or at least there's a v2 descriptor already - * present. Notify pending connections about this. */ - conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC; - rend_client_desc_trynow(conn->rend_data->onion_address); - } - break; - case 404: - /* Not there. Pending connections will be notified when - * connection_about_to_close_connection() cleans this conn up. */ - break; - case 400: - log_warn(LD_REND, - "http status 400 (%s). Dirserver didn't like our " - "rendezvous query?", escaped(reason)); - break; - default: - log_warn(LD_REND,"http status %d (%s) response unexpected while " - "fetching hidden service descriptor (server '%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - break; - } - } - - if (conn->base_.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) { - #define SEND_HS_DESC_FAILED_EVENT() ( \ - control_event_hs_descriptor_failed(conn->rend_data, \ - conn->identity_digest) ) - tor_assert(conn->rend_data); - log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " - "(%s))", - (int)body_len, status_code, escaped(reason)); - switch (status_code) { - case 200: - switch (rend_cache_store_v2_desc_as_client(body, conn->rend_data)) { - case -2: - log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. " - "Retrying at another directory."); - /* We'll retry when connection_about_to_close_connection() - * cleans this dir conn up. */ - SEND_HS_DESC_FAILED_EVENT(); - break; - case -1: - /* We already have a v0 descriptor here. Ignoring this one - * and _not_ performing another request. */ - log_info(LD_REND, "Successfully fetched v2 rendezvous " - "descriptor, but we already have a v0 descriptor."); - conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC; - break; - default: - /* success. notify pending connections about this. */ - log_info(LD_REND, "Successfully fetched v2 rendezvous " - "descriptor."); - control_event_hs_descriptor_received(conn->rend_data, - conn->identity_digest); - conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC; - rend_client_desc_trynow(conn->rend_data->onion_address); - break; - } - break; - case 404: - /* Not there. We'll retry when - * connection_about_to_close_connection() cleans this conn up. */ - log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: " - "Retrying at another directory."); - SEND_HS_DESC_FAILED_EVENT(); - break; - case 400: - log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " - "http status 400 (%s). Dirserver didn't like our " - "v2 rendezvous query? Retrying at another directory.", - escaped(reason)); - SEND_HS_DESC_FAILED_EVENT(); - break; - default: - log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " - "http status %d (%s) response unexpected while " - "fetching v2 hidden service descriptor (server '%s:%d'). " - "Retrying at another directory.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - SEND_HS_DESC_FAILED_EVENT(); - break; - } - } - - if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC || - conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) { - log_info(LD_REND,"Uploaded rendezvous descriptor (status %d " - "(%s))", - status_code, escaped(reason)); - switch (status_code) { - case 200: - log_info(LD_REND, - "Uploading rendezvous descriptor: finished with status " - "200 (%s)", escaped(reason)); - break; - case 400: - log_warn(LD_REND,"http status 400 (%s) response from dirserver " - "'%s:%d'. Malformed rendezvous descriptor?", - escaped(reason), conn->base_.address, conn->base_.port); - break; - default: - log_warn(LD_REND,"http status %d (%s) response unexpected (server " - "'%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); - break; - } - } - note_client_request(conn->base_.purpose, was_compressed, orig_len); - tor_free(body); tor_free(headers); tor_free(reason); - return 0; -} - -/** Called when a directory connection reaches EOF. */ -int -connection_dir_reached_eof(dir_connection_t *conn) -{ - int retval; - if (conn->base_.state != DIR_CONN_STATE_CLIENT_READING) { - log_info(LD_HTTP,"conn reached eof, not reading. [state=%d] Closing.", - conn->base_.state); - connection_close_immediate(TO_CONN(conn)); /* error: give up on flushing */ - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - - retval = connection_dir_client_reached_eof(conn); - if (retval == 0) /* success */ - conn->base_.state = DIR_CONN_STATE_CLIENT_FINISHED; - connection_mark_for_close(TO_CONN(conn)); - return retval; -} - -/** If any directory object is arriving, and it's over 10MB large, we're - * getting DoS'd. (As of 0.1.2.x, raw directories are about 1MB, and we never - * ask for more than 96 router descriptors at a time.) - */ -#define MAX_DIRECTORY_OBJECT_SIZE (10*(1<<20)) - -/** Read handler for directory connections. (That's connections to - * directory servers and connections at directory servers.) - */ -int -connection_dir_process_inbuf(dir_connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->base_.type == CONN_TYPE_DIR); - - /* Directory clients write, then read data until they receive EOF; - * directory servers read data until they get an HTTP command, then - * write their response (when it's finished flushing, they mark for - * close). - */ - - /* If we're on the dirserver side, look for a command. */ - if (conn->base_.state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) { - if (directory_handle_command(conn) < 0) { - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - return 0; - } - - if (connection_get_inbuf_len(TO_CONN(conn)) > MAX_DIRECTORY_OBJECT_SIZE) { - log_warn(LD_HTTP, "Too much data received from directory connection: " - "denial of service attempt, or you need to upgrade?"); - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - - if (!conn->base_.inbuf_reached_eof) - log_debug(LD_HTTP,"Got data, not eof. Leaving on inbuf."); - return 0; -} - -/** Called when we're about to finally unlink and free a directory connection: - * perform necessary accounting and cleanup */ -void -connection_dir_about_to_close(dir_connection_t *dir_conn) -{ - connection_t *conn = TO_CONN(dir_conn); - - if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) { - /* It's a directory connection and connecting or fetching - * failed: forget about this router, and maybe try again. */ - connection_dir_request_failed(dir_conn); - } - /* If we were trying to fetch a v2 rend desc and did not succeed, - * retry as needed. (If a fetch is successful, the connection state - * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that - * refetching is unnecessary.) */ - if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 && - dir_conn->rend_data && - strlen(dir_conn->rend_data->onion_address) == REND_SERVICE_ID_LEN_BASE32) - rend_client_refetch_v2_renddesc(dir_conn->rend_data); -} - -/** Create an http response for the client conn out of - * status and reason_phrase. Write it to conn. - */ -static void -write_http_status_line(dir_connection_t *conn, int status, - const char *reason_phrase) -{ - char buf[256]; - if (tor_snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\r\n\r\n", - status, reason_phrase ? reason_phrase : "OK") < 0) { - log_warn(LD_BUG,"status line too long."); - return; - } - connection_write_to_buf(buf, strlen(buf), TO_CONN(conn)); -} - -/** Write the header for an HTTP/1.0 response onto conn-\>outbuf, - * with type as the Content-Type. - * - * If length is nonnegative, it is the Content-Length. - * If encoding is provided, it is the Content-Encoding. - * If cache_lifetime is greater than 0, the content may be cached for - * up to cache_lifetime seconds. Otherwise, the content may not be cached. */ -static void -write_http_response_header_impl(dir_connection_t *conn, ssize_t length, - const char *type, const char *encoding, - const char *extra_headers, - long cache_lifetime) -{ - char date[RFC1123_TIME_LEN+1]; - char tmp[1024]; - char *cp; - time_t now = time(NULL); - - tor_assert(conn); - - format_rfc1123_time(date, now); - cp = tmp; - tor_snprintf(cp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\n", - date); - cp += strlen(tmp); - if (type) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type); - cp += strlen(cp); - } - if (!is_local_addr(&conn->base_.addr)) { - /* Don't report the source address for a nearby/private connection. - * Otherwise we tend to mis-report in cases where incoming ports are - * being forwarded to a Tor server running behind the firewall. */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - X_ADDRESS_HEADER "%s\r\n", conn->base_.address); - cp += strlen(cp); - } - if (encoding) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Encoding: %s\r\n", encoding); - cp += strlen(cp); - } - if (length >= 0) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Length: %ld\r\n", (long)length); - cp += strlen(cp); - } - if (cache_lifetime > 0) { - char expbuf[RFC1123_TIME_LEN+1]; - format_rfc1123_time(expbuf, now + cache_lifetime); - /* We could say 'Cache-control: max-age=%d' here if we start doing - * http/1.1 */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Expires: %s\r\n", expbuf); - cp += strlen(cp); - } else if (cache_lifetime == 0) { - /* We could say 'Cache-control: no-cache' here if we start doing - * http/1.1 */ - strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); - } - if (extra_headers) { - strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); - } - if (sizeof(tmp)-(cp-tmp) > 3) - memcpy(cp, "\r\n", 3); - else - tor_assert(0); - connection_write_to_buf(tmp, strlen(tmp), TO_CONN(conn)); -} - -/** As write_http_response_header_impl, but sets encoding and content-typed - * based on whether the response will be compressed or not. */ -static void -write_http_response_header(dir_connection_t *conn, ssize_t length, - int compressed, long cache_lifetime) -{ - write_http_response_header_impl(conn, length, - compressed?"application/octet-stream":"text/plain", - compressed?"deflate":"identity", - NULL, - cache_lifetime); -} - -#if defined(INSTRUMENT_DOWNLOADS) || defined(RUNNING_DOXYGEN) -/* DOCDOC */ -typedef struct request_t { - uint64_t bytes; /**< How many bytes have we transferred? */ - uint64_t count; /**< How many requests have we made? */ -} request_t; - -/** Map used to keep track of how much data we've up/downloaded in what kind - * of request. Maps from request type to pointer to request_t. */ -static strmap_t *request_map = NULL; - -/** Record that a client request of purpose was made, and that - * bytes bytes of possibly compressed data were sent/received. - * Used to keep track of how much we've up/downloaded in what kind of - * request. */ -static void -note_client_request(int purpose, int compressed, size_t bytes) -{ - char *key; - const char *kind = NULL; - switch (purpose) { - case DIR_PURPOSE_FETCH_CONSENSUS: kind = "dl/consensus"; break; - case DIR_PURPOSE_FETCH_CERTIFICATE: kind = "dl/cert"; break; - case DIR_PURPOSE_FETCH_STATUS_VOTE: kind = "dl/vote"; break; - case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: kind = "dl/detached_sig"; - break; - case DIR_PURPOSE_FETCH_SERVERDESC: kind = "dl/server"; break; - case DIR_PURPOSE_FETCH_EXTRAINFO: kind = "dl/extra"; break; - case DIR_PURPOSE_UPLOAD_DIR: kind = "dl/ul-dir"; break; - case DIR_PURPOSE_UPLOAD_VOTE: kind = "dl/ul-vote"; break; - case DIR_PURPOSE_UPLOAD_SIGNATURES: kind = "dl/ul-sig"; break; - case DIR_PURPOSE_FETCH_RENDDESC: kind = "dl/rend"; break; - case DIR_PURPOSE_FETCH_RENDDESC_V2: kind = "dl/rend2"; break; - case DIR_PURPOSE_UPLOAD_RENDDESC: kind = "dl/ul-rend"; break; - case DIR_PURPOSE_UPLOAD_RENDDESC_V2: kind = "dl/ul-rend2"; break; - } - if (kind) { - tor_asprintf(&key, "%s%s", kind, compressed?".z":""); - } else { - tor_asprintf(&key, "unknown purpose (%d)%s", - purpose, compressed?".z":""); - } - note_request(key, bytes); - tor_free(key); -} - -/** Helper: initialize the request map to instrument downloads. */ -static void -ensure_request_map_initialized(void) -{ - if (!request_map) - request_map = strmap_new(); -} - -/** Called when we just transmitted or received bytes worth of data - * because of a request of type key (an arbitrary identifier): adds - * bytes to the total associated with key. */ -void -note_request(const char *key, size_t bytes) -{ - request_t *r; - ensure_request_map_initialized(); - - r = strmap_get(request_map, key); - if (!r) { - r = tor_malloc_zero(sizeof(request_t)); - strmap_set(request_map, key, r); - } - r->bytes += bytes; - r->count++; -} - -/** Return a newly allocated string holding a summary of bytes used per - * request type. */ -char * -directory_dump_request_log(void) -{ - smartlist_t *lines; - char *result; - strmap_iter_t *iter; - - ensure_request_map_initialized(); - - lines = smartlist_new(); - - for (iter = strmap_iter_init(request_map); - !strmap_iter_done(iter); - iter = strmap_iter_next(request_map, iter)) { - const char *key; - void *val; - request_t *r; - strmap_iter_get(iter, &key, &val); - r = val; - smartlist_add_asprintf(lines, "%s "U64_FORMAT" "U64_FORMAT"\n", - key, U64_PRINTF_ARG(r->bytes), U64_PRINTF_ARG(r->count)); - } - smartlist_sort_strings(lines); - result = smartlist_join_strings(lines, "", 0, NULL); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); - return result; -} -#else -static void -note_client_request(int purpose, int compressed, size_t bytes) -{ - (void)purpose; - (void)compressed; - (void)bytes; -} - -void -note_request(const char *key, size_t bytes) -{ - (void)key; - (void)bytes; -} - -char * -directory_dump_request_log(void) -{ - return tor_strdup("Not supported."); -} -#endif - -/** Decide whether a client would accept the consensus we have. - * - * Clients can say they only want a consensus if it's signed by more - * than half the authorities in a list. They pass this list in - * the url as "...consensus/fpr+fpr+fpr". - * - * fpr may be an abbreviated fingerprint, i.e. only a left substring - * of the full authority identity digest. (Only strings of even length, - * i.e. encodings of full bytes, are handled correctly. In the case - * of an odd number of hex digits the last one is silently ignored.) - * - * Returns 1 if more than half of the requested authorities signed the - * consensus, 0 otherwise. - */ -int -client_likes_consensus(networkstatus_t *v, const char *want_url) -{ - smartlist_t *want_authorities = smartlist_new(); - int need_at_least; - int have = 0; - - dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0); - need_at_least = smartlist_len(want_authorities)/2+1; - SMARTLIST_FOREACH_BEGIN(want_authorities, const char *, d) { - char want_digest[DIGEST_LEN]; - size_t want_len = strlen(d)/2; - if (want_len > DIGEST_LEN) - want_len = DIGEST_LEN; - - if (base16_decode(want_digest, DIGEST_LEN, d, want_len*2) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_DIR, - "Failed to decode requested authority digest %s.", d); - continue; - }; - - SMARTLIST_FOREACH_BEGIN(v->voters, networkstatus_voter_info_t *, vi) { - if (smartlist_len(vi->sigs) && - tor_memeq(vi->identity_digest, want_digest, want_len)) { - have++; - break; - }; - } SMARTLIST_FOREACH_END(vi); - - /* early exit, if we already have enough */ - if (have >= need_at_least) - break; - } SMARTLIST_FOREACH_END(d); - - SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d)); - smartlist_free(want_authorities); - return (have >= need_at_least); -} - -/** Helper function: called when a dirserver gets a complete HTTP GET - * request. Look for a request for a directory or for a rendezvous - * service descriptor. On finding one, write a response into - * conn-\>outbuf. If the request is unrecognized, send a 400. - * Always return 0. */ -static int -directory_handle_command_get(dir_connection_t *conn, const char *headers, - const char *req_body, size_t req_body_len) -{ - size_t dlen; - char *url, *url_mem, *header; - const or_options_t *options = get_options(); - time_t if_modified_since = 0; - int compressed; - size_t url_len; - - /* We ignore the body of a GET request. */ - (void)req_body; - (void)req_body_len; - - log_debug(LD_DIRSERV,"Received GET command."); - - conn->base_.state = DIR_CONN_STATE_SERVER_WRITING; - - if (parse_http_url(headers, &url) < 0) { - write_http_status_line(conn, 400, "Bad request"); - return 0; - } - if ((header = http_get_header(headers, "If-Modified-Since: "))) { - struct tm tm; - if (parse_http_time(header, &tm) == 0) { - 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. */ - tor_free(header); - } - log_debug(LD_DIRSERV,"rewritten url as '%s'.", url); - - url_mem = url; - url_len = strlen(url); - compressed = url_len > 2 && !strcmp(url+url_len-2, ".z"); - if (compressed) { - url[url_len-2] = '\0'; - url_len -= 2; - } - - if (!strcmp(url,"/tor/")) { - const char *frontpage = get_dirportfrontpage(); - - if (frontpage) { - dlen = strlen(frontpage); - /* Let's return a disclaimer page (users shouldn't use V1 anymore, - and caches don't fetch '/', so this is safe). */ - - /* [We don't check for write_bucket_low here, since we want to serve - * this page no matter what.] */ - note_request(url, dlen); - write_http_response_header_impl(conn, dlen, "text/html", "identity", - NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME); - connection_write_to_buf(frontpage, dlen, TO_CONN(conn)); - goto done; - } - /* if no disclaimer file, fall through and continue */ - } - - if (!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir")) { /* v1 dir fetch */ - cached_dir_t *d = dirserv_get_directory(); - - if (!d) { - log_info(LD_DIRSERV,"Client asked for the mirrored directory, but we " - "don't have a good one yet. Sending 503 Dir not available."); - write_http_status_line(conn, 503, "Directory unavailable"); - goto done; - } - if (d->published < if_modified_since) { - write_http_status_line(conn, 304, "Not modified"); - goto done; - } - - dlen = compressed ? d->dir_z_len : d->dir_len; - - if (global_write_bucket_low(TO_CONN(conn), dlen, 1)) { - log_debug(LD_DIRSERV, - "Client asked for the mirrored directory, but we've been " - "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); - goto done; - } - - note_request(url, dlen); - - log_debug(LD_DIRSERV,"Dumping %sdirectory to client.", - compressed?"compressed ":""); - write_http_response_header(conn, dlen, compressed, - FULL_DIR_CACHE_LIFETIME); - conn->cached_dir = d; - conn->cached_dir_offset = 0; - if (!compressed) - conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); - ++d->refcnt; - - /* Prime the connection with some data. */ - conn->dir_spool_src = DIR_SPOOL_CACHED_DIR; - connection_dirserv_flushed_some(conn); - goto done; - } - - if (!strcmp(url,"/tor/running-routers")) { /* running-routers fetch */ - cached_dir_t *d = dirserv_get_runningrouters(); - if (!d) { - write_http_status_line(conn, 503, "Directory unavailable"); - goto done; - } - if (d->published < if_modified_since) { - write_http_status_line(conn, 304, "Not modified"); - goto done; - } - dlen = compressed ? d->dir_z_len : d->dir_len; - - if (global_write_bucket_low(TO_CONN(conn), dlen, 1)) { - log_info(LD_DIRSERV, - "Client asked for running-routers, but we've been " - "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); - goto done; - } - note_request(url, dlen); - write_http_response_header(conn, dlen, compressed, - RUNNINGROUTERS_CACHE_LIFETIME); - connection_write_to_buf(compressed ? d->dir_z : d->dir, dlen, - TO_CONN(conn)); - goto done; - } - - if (!strcmpstart(url, "/tor/status-vote/current/consensus")) { - /* v3 network status fetch. */ - smartlist_t *dir_fps = smartlist_new(); - const char *request_type = NULL; - long lifetime = NETWORKSTATUS_CACHE_LIFETIME; - - if (1) { - networkstatus_t *v; - time_t now = time(NULL); - const char *want_fps = NULL; - char *flavor = NULL; - int flav = FLAV_NS; - #define CONSENSUS_URL_PREFIX "/tor/status-vote/current/consensus/" - #define CONSENSUS_FLAVORED_PREFIX "/tor/status-vote/current/consensus-" - /* figure out the flavor if any, and who we wanted to sign the thing */ - if (!strcmpstart(url, CONSENSUS_FLAVORED_PREFIX)) { - const char *f, *cp; - f = url + strlen(CONSENSUS_FLAVORED_PREFIX); - cp = strchr(f, '/'); - if (cp) { - want_fps = cp+1; - flavor = tor_strndup(f, cp-f); - } else { - flavor = tor_strdup(f); - } - flav = networkstatus_parse_flavor_name(flavor); - if (flav < 0) - flav = FLAV_NS; - } else { - if (!strcmpstart(url, CONSENSUS_URL_PREFIX)) - want_fps = url+strlen(CONSENSUS_URL_PREFIX); - } - - v = networkstatus_get_latest_consensus_by_flavor(flav); - - if (v && want_fps && - !client_likes_consensus(v, want_fps)) { - write_http_status_line(conn, 404, "Consensus not signed by sufficient " - "number of requested authorities"); - smartlist_free(dir_fps); - geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS); - tor_free(flavor); - goto done; - } - - { - char *fp = tor_malloc_zero(DIGEST_LEN); - if (flavor) - strlcpy(fp, flavor, DIGEST_LEN); - tor_free(flavor); - smartlist_add(dir_fps, fp); - } - request_type = compressed?"v3.z":"v3"; - lifetime = (v && v->fresh_until > now) ? v->fresh_until - now : 0; - } - - if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */ - write_http_status_line(conn, 503, "Network status object unavailable"); - smartlist_free(dir_fps); - geoip_note_ns_response(GEOIP_REJECT_UNAVAILABLE); - goto done; - } - - if (!dirserv_remove_old_statuses(dir_fps, if_modified_since)) { - write_http_status_line(conn, 404, "Not found"); - SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp)); - smartlist_free(dir_fps); - geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); - goto done; - } else if (!smartlist_len(dir_fps)) { - write_http_status_line(conn, 304, "Not modified"); - SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp)); - smartlist_free(dir_fps); - geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED); - goto done; - } - - dlen = dirserv_estimate_data_size(dir_fps, 0, compressed); - if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) { - log_debug(LD_DIRSERV, - "Client asked for network status lists, but we've been " - "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); - SMARTLIST_FOREACH(dir_fps, char *, fp, tor_free(fp)); - smartlist_free(dir_fps); - - geoip_note_ns_response(GEOIP_REJECT_BUSY); - goto done; - } - - if (1) { - struct in_addr in; - tor_addr_t addr; - if (tor_inet_aton((TO_CONN(conn))->address, &in)) { - tor_addr_from_ipv4h(&addr, ntohl(in.s_addr)); - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, - &addr, NULL, - time(NULL)); - geoip_note_ns_response(GEOIP_SUCCESS); - /* Note that a request for a network status has started, so that we - * can measure the download time later on. */ - if (conn->dirreq_id) - geoip_start_dirreq(conn->dirreq_id, dlen, DIRREQ_TUNNELED); - else - geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen, - DIRREQ_DIRECT); - } - } - - // note_request(request_type,dlen); - (void) request_type; - write_http_response_header(conn, -1, compressed, - smartlist_len(dir_fps) == 1 ? lifetime : 0); - conn->fingerprint_stack = dir_fps; - if (! compressed) - conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); - - /* Prime the connection with some data. */ - conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS; - connection_dirserv_flushed_some(conn); - goto done; - } - - if (!strcmpstart(url,"/tor/status-vote/current/") || - !strcmpstart(url,"/tor/status-vote/next/")) { - /* XXXX If-modified-since is only implemented for the current - * consensus: that's probably fine, since it's the only vote document - * people fetch much. */ - int current; - ssize_t body_len = 0; - ssize_t estimated_len = 0; - smartlist_t *items = smartlist_new(); - smartlist_t *dir_items = smartlist_new(); - int lifetime = 60; /* XXXX023 should actually use vote intervals. */ - url += strlen("/tor/status-vote/"); - current = !strcmpstart(url, "current/"); - url = strchr(url, '/'); - tor_assert(url); - ++url; - if (!strcmp(url, "consensus")) { - const char *item; - tor_assert(!current); /* we handle current consensus specially above, - * since it wants to be spooled. */ - if ((item = dirvote_get_pending_consensus(FLAV_NS))) - smartlist_add(items, (char*)item); - } else if (!current && !strcmp(url, "consensus-signatures")) { - /* XXXX the spec says that we should implement - * current/consensus-signatures too. It doesn't seem to be needed, - * though. */ - const char *item; - if ((item=dirvote_get_pending_detached_signatures())) - smartlist_add(items, (char*)item); - } else if (!strcmp(url, "authority")) { - const cached_dir_t *d; - int flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - if ((d=dirvote_get_vote(NULL, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - } else { - const cached_dir_t *d; - smartlist_t *fps = smartlist_new(); - int flags; - if (!strcmpstart(url, "d/")) { - url += 2; - flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS; - } else { - flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - } - dir_split_resource_into_fingerprints(url, fps, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(fps, char *, fp, { - if ((d = dirvote_get_vote(fp, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - tor_free(fp); - }); - smartlist_free(fps); - } - if (!smartlist_len(dir_items) && !smartlist_len(items)) { - write_http_status_line(conn, 404, "Not found"); - goto vote_done; - } - SMARTLIST_FOREACH(dir_items, cached_dir_t *, d, - body_len += compressed ? d->dir_z_len : d->dir_len); - estimated_len += body_len; - SMARTLIST_FOREACH(items, const char *, item, { - size_t ln = strlen(item); - if (compressed) { - estimated_len += ln/2; - } else { - body_len += ln; estimated_len += ln; - } - }); - - if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later."); - goto vote_done; - } - write_http_response_header(conn, body_len ? body_len : -1, compressed, - lifetime); - - if (smartlist_len(items)) { - if (compressed) { - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); - SMARTLIST_FOREACH(items, const char *, c, - connection_write_to_buf_zlib(c, strlen(c), conn, 0)); - connection_write_to_buf_zlib("", 0, conn, 1); - } else { - SMARTLIST_FOREACH(items, const char *, c, - connection_write_to_buf(c, strlen(c), TO_CONN(conn))); - } - } else { - SMARTLIST_FOREACH(dir_items, cached_dir_t *, d, - connection_write_to_buf(compressed ? d->dir_z : d->dir, - compressed ? d->dir_z_len : d->dir_len, - TO_CONN(conn))); - } - vote_done: - smartlist_free(items); - smartlist_free(dir_items); - goto done; - } - - if (!strcmpstart(url, "/tor/micro/d/")) { - smartlist_t *fps = smartlist_new(); - - dir_split_resource_into_fingerprints(url+strlen("/tor/micro/d/"), - fps, NULL, - DSR_DIGEST256|DSR_BASE64|DSR_SORT_UNIQ); - - if (!dirserv_have_any_microdesc(fps)) { - write_http_status_line(conn, 404, "Not found"); - SMARTLIST_FOREACH(fps, char *, fp, tor_free(fp)); - smartlist_free(fps); - goto done; - } - dlen = dirserv_estimate_microdesc_size(fps, compressed); - if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) { - log_info(LD_DIRSERV, - "Client asked for server descriptors, but we've been " - "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); - SMARTLIST_FOREACH(fps, char *, fp, tor_free(fp)); - smartlist_free(fps); - goto done; - } - - write_http_response_header(conn, -1, compressed, MICRODESC_CACHE_LIFETIME); - conn->dir_spool_src = DIR_SPOOL_MICRODESC; - conn->fingerprint_stack = fps; - - if (compressed) - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); - - connection_dirserv_flushed_some(conn); - goto done; - } - - if (!strcmpstart(url,"/tor/server/") || - (!options->BridgeAuthoritativeDir && - !options->BridgeRelay && !strcmpstart(url,"/tor/extra/"))) { - int res; - const char *msg; - const char *request_type = NULL; - int cache_lifetime = 0; - int is_extra = !strcmpstart(url,"/tor/extra/"); - url += is_extra ? strlen("/tor/extra/") : strlen("/tor/server/"); - conn->fingerprint_stack = smartlist_new(); - res = dirserv_get_routerdesc_fingerprints(conn->fingerprint_stack, url, - &msg, - !connection_dir_is_encrypted(conn), - is_extra); - - if (!strcmpstart(url, "fp/")) { - request_type = compressed?"/tor/server/fp.z":"/tor/server/fp"; - if (smartlist_len(conn->fingerprint_stack) == 1) - cache_lifetime = ROUTERDESC_CACHE_LIFETIME; - } else if (!strcmpstart(url, "authority")) { - request_type = compressed?"/tor/server/authority.z": - "/tor/server/authority"; - cache_lifetime = ROUTERDESC_CACHE_LIFETIME; - } else if (!strcmpstart(url, "all")) { - request_type = compressed?"/tor/server/all.z":"/tor/server/all"; - cache_lifetime = FULL_DIR_CACHE_LIFETIME; - } else if (!strcmpstart(url, "d/")) { - request_type = compressed?"/tor/server/d.z":"/tor/server/d"; - if (smartlist_len(conn->fingerprint_stack) == 1) - cache_lifetime = ROUTERDESC_BY_DIGEST_CACHE_LIFETIME; - } else { - request_type = "/tor/server/?"; - } - (void) request_type; /* usable for note_request. */ - if (!strcmpstart(url, "d/")) - conn->dir_spool_src = - is_extra ? DIR_SPOOL_EXTRA_BY_DIGEST : DIR_SPOOL_SERVER_BY_DIGEST; - else - conn->dir_spool_src = - is_extra ? DIR_SPOOL_EXTRA_BY_FP : DIR_SPOOL_SERVER_BY_FP; - - if (!dirserv_have_any_serverdesc(conn->fingerprint_stack, - conn->dir_spool_src)) { - res = -1; - msg = "Not found"; - } - - if (res < 0) - write_http_status_line(conn, 404, msg); - else { - dlen = dirserv_estimate_data_size(conn->fingerprint_stack, - 1, compressed); - if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) { - log_info(LD_DIRSERV, - "Client asked for server descriptors, but we've been " - "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); - conn->dir_spool_src = DIR_SPOOL_NONE; - goto done; - } - write_http_response_header(conn, -1, compressed, cache_lifetime); - if (compressed) - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); - /* Prime the connection with some data. */ - connection_dirserv_flushed_some(conn); - } - goto done; - } - - if (!strcmpstart(url,"/tor/keys/")) { - smartlist_t *certs = smartlist_new(); - ssize_t len = -1; - if (!strcmp(url, "/tor/keys/all")) { - authority_cert_get_all(certs); - } else if (!strcmp(url, "/tor/keys/authority")) { - authority_cert_t *cert = get_my_v3_authority_cert(); - if (cert) - smartlist_add(certs, cert); - } else if (!strcmpstart(url, "/tor/keys/fp/")) { - smartlist_t *fps = smartlist_new(); - dir_split_resource_into_fingerprints(url+strlen("/tor/keys/fp/"), - fps, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(fps, char *, d, { - authority_cert_t *c = authority_cert_get_newest_by_id(d); - if (c) smartlist_add(certs, c); - tor_free(d); - }); - smartlist_free(fps); - } else if (!strcmpstart(url, "/tor/keys/sk/")) { - smartlist_t *fps = smartlist_new(); - dir_split_resource_into_fingerprints(url+strlen("/tor/keys/sk/"), - fps, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(fps, char *, d, { - authority_cert_t *c = authority_cert_get_by_sk_digest(d); - if (c) smartlist_add(certs, c); - tor_free(d); - }); - smartlist_free(fps); - } else if (!strcmpstart(url, "/tor/keys/fp-sk/")) { - smartlist_t *fp_sks = smartlist_new(); - dir_split_resource_into_fingerprint_pairs(url+strlen("/tor/keys/fp-sk/"), - fp_sks); - SMARTLIST_FOREACH(fp_sks, fp_pair_t *, pair, { - authority_cert_t *c = authority_cert_get_by_digests(pair->first, - pair->second); - if (c) smartlist_add(certs, c); - tor_free(pair); - }); - smartlist_free(fp_sks); - } else { - write_http_status_line(conn, 400, "Bad request"); - goto keys_done; - } - if (!smartlist_len(certs)) { - write_http_status_line(conn, 404, "Not found"); - goto keys_done; - } - SMARTLIST_FOREACH(certs, authority_cert_t *, c, - if (c->cache_info.published_on < if_modified_since) - SMARTLIST_DEL_CURRENT(certs, c)); - if (!smartlist_len(certs)) { - write_http_status_line(conn, 304, "Not modified"); - goto keys_done; - } - len = 0; - SMARTLIST_FOREACH(certs, authority_cert_t *, c, - len += c->cache_info.signed_descriptor_len); - - if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later."); - goto keys_done; - } - - write_http_response_header(conn, compressed?-1:len, compressed, 60*60); - if (compressed) { - conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); - SMARTLIST_FOREACH(certs, authority_cert_t *, c, - connection_write_to_buf_zlib(c->cache_info.signed_descriptor_body, - c->cache_info.signed_descriptor_len, - conn, 0)); - connection_write_to_buf_zlib("", 0, conn, 1); - } else { - SMARTLIST_FOREACH(certs, authority_cert_t *, c, - connection_write_to_buf(c->cache_info.signed_descriptor_body, - c->cache_info.signed_descriptor_len, - TO_CONN(conn))); - } - keys_done: - smartlist_free(certs); - goto done; - } - - if (options->HidServDirectoryV2 && - connection_dir_is_encrypted(conn) && - !strcmpstart(url,"/tor/rendezvous2/")) { - /* Handle v2 rendezvous descriptor fetch request. */ - const char *descp; - const char *query = url + strlen("/tor/rendezvous2/"); - if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) { - log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'", - safe_str(query)); - switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) { - case 1: /* valid */ - write_http_response_header(conn, strlen(descp), 0, 0); - connection_write_to_buf(descp, strlen(descp), TO_CONN(conn)); - break; - case 0: /* well-formed but not present */ - write_http_status_line(conn, 404, "Not found"); - break; - case -1: /* not well-formed */ - write_http_status_line(conn, 400, "Bad request"); - break; - } - } else { /* not well-formed */ - write_http_status_line(conn, 400, "Bad request"); - } - goto done; - } - - if (options->HSAuthoritativeDir && !strcmpstart(url,"/tor/rendezvous/")) { - /* rendezvous descriptor fetch */ - const char *descp; - size_t desc_len; - const char *query = url+strlen("/tor/rendezvous/"); - - log_info(LD_REND, "Handling rendezvous descriptor get"); - switch (rend_cache_lookup_desc(query, 0, &descp, &desc_len)) { - case 1: /* valid */ - write_http_response_header_impl(conn, desc_len, - "application/octet-stream", - NULL, NULL, 0); - note_request("/tor/rendezvous?/", desc_len); - /* need to send descp separately, because it may include NULs */ - connection_write_to_buf(descp, desc_len, TO_CONN(conn)); - break; - case 0: /* well-formed but not present */ - write_http_status_line(conn, 404, "Not found"); - break; - case -1: /* not well-formed */ - write_http_status_line(conn, 400, "Bad request"); - break; - } - goto done; - } - - if (options->BridgeAuthoritativeDir && - options->BridgePassword_AuthDigest_ && - connection_dir_is_encrypted(conn) && - !strcmp(url,"/tor/networkstatus-bridges")) { - char *status; - char digest[DIGEST256_LEN]; - - header = http_get_header(headers, "Authorization: Basic "); - if (header) - crypto_digest256(digest, header, strlen(header), DIGEST_SHA256); - - /* now make sure the password is there and right */ - if (!header || - tor_memneq(digest, - options->BridgePassword_AuthDigest_, DIGEST256_LEN)) { - write_http_status_line(conn, 404, "Not found"); - tor_free(header); - goto done; - } - tor_free(header); - - /* all happy now. send an answer. */ - status = networkstatus_getinfo_by_purpose("bridge", time(NULL)); - dlen = strlen(status); - write_http_response_header(conn, dlen, 0, 0); - connection_write_to_buf(status, dlen, TO_CONN(conn)); - tor_free(status); - goto done; - } - - if (!strcmpstart(url,"/tor/bytes.txt")) { - char *bytes = directory_dump_request_log(); - size_t len = strlen(bytes); - write_http_response_header(conn, len, 0, 0); - connection_write_to_buf(bytes, len, TO_CONN(conn)); - tor_free(bytes); - goto done; - } - - if (!strcmp(url,"/tor/robots.txt")) { /* /robots.txt will have been - rewritten to /tor/robots.txt */ - char robots[] = "User-agent: *\r\nDisallow: /\r\n"; - size_t len = strlen(robots); - write_http_response_header(conn, len, 0, ROBOTS_CACHE_LIFETIME); - connection_write_to_buf(robots, len, TO_CONN(conn)); - goto done; - } - - if (!strcmp(url,"/tor/dbg-stability.txt")) { - const char *stability; - size_t len; - if (options->BridgeAuthoritativeDir || - ! authdir_mode_tests_reachability(options) || - ! (stability = rep_hist_get_router_stability_doc(time(NULL)))) { - write_http_status_line(conn, 404, "Not found."); - goto done; - } - - len = strlen(stability); - write_http_response_header(conn, len, 0, 0); - connection_write_to_buf(stability, len, TO_CONN(conn)); - goto done; - } - -#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) -#define ADD_MALLINFO_LINE(x) do { \ - smartlist_add_asprintf(lines, "%s %d\n", #x, mi.x); \ - }while(0); - - if (!strcmp(url,"/tor/mallinfo.txt") && - (tor_addr_eq_ipv4h(&conn->base_.addr, 0x7f000001ul))) { - char *result; - size_t len; - struct mallinfo mi; - smartlist_t *lines; - - memset(&mi, 0, sizeof(mi)); - mi = mallinfo(); - lines = smartlist_new(); - - ADD_MALLINFO_LINE(arena) - ADD_MALLINFO_LINE(ordblks) - ADD_MALLINFO_LINE(smblks) - ADD_MALLINFO_LINE(hblks) - ADD_MALLINFO_LINE(hblkhd) - ADD_MALLINFO_LINE(usmblks) - ADD_MALLINFO_LINE(fsmblks) - ADD_MALLINFO_LINE(uordblks) - ADD_MALLINFO_LINE(fordblks) - ADD_MALLINFO_LINE(keepcost) - - result = smartlist_join_strings(lines, "", 0, NULL); - SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); - smartlist_free(lines); - - len = strlen(result); - write_http_response_header(conn, len, 0, 0); - connection_write_to_buf(result, len, TO_CONN(conn)); - tor_free(result); - goto done; - } -#endif - - /* we didn't recognize the url */ - write_http_status_line(conn, 404, "Not found"); - - done: - tor_free(url_mem); - return 0; -} - -/** Helper function: called when a dirserver gets a complete HTTP POST - * request. Look for an uploaded server descriptor or rendezvous - * service descriptor. On finding one, process it and write a - * response into conn-\>outbuf. If the request is unrecognized, send a - * 400. Always return 0. */ -static int -directory_handle_command_post(dir_connection_t *conn, const char *headers, - const char *body, size_t body_len) -{ - char *url = NULL; - const or_options_t *options = get_options(); - - log_debug(LD_DIRSERV,"Received POST command."); - - conn->base_.state = DIR_CONN_STATE_SERVER_WRITING; - - if (parse_http_url(headers, &url) < 0) { - write_http_status_line(conn, 400, "Bad request"); - return 0; - } - log_debug(LD_DIRSERV,"rewritten url as '%s'.", url); - - /* Handle v2 rendezvous service publish request. */ - if (options->HidServDirectoryV2 && - connection_dir_is_encrypted(conn) && - !strcmpstart(url,"/tor/rendezvous2/publish")) { - switch (rend_cache_store_v2_desc_as_dir(body)) { - case -2: - log_info(LD_REND, "Rejected v2 rend descriptor (length %d) from %s " - "since we're not currently a hidden service directory.", - (int)body_len, conn->base_.address); - write_http_status_line(conn, 503, "Currently not acting as v2 " - "hidden service directory"); - break; - case -1: - log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.", - (int)body_len, conn->base_.address); - write_http_status_line(conn, 400, - "Invalid v2 service descriptor rejected"); - break; - default: - write_http_status_line(conn, 200, "Service descriptor (v2) stored"); - log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted"); - } - goto done; - } - - if (!authdir_mode(options)) { - /* we just provide cached directories; we don't want to - * receive anything. */ - write_http_status_line(conn, 400, "Nonauthoritative directory does not " - "accept posted server descriptors"); - goto done; - } - - if (authdir_mode_handles_descs(options, -1) && - !strcmp(url,"/tor/")) { /* server descriptor post */ - const char *msg = "[None]"; - uint8_t purpose = authdir_mode_bridge(options) ? - ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL; - was_router_added_t r = dirserv_add_multiple_descriptors(body, purpose, - conn->base_.address, &msg); - tor_assert(msg); - if (WRA_WAS_ADDED(r)) - dirserv_get_directory(); /* rebuild and write to disk */ - - if (r == ROUTER_ADDED_NOTIFY_GENERATOR) { - /* Accepted with a message. */ - log_info(LD_DIRSERV, - "Problematic router descriptor or extra-info from %s " - "(\"%s\").", - conn->base_.address, msg); - write_http_status_line(conn, 400, msg); - } else if (r == ROUTER_ADDED_SUCCESSFULLY) { - write_http_status_line(conn, 200, msg); - } else if (WRA_WAS_OUTDATED(r)) { - write_http_response_header_impl(conn, -1, NULL, NULL, - "X-Descriptor-Not-New: Yes\r\n", -1); - } else { - log_info(LD_DIRSERV, - "Rejected router descriptor or extra-info from %s " - "(\"%s\").", - conn->base_.address, msg); - write_http_status_line(conn, 400, msg); - } - goto done; - } - - if (options->HSAuthoritativeDir && - !strcmpstart(url,"/tor/rendezvous/publish")) { - /* rendezvous descriptor post */ - log_info(LD_REND, "Handling rendezvous descriptor post."); - if (rend_cache_store(body, body_len, 1, NULL) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV, - "Rejected rend descriptor (length %d) from %s.", - (int)body_len, conn->base_.address); - write_http_status_line(conn, 400, - "Invalid v0 service descriptor rejected"); - } else { - write_http_status_line(conn, 200, "Service descriptor (v0) stored"); - } - goto done; - } - - if (authdir_mode_v3(options) && - !strcmp(url,"/tor/post/vote")) { /* v3 networkstatus vote */ - const char *msg = "OK"; - int status; - if (dirvote_add_vote(body, &msg, &status)) { - write_http_status_line(conn, status, "Vote stored"); - } else { - tor_assert(msg); - log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").", - conn->base_.address, msg); - write_http_status_line(conn, status, msg); - } - goto done; - } - - if (authdir_mode_v3(options) && - !strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */ - const char *msg = NULL; - if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) { - write_http_status_line(conn, 200, msg?msg:"Signatures stored"); - } else { - log_warn(LD_DIR, "Unable to store signatures posted by %s: %s", - conn->base_.address, msg?msg:"???"); - write_http_status_line(conn, 400, msg?msg:"Unable to store signatures"); - } - goto done; - } - - /* we didn't recognize the url */ - write_http_status_line(conn, 404, "Not found"); - - done: - tor_free(url); - return 0; -} - -/** Called when a dirserver receives data on a directory connection; - * looks for an HTTP request. If the request is complete, remove it - * from the inbuf, try to process it; otherwise, leave it on the - * buffer. Return a 0 on success, or -1 on error. - */ -static int -directory_handle_command(dir_connection_t *conn) -{ - char *headers=NULL, *body=NULL; - size_t body_len=0; - int r; - - tor_assert(conn); - tor_assert(conn->base_.type == CONN_TYPE_DIR); - - switch (connection_fetch_from_buf_http(TO_CONN(conn), - &headers, MAX_HEADERS_SIZE, - &body, &body_len, MAX_DIR_UL_SIZE, 0)) { - case -1: /* overflow */ - log_warn(LD_DIRSERV, - "Request too large from address '%s' to DirPort. Closing.", - safe_str(conn->base_.address)); - return -1; - case 0: - log_debug(LD_DIRSERV,"command not all here yet."); - return 0; - /* case 1, fall through */ - } - - http_set_address_origin(headers, TO_CONN(conn)); - //log_debug(LD_DIRSERV,"headers %s, body %s.", headers, body); - - if (!strncasecmp(headers,"GET",3)) - r = directory_handle_command_get(conn, headers, body, body_len); - else if (!strncasecmp(headers,"POST",4)) - r = directory_handle_command_post(conn, headers, body, body_len); - else { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Got headers %s with unknown command. Closing.", - escaped(headers)); - r = -1; - } - - tor_free(headers); tor_free(body); - return r; -} - -/** Write handler for directory connections; called when all data has - * been flushed. Close the connection or wait for a response as - * appropriate. - */ -int -connection_dir_finished_flushing(dir_connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->base_.type == CONN_TYPE_DIR); - - /* Note that we have finished writing the directory response. For direct - * connections this means we're done, for tunneled connections its only - * an intermediate step. */ - if (conn->dirreq_id) - geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED, - DIRREQ_FLUSHING_DIR_CONN_FINISHED); - else - geoip_change_dirreq_state(TO_CONN(conn)->global_identifier, - DIRREQ_DIRECT, - DIRREQ_FLUSHING_DIR_CONN_FINISHED); - switch (conn->base_.state) { - case DIR_CONN_STATE_CONNECTING: - case DIR_CONN_STATE_CLIENT_SENDING: - log_debug(LD_DIR,"client finished sending command."); - conn->base_.state = DIR_CONN_STATE_CLIENT_READING; - return 0; - case DIR_CONN_STATE_SERVER_WRITING: - if (conn->dir_spool_src != DIR_SPOOL_NONE) { -#ifdef USE_BUFFEREVENTS - /* This can happen with paired bufferevents, since a paired connection - * can flush immediately when you write to it, making the subsequent - * check in connection_handle_write_cb() decide that the connection - * is flushed. */ - log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling."); -#else - log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!"); - connection_mark_for_close(TO_CONN(conn)); -#endif - } else { - log_debug(LD_DIRSERV, "Finished writing server response. Closing."); - connection_mark_for_close(TO_CONN(conn)); - } - return 0; - default: - log_warn(LD_BUG,"called in unexpected state %d.", - conn->base_.state); - tor_fragile_assert(); - return -1; - } - return 0; -} - -/** Connected handler for directory connections: begin sending data to the - * server */ -int -connection_dir_finished_connecting(dir_connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->base_.type == CONN_TYPE_DIR); - tor_assert(conn->base_.state == DIR_CONN_STATE_CONNECTING); - - log_debug(LD_HTTP,"Dir connection to router %s:%u established.", - conn->base_.address,conn->base_.port); - - conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ - return 0; -} - -/** Decide which download schedule we want to use based on descriptor type - * in dls and whether we are acting as directory server, and - * then return a list of int pointers defining download delays in seconds. - * Helper function for download_status_increment_failure() and - * download_status_reset(). */ -static const smartlist_t * -find_dl_schedule_and_len(download_status_t *dls, int server) -{ - switch (dls->schedule) { - case DL_SCHED_GENERIC: - if (server) - return get_options()->TestingServerDownloadSchedule; - else - return get_options()->TestingClientDownloadSchedule; - case DL_SCHED_CONSENSUS: - if (server) - return get_options()->TestingServerConsensusDownloadSchedule; - else - return get_options()->TestingClientConsensusDownloadSchedule; - case DL_SCHED_BRIDGE: - return get_options()->TestingBridgeDownloadSchedule; - default: - tor_assert(0); - } -} - -/** Called when an attempt to download dls has failed with HTTP status - * status_code. Increment the failure count (if the code indicates a - * real failure) and set dls-\>next_attempt_at to an appropriate time - * in the future. */ -time_t -download_status_increment_failure(download_status_t *dls, int status_code, - const char *item, int server, time_t now) -{ - const smartlist_t *schedule; - int increment; - tor_assert(dls); - if (status_code != 503 || server) { - if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) - ++dls->n_download_failures; - } - - schedule = find_dl_schedule_and_len(dls, server); - - if (dls->n_download_failures < smartlist_len(schedule)) - increment = *(int *)smartlist_get(schedule, dls->n_download_failures); - else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD) - increment = INT_MAX; - else - increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); - - if (increment < INT_MAX) - dls->next_attempt_at = now+increment; - else - dls->next_attempt_at = TIME_MAX; - - if (item) { - if (increment == 0) - log_debug(LD_DIR, "%s failed %d time(s); I'll try again immediately.", - item, (int)dls->n_download_failures); - else if (dls->next_attempt_at < TIME_MAX) - log_debug(LD_DIR, "%s failed %d time(s); I'll try again in %d seconds.", - item, (int)dls->n_download_failures, - (int)(dls->next_attempt_at-now)); - else - log_debug(LD_DIR, "%s failed %d time(s); Giving up for a while.", - item, (int)dls->n_download_failures); - } - return dls->next_attempt_at; -} - -/** Reset dls so that it will be considered downloadable - * immediately, and/or to show that we don't need it anymore. - * - * (We find the zeroth element of the download schedule, and set - * next_attempt_at to be the appropriate offset from 'now'. In most - * cases this means setting it to 'now', so the item will be immediately - * downloadable; in the case of bridge descriptors, the zeroth element - * is an hour from now.) */ -void -download_status_reset(download_status_t *dls) -{ - const smartlist_t *schedule = find_dl_schedule_and_len( - dls, get_options()->DirPort_set); - - dls->n_download_failures = 0; - dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0); -} - -/** Return the number of failures on dls since the last success (if - * any). */ -int -download_status_get_n_failures(const download_status_t *dls) -{ - return dls->n_download_failures; -} - -/** Called when one or more routerdesc (or extrainfo, if was_extrainfo) - * fetches have failed (with uppercase fingerprints listed in failed, - * either as descriptor digests or as identity digests based on - * was_descriptor_digests). - */ -static void -dir_routerdesc_download_failed(smartlist_t *failed, int status_code, - int router_purpose, - int was_extrainfo, int was_descriptor_digests) -{ - char digest[DIGEST_LEN]; - time_t now = time(NULL); - int server = directory_fetches_from_authorities(get_options()); - if (!was_descriptor_digests) { - if (router_purpose == ROUTER_PURPOSE_BRIDGE) { - tor_assert(!was_extrainfo); - connection_dir_retry_bridges(failed); - } - return; /* FFFF should implement for other-than-router-purpose someday */ - } - SMARTLIST_FOREACH_BEGIN(failed, const char *, cp) { - download_status_t *dls = NULL; - if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp)) < 0) { - log_warn(LD_BUG, "Malformed fingerprint in list: %s", escaped(cp)); - continue; - } - if (was_extrainfo) { - signed_descriptor_t *sd = - router_get_by_extrainfo_digest(digest); - if (sd) - dls = &sd->ei_dl_status; - } else { - dls = router_get_dl_status_by_descriptor_digest(digest); - } - if (!dls || dls->n_download_failures >= - get_options()->TestingDescriptorMaxDownloadTries) - continue; - download_status_increment_failure(dls, status_code, cp, server, now); - } SMARTLIST_FOREACH_END(cp); - - /* No need to relaunch descriptor downloads here: we already do it - * every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in onion_main.c. */ -} - -/** Called when a connection to download microdescriptors has failed in whole - * or in part. failed is a list of every microdesc digest we didn't - * get. status_code is the http status code we received. Reschedule the - * microdesc downloads as appropriate. */ -static void -dir_microdesc_download_failed(smartlist_t *failed, - int status_code) -{ - networkstatus_t *consensus - = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC); - routerstatus_t *rs; - download_status_t *dls; - time_t now = time(NULL); - int server = directory_fetches_from_authorities(get_options()); - - if (! consensus) - return; - SMARTLIST_FOREACH_BEGIN(failed, const char *, d) { - rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d); - if (!rs) - continue; - dls = &rs->dl_status; - if (dls->n_download_failures >= - get_options()->TestingMicrodescMaxDownloadTries) - continue; - { - char buf[BASE64_DIGEST256_LEN+1]; - digest256_to_base64(buf, d); - download_status_increment_failure(dls, status_code, buf, - server, now); - } - } SMARTLIST_FOREACH_END(d); -} - -/** Helper. Compare two fp_pair_t objects, and return negative, 0, or - * positive as appropriate. */ -static int -compare_pairs_(const void **a, const void **b) -{ - const fp_pair_t *fp1 = *a, *fp2 = *b; - int r; - if ((r = fast_memcmp(fp1->first, fp2->first, DIGEST_LEN))) - return r; - else - return fast_memcmp(fp1->second, fp2->second, DIGEST_LEN); -} - -/** Divide a string res of the form FP1-FP2+FP3-FP4...[.z], where each - * FP is a hex-encoded fingerprint, into a sequence of distinct sorted - * fp_pair_t. Skip malformed pairs. On success, return 0 and add those - * fp_pair_t into pairs_out. On failure, return -1. */ -int -dir_split_resource_into_fingerprint_pairs(const char *res, - smartlist_t *pairs_out) -{ - smartlist_t *pairs_tmp = smartlist_new(); - smartlist_t *pairs_result = smartlist_new(); - - smartlist_split_string(pairs_tmp, res, "+", 0, 0); - if (smartlist_len(pairs_tmp)) { - char *last = smartlist_get(pairs_tmp,smartlist_len(pairs_tmp)-1); - size_t last_len = strlen(last); - if (last_len > 2 && !strcmp(last+last_len-2, ".z")) { - last[last_len-2] = '\0'; - } - } - SMARTLIST_FOREACH_BEGIN(pairs_tmp, char *, cp) { - if (strlen(cp) != HEX_DIGEST_LEN*2+1) { - log_info(LD_DIR, - "Skipping digest pair %s with non-standard length.", escaped(cp)); - } else if (cp[HEX_DIGEST_LEN] != '-') { - log_info(LD_DIR, - "Skipping digest pair %s with missing dash.", escaped(cp)); - } else { - fp_pair_t pair; - if (base16_decode(pair.first, DIGEST_LEN, cp, HEX_DIGEST_LEN)<0 || - base16_decode(pair.second, - DIGEST_LEN, cp+HEX_DIGEST_LEN+1, HEX_DIGEST_LEN)<0) { - log_info(LD_DIR, "Skipping non-decodable digest pair %s", escaped(cp)); - } else { - smartlist_add(pairs_result, tor_memdup(&pair, sizeof(pair))); - } - } - tor_free(cp); - } SMARTLIST_FOREACH_END(cp); - smartlist_free(pairs_tmp); - - /* Uniq-and-sort */ - smartlist_sort(pairs_result, compare_pairs_); - smartlist_uniq(pairs_result, compare_pairs_, tor_free_); - - smartlist_add_all(pairs_out, pairs_result); - smartlist_free(pairs_result); - return 0; -} - -/** Given a directory resource request, containing zero - * or more strings separated by plus signs, followed optionally by ".z", store - * the strings, in order, into fp_out. If compressed_out is - * non-NULL, set it to 1 if the resource ends in ".z", else set it to 0. - * - * If (flags & DSR_HEX), then delete all elements that aren't hex digests, and - * decode the rest. If (flags & DSR_BASE64), then use "-" rather than "+" as - * a separator, delete all the elements that aren't base64-encoded digests, - * and decode the rest. If (flags & DSR_DIGEST256), these digests should be - * 256 bits long; else they should be 160. - * - * If (flags & DSR_SORT_UNIQ), then sort the list and remove all duplicates. - */ -int -dir_split_resource_into_fingerprints(const char *resource, - smartlist_t *fp_out, int *compressed_out, - int flags) -{ - const int decode_hex = flags & DSR_HEX; - const int decode_base64 = flags & DSR_BASE64; - const int digests_are_256 = flags & DSR_DIGEST256; - const int sort_uniq = flags & DSR_SORT_UNIQ; - - const int digest_len = digests_are_256 ? DIGEST256_LEN : DIGEST_LEN; - const int hex_digest_len = digests_are_256 ? - HEX_DIGEST256_LEN : HEX_DIGEST_LEN; - const int base64_digest_len = digests_are_256 ? - BASE64_DIGEST256_LEN : BASE64_DIGEST_LEN; - smartlist_t *fp_tmp = smartlist_new(); - - tor_assert(!(decode_hex && decode_base64)); - tor_assert(fp_out); - - smartlist_split_string(fp_tmp, resource, decode_base64?"-":"+", 0, 0); - if (compressed_out) - *compressed_out = 0; - if (smartlist_len(fp_tmp)) { - char *last = smartlist_get(fp_tmp,smartlist_len(fp_tmp)-1); - size_t last_len = strlen(last); - if (last_len > 2 && !strcmp(last+last_len-2, ".z")) { - last[last_len-2] = '\0'; - if (compressed_out) - *compressed_out = 1; - } - } - if (decode_hex || decode_base64) { - const size_t encoded_len = decode_hex ? hex_digest_len : base64_digest_len; - int i; - char *cp, *d = NULL; - for (i = 0; i < smartlist_len(fp_tmp); ++i) { - cp = smartlist_get(fp_tmp, i); - if (strlen(cp) != encoded_len) { - log_info(LD_DIR, - "Skipping digest %s with non-standard length.", escaped(cp)); - smartlist_del_keeporder(fp_tmp, i--); - goto again; - } - d = tor_malloc_zero(digest_len); - if (decode_hex ? - (base16_decode(d, digest_len, cp, hex_digest_len)<0) : - (base64_decode(d, digest_len, cp, base64_digest_len)<0)) { - log_info(LD_DIR, "Skipping non-decodable digest %s", escaped(cp)); - smartlist_del_keeporder(fp_tmp, i--); - goto again; - } - smartlist_set(fp_tmp, i, d); - d = NULL; - again: - tor_free(cp); - tor_free(d); - } - } - if (sort_uniq) { - if (decode_hex || decode_base64) { - if (digests_are_256) { - smartlist_sort_digests256(fp_tmp); - smartlist_uniq_digests256(fp_tmp); - } else { - smartlist_sort_digests(fp_tmp); - smartlist_uniq_digests(fp_tmp); - } - } else { - smartlist_sort_strings(fp_tmp); - smartlist_uniq_strings(fp_tmp); - } - } - smartlist_add_all(fp_out, fp_tmp); - smartlist_free(fp_tmp); - return 0; -} - diff --git a/src/tor/directory.h b/src/tor/directory.h deleted file mode 100644 index 0453160..0000000 --- a/src/tor/directory.h +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file directory.h - * \brief Header file for directory.c. - **/ - -#ifndef TOR_DIRECTORY_H -#define TOR_DIRECTORY_H - -int directories_have_accepted_server_descriptor(void); -void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, - dirinfo_type_t type, const char *payload, - size_t payload_len, size_t extrainfo_len); -void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, - const char *resource, - int pds_flags); -void directory_get_from_all_authorities(uint8_t dir_purpose, - uint8_t router_purpose, - const char *resource); - -/** Enumeration of ways to connect to a directory server */ -typedef enum { - /** Default: connect over a one-hop Tor circuit but fall back to direct - * connection */ - DIRIND_ONEHOP=0, - /** Connect over a multi-hop anonymizing Tor circuit */ - DIRIND_ANONYMOUS=1, - /** Conncet to the DirPort directly */ - DIRIND_DIRECT_CONN, - /** Connect over a multi-hop anonymizing Tor circuit to our dirport */ - DIRIND_ANON_DIRPORT, -} dir_indirection_t; - -void directory_initiate_command_routerstatus(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since); -void directory_initiate_command_routerstatus_rend(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - const rend_data_t *rend_query); - -int parse_http_response(const char *headers, int *code, time_t *date, - compress_method_t *compression, char **response); - -int connection_dir_is_encrypted(dir_connection_t *conn); -int connection_dir_reached_eof(dir_connection_t *conn); -int connection_dir_process_inbuf(dir_connection_t *conn); -int connection_dir_finished_flushing(dir_connection_t *conn); -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, - const char *digest, - uint8_t dir_purpose, uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, size_t payload_len, - time_t if_modified_since); - -#define DSR_HEX (1<<0) -#define DSR_BASE64 (1<<1) -#define DSR_DIGEST256 (1<<2) -#define DSR_SORT_UNIQ (1<<3) -int dir_split_resource_into_fingerprints(const char *resource, - smartlist_t *fp_out, int *compressed_out, - int flags); - -int dir_split_resource_into_fingerprint_pairs(const char *res, - smartlist_t *pairs_out); -char *directory_dump_request_log(void); -void note_request(const char *key, size_t bytes); -int router_supports_extrainfo(const char *identity_digest, int is_authority); - -time_t download_status_increment_failure(download_status_t *dls, - int status_code, const char *item, - int server, time_t now); -/** Increment the failure count of the download_status_t dls, with - * the optional status code sc. */ -#define download_status_failed(dls, sc) \ - download_status_increment_failure((dls), (sc), 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, - int max_failures); -/** Return true iff, as of now, the resource tracked by dls is - * ready to get its download reattempted. */ -static INLINE int -download_status_is_ready(download_status_t *dls, time_t now, - int max_failures) -{ - return (dls->n_download_failures <= max_failures - && dls->next_attempt_at <= now); -} - -static void download_status_mark_impossible(download_status_t *dl); -/** Mark dl as never downloadable. */ -static INLINE void -download_status_mark_impossible(download_status_t *dl) -{ - dl->n_download_failures = IMPOSSIBLE_TO_DOWNLOAD; -} - -int download_status_get_n_failures(const download_status_t *dls); - -#ifdef TOR_UNIT_TESTS -/* Used only by directory.c and test_dir.c */ -STATIC int parse_http_url(const char *headers, char **url); -#endif - -#endif - diff --git a/src/tor/dirserv.c b/src/tor/dirserv.c deleted file mode 100644 index b156282..0000000 --- a/src/tor/dirserv.c +++ /dev/null @@ -1,3780 +0,0 @@ -/* Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#define DIRSERV_PRIVATE -#include "or.h" -#include "buffers.h" -#include "config.h" -#include "confparse.h" -#include "channel.h" -#include "channeltls.h" -#include "command.h" -#include "connection.h" -#include "connection_or.h" -#include "control.h" -#include "directory.h" -#include "dirserv.h" -#include "dirvote.h" -#include "hibernate.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" -#include "routerset.h" - -/** - * \file dirserv.c - * \brief Directory server core implementation. Manages directory - * contents and generates directories. - */ - -/** How far in the future do we allow a router to get? (seconds) */ -#define ROUTER_ALLOW_SKEW (60*60*12) -/** How many seconds do we wait before regenerating the directory? */ -#define DIR_REGEN_SLACK_TIME 30 -/** If we're a cache, keep this many networkstatuses around from non-trusted - * directory authorities. */ -#define MAX_UNTRUSTED_NETWORKSTATUSES 16 - -/** If a v1 directory is older than this, discard it. */ -#define MAX_V1_DIRECTORY_AGE (30*24*60*60) -/** If a v1 running-routers is older than this, discard it. */ -#define MAX_V1_RR_AGE (7*24*60*60) - -extern time_t time_of_process_start; /* from onion_main.c */ - -extern long stats_n_seconds_working; /* from onion_main.c */ - -/** Do we need to regenerate the v1 directory when someone asks for it? */ -static time_t the_directory_is_dirty = 1; -/** Do we need to regenerate the v1 runningrouters document when somebody - * asks for it? */ -static time_t runningrouters_is_dirty = 1; - -/** Most recently generated encoded signed v1 directory. (v1 auth dirservers - * only.) */ -static cached_dir_t *the_directory = NULL; - -/** For authoritative directories: the current (v1) network status. */ -static cached_dir_t the_runningrouters; - -/** Total number of routers with measured bandwidth; this is set by - * dirserv_count_measured_bws() before the loop in - * dirserv_generate_networkstatus_vote_obj() and checked by - * dirserv_get_credible_bandwidth() and - * dirserv_compute_performance_thresholds() */ -static int routers_with_measured_bw = 0; - -static void directory_remove_invalid(void); -static cached_dir_t *dirserv_regenerate_directory(void); -static char *format_versions_list(config_line_t *ln); -struct authdir_config_t; -static int add_fingerprint_to_dir(const char *nickname, const char *fp, - struct authdir_config_t *list); -static uint32_t -dirserv_get_status_impl(const char *fp, const char *nickname, - const char *address, - uint32_t addr, uint16_t or_port, - const char *platform, const char *contact, - const char **msg, int should_log); -static void clear_cached_dir(cached_dir_t *d); -static const signed_descriptor_t *get_signed_descriptor_by_fp( - const char *fp, - int extrainfo, - time_t publish_cutoff); -static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, - const char **msg); -static uint32_t dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri); -static uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri); - -/************** Fingerprint handling code ************/ - -#define FP_NAMED 1 /**< Listed in fingerprint file. */ -#define FP_INVALID 2 /**< Believed invalid. */ -#define FP_REJECT 4 /**< We will not publish this router. */ -#define FP_BADDIR 8 /**< We'll tell clients to avoid using this as a dir. */ -#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */ -#define FP_UNNAMED 32 /**< Another router has this name in fingerprint file. */ - -/** Encapsulate a nickname and an FP_* status; target of status_by_digest - * map. */ -typedef struct router_status_t { - char nickname[MAX_NICKNAME_LEN+1]; - uint32_t status; -} router_status_t; - -/** List of nickname-\>identity fingerprint mappings for all the routers - * that we name. Used to prevent router impersonation. */ -typedef struct authdir_config_t { - strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */ - digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */ -} authdir_config_t; - -/** Should be static; exposed for testing. */ -static authdir_config_t *fingerprint_list = NULL; - -/** Allocate and return a new, empty, authdir_config_t. */ -static authdir_config_t * -authdir_config_new(void) -{ - authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t)); - list->fp_by_name = strmap_new(); - list->status_by_digest = digestmap_new(); - return list; -} - -/** Add the fingerprint fp for nickname to - * the smartlist of fingerprint_entry_t's list. Return 0 if it's - * new, or 1 if we replaced the old value. - */ -/* static */ int -add_fingerprint_to_dir(const char *nickname, const char *fp, - authdir_config_t *list) -{ - char *fingerprint; - char d[DIGEST_LEN]; - router_status_t *status; - tor_assert(nickname); - tor_assert(fp); - tor_assert(list); - - fingerprint = tor_strdup(fp); - tor_strstrip(fingerprint, " "); - if (base16_decode(d, DIGEST_LEN, fingerprint, strlen(fingerprint))) { - log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"", - escaped(fp)); - tor_free(fingerprint); - return 0; - } - - if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) { - log_warn(LD_DIRSERV, "Tried to add a mapping for reserved nickname %s", - UNNAMED_ROUTER_NICKNAME); - tor_free(fingerprint); - return 0; - } - - status = digestmap_get(list->status_by_digest, d); - if (!status) { - status = tor_malloc_zero(sizeof(router_status_t)); - digestmap_set(list->status_by_digest, d, status); - } - - if (nickname[0] != '!') { - char *old_fp = strmap_get_lc(list->fp_by_name, nickname); - if (old_fp && !strcasecmp(fingerprint, old_fp)) { - tor_free(fingerprint); - } else { - tor_free(old_fp); - strmap_set_lc(list->fp_by_name, nickname, fingerprint); - } - status->status |= FP_NAMED; - strlcpy(status->nickname, nickname, sizeof(status->nickname)); - } else { - tor_free(fingerprint); - if (!strcasecmp(nickname, "!reject")) { - status->status |= FP_REJECT; - } else if (!strcasecmp(nickname, "!invalid")) { - status->status |= FP_INVALID; - } else if (!strcasecmp(nickname, "!baddir")) { - status->status |= FP_BADDIR; - } else if (!strcasecmp(nickname, "!badexit")) { - status->status |= FP_BADEXIT; - } - } - return 0; -} - -/** Add the nickname and fingerprint for this OR to the - * global list of recognized identity key fingerprints. */ -int -dirserv_add_own_fingerprint(const char *nickname, crypto_pk_t *pk) -{ - char fp[FINGERPRINT_LEN+1]; - if (crypto_pk_get_fingerprint(pk, fp, 0)<0) { - log_err(LD_BUG, "Error computing fingerprint"); - return -1; - } - if (!fingerprint_list) - fingerprint_list = authdir_config_new(); - add_fingerprint_to_dir(nickname, fp, fingerprint_list); - return 0; -} - -/** Load the nickname-\>fingerprint mappings stored in the approved-routers - * file. The file format is line-based, with each non-blank holding one - * nickname, some space, and a fingerprint for that nickname. On success, - * replace the current fingerprint list with the new list and return 0. On - * failure, leave the current fingerprint list untouched, and return -1. */ -int -dirserv_load_fingerprint_file(void) -{ - char *fname; - char *cf; - char *nickname, *fingerprint; - authdir_config_t *fingerprint_list_new; - int result; - config_line_t *front=NULL, *list; - const or_options_t *options = get_options(); - - fname = get_datadir_fname("approved-routers"); - log_info(LD_GENERAL, - "Reloading approved fingerprints from \"%s\"...", fname); - - cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); - if (!cf) { - if (options->NamingAuthoritativeDir) { - log_warn(LD_FS, "Cannot open fingerprint file '%s'. Failing.", fname); - tor_free(fname); - return -1; - } else { - log_info(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname); - tor_free(fname); - return 0; - } - } - tor_free(fname); - - result = config_get_lines(cf, &front, 0); - tor_free(cf); - if (result < 0) { - log_warn(LD_CONFIG, "Error reading from fingerprint file"); - return -1; - } - - fingerprint_list_new = authdir_config_new(); - - for (list=front; list; list=list->next) { - char digest_tmp[DIGEST_LEN]; - nickname = list->key; fingerprint = list->value; - if (strlen(nickname) > MAX_NICKNAME_LEN) { - log_notice(LD_CONFIG, - "Nickname '%s' too long in fingerprint file. Skipping.", - nickname); - continue; - } - if (!is_legal_nickname(nickname) && - strcasecmp(nickname, "!reject") && - strcasecmp(nickname, "!invalid") && - strcasecmp(nickname, "!badexit")) { - log_notice(LD_CONFIG, - "Invalid nickname '%s' in fingerprint file. Skipping.", - nickname); - continue; - } - tor_strstrip(fingerprint, " "); /* remove spaces */ - if (strlen(fingerprint) != HEX_DIGEST_LEN || - base16_decode(digest_tmp, sizeof(digest_tmp), - fingerprint, HEX_DIGEST_LEN) < 0) { - log_notice(LD_CONFIG, - "Invalid fingerprint (nickname '%s', " - "fingerprint %s). Skipping.", - nickname, fingerprint); - continue; - } - if (0==strcasecmp(nickname, DEFAULT_CLIENT_NICKNAME)) { - /* If you approved an OR called "client", then clients who use - * the default nickname could all be rejected. That's no good. */ - log_notice(LD_CONFIG, - "Authorizing nickname '%s' would break " - "many clients; skipping.", - DEFAULT_CLIENT_NICKNAME); - continue; - } - if (0==strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) { - /* If you approved an OR called "unnamed", then clients will be - * confused. */ - log_notice(LD_CONFIG, - "Authorizing nickname '%s' is not allowed; skipping.", - UNNAMED_ROUTER_NICKNAME); - continue; - } - if (add_fingerprint_to_dir(nickname, fingerprint, fingerprint_list_new) - != 0) - log_notice(LD_CONFIG, "Duplicate nickname '%s'.", nickname); - } - - config_free_lines(front); - dirserv_free_fingerprint_list(); - fingerprint_list = fingerprint_list_new; - /* Delete any routers whose fingerprints we no longer recognize */ - directory_remove_invalid(); - return 0; -} - -/** Check whether router has a nickname/identity key combination that - * we recognize from the fingerprint list, or an IP we automatically act on - * according to our configuration. Return the appropriate router status. - * - * If the status is 'FP_REJECT' and msg is provided, set - * *msg to an explanation of why. */ -uint32_t -dirserv_router_get_status(const routerinfo_t *router, const char **msg) -{ - char d[DIGEST_LEN]; - - if (crypto_pk_get_digest(router->identity_pkey, d)) { - log_warn(LD_BUG,"Error computing fingerprint"); - if (msg) - *msg = "Bug: Error computing fingerprint"; - return FP_REJECT; - } - - return dirserv_get_status_impl(d, router->nickname, - router->address, - router->addr, router->or_port, - router->platform, router->contact_info, - msg, 1); -} - -/** Return true if there is no point in downloading the router described by - * rs because this directory would reject it. */ -int -dirserv_would_reject_router(const routerstatus_t *rs) -{ - uint32_t res; - - res = dirserv_get_status_impl(rs->identity_digest, rs->nickname, - "", /* address is only used in logs */ - rs->addr, rs->or_port, - NULL, NULL, - NULL, 0); - - return (res & FP_REJECT) != 0; -} - -/** Helper: Based only on the ID/Nickname combination, - * return FP_UNNAMED (unnamed), FP_NAMED (named), or 0 (neither). - */ -static uint32_t -dirserv_get_name_status(const char *id_digest, const char *nickname) -{ - char fp[HEX_DIGEST_LEN+1]; - char *fp_by_name; - - base16_encode(fp, sizeof(fp), id_digest, DIGEST_LEN); - - if ((fp_by_name = - strmap_get_lc(fingerprint_list->fp_by_name, nickname))) { - if (!strcasecmp(fp, fp_by_name)) { - return FP_NAMED; - } else { - return FP_UNNAMED; /* Wrong fingerprint. */ - } - } - return 0; -} - -/** Helper: As dirserv_router_get_status, but takes the router fingerprint - * (hex, no spaces), nickname, address (used for logging only), IP address, OR - * port, platform (logging only) and contact info (logging only) as arguments. - * - * If should_log is false, do not log messages. (There's not much point in - * logging that we're rejecting servers we'll not download.) - */ -static uint32_t -dirserv_get_status_impl(const char *id_digest, const char *nickname, - const char *address, - uint32_t addr, uint16_t or_port, - const char *platform, const char *contact, - const char **msg, int should_log) -{ - int reject_unlisted = get_options()->AuthDirRejectUnlisted; - uint32_t result; - router_status_t *status_by_digest; - - if (!fingerprint_list) - fingerprint_list = authdir_config_new(); - - if (should_log) - log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.", - strmap_size(fingerprint_list->fp_by_name), - digestmap_size(fingerprint_list->status_by_digest)); - - /* 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.2.35")) { - if (msg) - *msg = "Tor version is insecure or unsupported. Please upgrade!"; - return FP_REJECT; - } 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.3.10-alpha")) { - if (msg) - *msg = "Tor version is insecure or unsupported. Please upgrade!"; - return FP_REJECT; - } - } - - result = dirserv_get_name_status(id_digest, nickname); - if (result & FP_NAMED) { - if (should_log) - log_debug(LD_DIRSERV,"Good fingerprint for '%s'",nickname); - } - if (result & FP_UNNAMED) { - if (should_log) { - char *esc_contact = esc_for_log(contact); - log_info(LD_DIRSERV, - "Mismatched fingerprint for '%s'. " - "ContactInfo '%s', platform '%s'.)", - nickname, - esc_contact, - platform ? escaped(platform) : ""); - tor_free(esc_contact); - } - if (msg) - *msg = "Rejected: There is already a named server with this nickname " - "and a different fingerprint."; - } - - status_by_digest = digestmap_get(fingerprint_list->status_by_digest, - id_digest); - if (status_by_digest) - result |= (status_by_digest->status & ~FP_NAMED); - - if (result & FP_REJECT) { - if (msg) - *msg = "Fingerprint is marked rejected"; - return FP_REJECT; - } else if (result & FP_INVALID) { - if (msg) - *msg = "Fingerprint is marked invalid"; - } - - if (authdir_policy_baddir_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, - "Marking '%s' as bad directory because of address '%s'", - nickname, address); - result |= FP_BADDIR; - } - - if (authdir_policy_badexit_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Marking '%s' as bad exit because of address '%s'", - nickname, address); - result |= FP_BADEXIT; - } - - if (!(result & FP_NAMED)) { - if (!authdir_policy_permits_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Rejecting '%s' because of address '%s'", - nickname, address); - if (msg) - *msg = "Authdir is rejecting routers in this range."; - return FP_REJECT; - } - if (!authdir_policy_valid_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Not marking '%s' valid because of address '%s'", - nickname, address); - result |= FP_INVALID; - } - if (reject_unlisted) { - if (msg) - *msg = "Authdir rejects unknown routers."; - return FP_REJECT; - } - } - - return result; -} - -/** If we are an authoritative dirserver, and the list of approved - * servers contains one whose identity key digest is digest, - * return that router's nickname. Otherwise return NULL. */ -const char * -dirserv_get_nickname_by_digest(const char *digest) -{ - router_status_t *status; - if (!fingerprint_list) - return NULL; - tor_assert(digest); - - status = digestmap_get(fingerprint_list->status_by_digest, digest); - return status ? status->nickname : NULL; -} - -/** Clear the current fingerprint list. */ -void -dirserv_free_fingerprint_list(void) -{ - if (!fingerprint_list) - return; - - strmap_free(fingerprint_list->fp_by_name, tor_free_); - digestmap_free(fingerprint_list->status_by_digest, tor_free_); - tor_free(fingerprint_list); -} - -/* - * Descriptor list - */ - -/** Return -1 if ri has a private or otherwise bad address, - * unless we're configured to not care. Return 0 if all ok. */ -static int -dirserv_router_has_valid_address(routerinfo_t *ri) -{ - struct in_addr iaddr; - if (get_options()->DirAllowPrivateAddresses) - return 0; /* whatever it is, we're fine with it */ - if (!tor_inet_aton(ri->address, &iaddr)) { - log_info(LD_DIRSERV,"Router %s published non-IP address '%s'. Refusing.", - router_describe(ri), - ri->address); - return -1; - } - if (is_internal_IP(ntohl(iaddr.s_addr), 0)) { - log_info(LD_DIRSERV, - "Router %s published internal IP address '%s'. Refusing.", - router_describe(ri), ri->address); - return -1; /* it's a private IP, we should reject it */ - } - return 0; -} - -/** Check whether we, as a directory server, want to accept ri. If so, - * set its is_valid,named,running fields and return 0. Otherwise, return -1. - * - * If the router is rejected, set *msg to an explanation of why. - * - * If complain then explain at log-level 'notice' why we refused - * a descriptor; else explain at log-level 'info'. - */ -int -authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, - int complain, int *valid_out) -{ - /* Okay. Now check whether the fingerprint is recognized. */ - uint32_t status = dirserv_router_get_status(ri, msg); - time_t now; - int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO; - tor_assert(msg); - if (status & FP_REJECT) - return -1; /* msg is already set. */ - - /* Is there too much clock skew? */ - now = time(NULL); - if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) { - log_fn(severity, LD_DIRSERV, "Publication time for %s is too " - "far (%d minutes) in the future; possible clock skew. Not adding " - "(%s)", - router_describe(ri), - (int)((ri->cache_info.published_on-now)/60), - esc_router_info(ri)); - *msg = "Rejected: Your clock is set too far in the future, or your " - "timezone is not correct."; - return -1; - } - if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) { - log_fn(severity, LD_DIRSERV, - "Publication time for %s is too far " - "(%d minutes) in the past. Not adding (%s)", - router_describe(ri), - (int)((now-ri->cache_info.published_on)/60), - esc_router_info(ri)); - *msg = "Rejected: Server is expired, or your clock is too far in the past," - " or your timezone is not correct."; - return -1; - } - if (dirserv_router_has_valid_address(ri) < 0) { - log_fn(severity, LD_DIRSERV, - "Router %s has invalid address '%s'. " - "Not adding (%s).", - router_describe(ri), - ri->address, - esc_router_info(ri)); - *msg = "Rejected: Address is not an IP, or IP is a private address."; - return -1; - } - - *valid_out = ! (status & FP_INVALID); - - return 0; -} - -/** Update the relevant flags of node based on our opinion as a - * directory authority in authstatus, as returned by - * dirserv_router_get_status or equivalent. */ -void -dirserv_set_node_flags_from_authoritative_status(node_t *node, - uint32_t authstatus) -{ - node->is_valid = (authstatus & FP_INVALID) ? 0 : 1; - node->is_bad_directory = (authstatus & FP_BADDIR) ? 1 : 0; - node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0; -} - -/** True iff a is more severe than b. */ -static int -WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b) -{ - return a < b; -} - -/** As for dirserv_add_descriptor(), but accepts multiple documents, and - * returns the most severe error that occurred for any one of them. */ -was_router_added_t -dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, - const char *source, - const char **msg) -{ - was_router_added_t r, r_tmp; - const char *msg_out; - smartlist_t *list; - const char *s; - int n_parsed = 0; - time_t now = time(NULL); - char annotation_buf[ROUTER_ANNOTATION_BUF_LEN]; - char time_buf[ISO_TIME_LEN+1]; - int general = purpose == ROUTER_PURPOSE_GENERAL; - tor_assert(msg); - - r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */ - - format_iso_time(time_buf, now); - if (tor_snprintf(annotation_buf, sizeof(annotation_buf), - "@uploaded-at %s\n" - "@source %s\n" - "%s%s%s", time_buf, escaped(source), - !general ? "@purpose " : "", - !general ? router_purpose_to_string(purpose) : "", - !general ? "\n" : "")<0) { - *msg = "Couldn't format annotations"; - return -1; - } - - s = desc; - list = smartlist_new(); - if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0, - annotation_buf)) { - SMARTLIST_FOREACH(list, routerinfo_t *, ri, { - msg_out = NULL; - tor_assert(ri->purpose == purpose); - r_tmp = dirserv_add_descriptor(ri, &msg_out, source); - if (WRA_MORE_SEVERE(r_tmp, r)) { - r = r_tmp; - *msg = msg_out; - } - }); - } - n_parsed += smartlist_len(list); - smartlist_clear(list); - - s = desc; - if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0, - NULL)) { - SMARTLIST_FOREACH(list, extrainfo_t *, ei, { - msg_out = NULL; - - r_tmp = dirserv_add_extrainfo(ei, &msg_out); - if (WRA_MORE_SEVERE(r_tmp, r)) { - r = r_tmp; - *msg = msg_out; - } - }); - } - n_parsed += smartlist_len(list); - smartlist_free(list); - - if (! *msg) { - if (!n_parsed) { - *msg = "No descriptors found in your POST."; - if (WRA_WAS_ADDED(r)) - r = ROUTER_WAS_NOT_NEW; - } else { - *msg = "(no message)"; - } - } - - return r; -} - -/** Examine the parsed server descriptor in ri and maybe insert it into - * the list of server descriptors. Set *msg to a message that should be - * passed back to the origin of this descriptor, or NULL if there is no such - * message. Use source to produce better log messages. - * - * Return the status of the operation - * - * This function is only called when fresh descriptors are posted, not when - * we re-load the cache. - */ -was_router_added_t -dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) -{ - was_router_added_t r; - routerinfo_t *ri_old; - char *desc, *nickname; - size_t desclen = 0; - *msg = NULL; - - /* If it's too big, refuse it now. Otherwise we'll cache it all over the - * network and it'll clog everything up. */ - if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) { - log_notice(LD_DIR, "Somebody attempted to publish a router descriptor '%s'" - " (source: %s) with size %d. Either this is an attack, or the " - "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."; - control_event_or_authdir_new_descriptor("REJECTED", - ri->cache_info.signed_descriptor_body, - ri->cache_info.signed_descriptor_len, *msg); - routerinfo_free(ri); - return ROUTER_AUTHDIR_REJECTS; - } - - /* Check whether this descriptor is semantically identical to the last one - * from this server. (We do this here and not in router_add_to_routerlist - * because we want to be able to accept the newest router descriptor that - * another authority has, so we all converge on the same one.) */ - ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest); - if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on - && router_differences_are_cosmetic(ri_old, ri) - && !router_is_me(ri)) { - log_info(LD_DIRSERV, - "Not replacing descriptor from %s (source: %s); " - "differences are cosmetic.", - router_describe(ri), source); - *msg = "Not replacing router descriptor; no information has changed since " - "the last one with this identity."; - control_event_or_authdir_new_descriptor("DROPPED", - ri->cache_info.signed_descriptor_body, - ri->cache_info.signed_descriptor_len, *msg); - routerinfo_free(ri); - return ROUTER_WAS_NOT_NEW; - } - - /* Make a copy of desc, since router_add_to_routerlist might free - * ri and its associated signed_descriptor_t. */ - desclen = ri->cache_info.signed_descriptor_len; - desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen); - nickname = tor_strdup(ri->nickname); - - /* Tell if we're about to need to launch a test if we add this. */ - ri->needs_retest_if_added = - dirserv_should_launch_reachability_test(ri, ri_old); - - r = router_add_to_routerlist(ri, msg, 0, 0); - if (!WRA_WAS_ADDED(r)) { - /* unless the routerinfo was fine, just out-of-date */ - if (WRA_WAS_REJECTED(r)) - control_event_or_authdir_new_descriptor("REJECTED", desc, desclen, *msg); - log_info(LD_DIRSERV, - "Did not add descriptor from '%s' (source: %s): %s.", - nickname, source, *msg ? *msg : "(no message)"); - } else { - smartlist_t *changed; - control_event_or_authdir_new_descriptor("ACCEPTED", desc, desclen, *msg); - - changed = smartlist_new(); - smartlist_add(changed, ri); - routerlist_descriptors_added(changed, 0); - smartlist_free(changed); - if (!*msg) { - *msg = "Descriptor accepted"; - } - log_info(LD_DIRSERV, - "Added descriptor from '%s' (source: %s): %s.", - nickname, source, *msg); - } - tor_free(desc); - tor_free(nickname); - return r; -} - -/** As dirserv_add_descriptor, but for an extrainfo_t ei. */ -static was_router_added_t -dirserv_add_extrainfo(extrainfo_t *ei, const char **msg) -{ - const routerinfo_t *ri; - int r; - tor_assert(msg); - *msg = NULL; - - ri = router_get_by_id_digest(ei->cache_info.identity_digest); - if (!ri) { - *msg = "No corresponding router descriptor for extra-info descriptor"; - extrainfo_free(ei); - return ROUTER_BAD_EI; - } - - /* If it's too big, refuse it now. Otherwise we'll cache it all over the - * network and it'll clog everything up. */ - if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) { - log_notice(LD_DIR, "Somebody attempted to publish an extrainfo " - "with size %d. Either this is an attack, or the " - "MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.", - (int)ei->cache_info.signed_descriptor_len, - MAX_EXTRAINFO_UPLOAD_SIZE); - *msg = "Extrainfo document was too large"; - extrainfo_free(ei); - return ROUTER_BAD_EI; - } - - if ((r = routerinfo_incompatible_with_extrainfo(ri, ei, NULL, msg))) { - extrainfo_free(ei); - return r < 0 ? ROUTER_WAS_NOT_NEW : ROUTER_BAD_EI; - } - router_add_extrainfo_to_routerlist(ei, msg, 0, 0); - return ROUTER_ADDED_SUCCESSFULLY; -} - -/** Remove all descriptors whose nicknames or fingerprints no longer - * are allowed by our fingerprint list. (Descriptors that used to be - * good can become bad when we reload the fingerprint list.) - */ -static void -directory_remove_invalid(void) -{ - int changed = 0; - routerlist_t *rl = router_get_routerlist(); - smartlist_t *nodes = smartlist_new(); - smartlist_add_all(nodes, nodelist_get_list()); - - SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) { - const char *msg; - routerinfo_t *ent = node->ri; - char description[NODE_DESC_BUF_LEN]; - uint32_t r; - if (!ent) - continue; - r = dirserv_router_get_status(ent, &msg); - router_get_description(description, ent); - if (r & FP_REJECT) { - log_info(LD_DIRSERV, "Router %s is now rejected: %s", - description, msg?msg:""); - routerlist_remove(rl, ent, 0, time(NULL)); - changed = 1; - continue; - } -#if 0 - if (bool_neq((r & FP_NAMED), ent->auth_says_is_named)) { - log_info(LD_DIRSERV, - "Router %s is now %snamed.", description, - (r&FP_NAMED)?"":"un"); - ent->is_named = (r&FP_NAMED)?1:0; - changed = 1; - } - if (bool_neq((r & FP_UNNAMED), ent->auth_says_is_unnamed)) { - log_info(LD_DIRSERV, - "Router '%s' is now %snamed. (FP_UNNAMED)", description, - (r&FP_NAMED)?"":"un"); - ent->is_named = (r&FP_NUNAMED)?0:1; - changed = 1; - } -#endif - if (bool_neq((r & FP_INVALID), !node->is_valid)) { - log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description, - (r&FP_INVALID) ? "in" : ""); - node->is_valid = (r&FP_INVALID)?0:1; - changed = 1; - } - if (bool_neq((r & FP_BADDIR), node->is_bad_directory)) { - log_info(LD_DIRSERV, "Router '%s' is now a %s directory", description, - (r & FP_BADDIR) ? "bad" : "good"); - node->is_bad_directory = (r&FP_BADDIR) ? 1: 0; - changed = 1; - } - if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) { - log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description, - (r & FP_BADEXIT) ? "bad" : "good"); - node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0; - changed = 1; - } - } SMARTLIST_FOREACH_END(node); - if (changed) - directory_set_dirty(); - - routerlist_assert_ok(rl); - smartlist_free(nodes); -} - -/** Mark the directory as dirty -- when we're next asked for a - * directory, we will rebuild it instead of reusing the most recently - * generated one. - */ -void -directory_set_dirty(void) -{ - time_t now = time(NULL); - int set_v1_dirty=0; - - /* Regenerate stubs only every 8 hours. - * XXXX It would be nice to generate less often, but these are just - * stubs: it doesn't matter. */ -#define STUB_REGENERATE_INTERVAL (8*60*60) - if (!the_directory || !the_runningrouters.dir) - set_v1_dirty = 1; - else if (the_directory->published < now - STUB_REGENERATE_INTERVAL || - the_runningrouters.published < now - STUB_REGENERATE_INTERVAL) - set_v1_dirty = 1; - - if (set_v1_dirty) { - if (!the_directory_is_dirty) - the_directory_is_dirty = now; - if (!runningrouters_is_dirty) - runningrouters_is_dirty = now; - } -} - -/** - * Allocate and return a description of the status of the server desc, - * for use in a v1-style router-status line. The server is listed - * as running iff is_live is true. - */ -static char * -list_single_server_status(const routerinfo_t *desc, int is_live) -{ - char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */ - char *cp; - const node_t *node; - - tor_assert(desc); - - cp = buf; - if (!is_live) { - *cp++ = '!'; - } - node = node_get_by_id(desc->cache_info.identity_digest); - if (node && node->is_valid) { - strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf)); - cp += strlen(cp); - *cp++ = '='; - } - *cp++ = '$'; - base16_encode(cp, HEX_DIGEST_LEN+1, desc->cache_info.identity_digest, - DIGEST_LEN); - return tor_strdup(buf); -} - -/* DOCDOC running_long_enough_to_decide_unreachable */ -static INLINE int -running_long_enough_to_decide_unreachable(void) -{ - return time_of_process_start - + get_options()->TestingAuthDirTimeToLearnReachability < approx_time(); -} - -/** Each server needs to have passed a reachability test no more - * than this number of seconds ago, or he is listed as down in - * the directory. */ -#define REACHABLE_TIMEOUT (45*60) - -/** If we tested a router and found it reachable _at least this long_ after it - * declared itself hibernating, it is probably done hibernating and we just - * missed a descriptor from it. */ -#define HIBERNATION_PUBLICATION_SKEW (60*60) - -/** Treat a router as alive if - * - It's me, and I'm not hibernating. - * or - We've found it reachable recently. */ -void -dirserv_set_router_is_running(routerinfo_t *router, time_t now) -{ - /*XXXX024 This function is a mess. Separate out the part that calculates - whether it's reachable and the part that tells rephist that the router was - 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); - - if (router_is_me(router)) { - /* We always know if we are down ourselves. */ - answer = ! we_are_hibernating(); - } else if (router->is_hibernating && - (router->cache_info.published_on + - 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 (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 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()) { - /* Not considered reachable. tell rephist about that. - - Because we launch a reachability test for each router every - 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 (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); - } - - node->is_running = answer; -} - -/** Based on the routerinfo_ts in routers, allocate the - * contents of a v1-style router-status line, and store it in - * *router_status_out. Return 0 on success, -1 on failure. - * - * If for_controller is true, include the routers with very old descriptors. - */ -int -list_server_status_v1(smartlist_t *routers, char **router_status_out, - int for_controller) -{ - /* List of entries in a router-status style: An optional !, then an optional - * equals-suffixed nickname, then a dollar-prefixed hexdigest. */ - smartlist_t *rs_entries; - time_t now = time(NULL); - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - const or_options_t *options = get_options(); - /* We include v2 dir auths here too, because they need to answer - * controllers. Eventually we'll deprecate this whole function; - * see also networkstatus_getinfo_by_purpose(). */ - int authdir = authdir_mode_publishes_statuses(options); - tor_assert(router_status_out); - - rs_entries = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - const node_t *node = node_get_by_id(ri->cache_info.identity_digest); - tor_assert(node); - if (authdir) { - /* Update router status in routerinfo_t. */ - dirserv_set_router_is_running(ri, now); - } - if (for_controller) { - char name_buf[MAX_VERBOSE_NICKNAME_LEN+2]; - char *cp = name_buf; - if (!node->is_running) - *cp++ = '!'; - router_get_verbose_nickname(cp, ri); - smartlist_add(rs_entries, tor_strdup(name_buf)); - } else if (ri->cache_info.published_on >= cutoff) { - smartlist_add(rs_entries, list_single_server_status(ri, - node->is_running)); - } - } SMARTLIST_FOREACH_END(ri); - - *router_status_out = smartlist_join_strings(rs_entries, " ", 0, NULL); - - SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp)); - smartlist_free(rs_entries); - - return 0; -} - -/** Given a (possibly empty) list of config_line_t, each line of which contains - * a list of comma-separated version numbers surrounded by optional space, - * allocate and return a new string containing the version numbers, in order, - * separated by commas. Used to generate Recommended(Client|Server)?Versions - */ -static char * -format_versions_list(config_line_t *ln) -{ - smartlist_t *versions; - char *result; - versions = smartlist_new(); - for ( ; ln; ln = ln->next) { - smartlist_split_string(versions, ln->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - } - sort_version_list(versions, 1); - result = smartlist_join_strings(versions,",",0,NULL); - SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); - smartlist_free(versions); - return result; -} - -/** Return 1 if ri's descriptor is "active" -- running, valid, - * not hibernating, and not too old. Else return 0. - */ -static int -router_is_active(const routerinfo_t *ri, const node_t *node, time_t now) -{ - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - if (ri->cache_info.published_on < cutoff) - return 0; - if (!node->is_running || !node->is_valid || ri->is_hibernating) - return 0; - return 1; -} - -/** Generate a new v1 directory and write it into a newly allocated string. - * Point *dir_out to the allocated string. Sign the - * directory with private_key. Return 0 on success, -1 on - * failure. If complete is set, give us all the descriptors; - * otherwise leave out non-running and non-valid ones. - */ -int -dirserv_dump_directory_to_string(char **dir_out, - crypto_pk_t *private_key) -{ - /* XXXX 024 Get rid of this function if we can confirm that nobody's - * fetching these any longer */ - char *cp; - char *identity_pkey; /* Identity key, DER64-encoded. */ - char *recommended_versions; - char digest[DIGEST_LEN]; - char published[ISO_TIME_LEN+1]; - char *buf = NULL; - size_t buf_len; - size_t identity_pkey_len; - time_t now = time(NULL); - - tor_assert(dir_out); - *dir_out = NULL; - - if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey, - &identity_pkey_len)<0) { - log_warn(LD_BUG,"write identity_pkey to string failed!"); - return -1; - } - - recommended_versions = - format_versions_list(get_options()->RecommendedVersions); - - format_iso_time(published, now); - - buf_len = 2048+strlen(recommended_versions); - - buf = tor_malloc(buf_len); - /* We'll be comparing against buf_len throughout the rest of the - function, though strictly speaking we shouldn't be able to exceed - it. This is C, after all, so we may as well check for buffer - overruns.*/ - - tor_snprintf(buf, buf_len, - "signed-directory\n" - "published %s\n" - "recommended-software %s\n" - "router-status %s\n" - "dir-signing-key\n%s\n", - published, recommended_versions, "", - identity_pkey); - - tor_free(recommended_versions); - tor_free(identity_pkey); - - cp = buf + strlen(buf); - *cp = '\0'; - - /* These multiple strlcat calls are inefficient, but dwarfed by the RSA - signature. */ - if (strlcat(buf, "directory-signature ", buf_len) >= buf_len) - goto truncated; - if (strlcat(buf, get_options()->Nickname, buf_len) >= buf_len) - goto truncated; - if (strlcat(buf, "\n", buf_len) >= buf_len) - goto truncated; - - if (router_get_dir_hash(buf,digest)) { - log_warn(LD_BUG,"couldn't compute digest"); - tor_free(buf); - return -1; - } - note_crypto_pk_op(SIGN_DIR); - if (router_append_dirobj_signature(buf,buf_len,digest,DIGEST_LEN, - private_key)<0) { - tor_free(buf); - return -1; - } - - *dir_out = buf; - return 0; - truncated: - log_warn(LD_BUG,"tried to exceed string length."); - tor_free(buf); - return -1; -} - -/********************************************************************/ - -/* A set of functions to answer questions about how we'd like to behave - * as a directory mirror/client. */ - -/** Return 1 if we fetch our directory material directly from the - * authorities, rather than from a mirror. */ -int -directory_fetches_from_authorities(const or_options_t *options) -{ - const routerinfo_t *me; - uint32_t addr; - int refuseunknown; - if (options->FetchDirInfoEarly) - return 1; - if (options->BridgeRelay == 1) - return 0; - if (server_mode(options) && router_pick_published_address(options, &addr)<0) - 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_set && !refuseunknown) - return 0; - if (!server_mode(options) || !advertised_server_mode()) - return 0; - me = router_get_my_routerinfo(); - if (!me || (!me->dir_port && !refuseunknown)) - return 0; /* if dirport not advertised, return 0 too */ - return 1; -} - -/** Return 1 if we should fetch new networkstatuses, descriptors, etc - * on the "mirror" schedule rather than the "client" schedule. - */ -int -directory_fetches_dir_info_early(const or_options_t *options) -{ - return directory_fetches_from_authorities(options); -} - -/** Return 1 if we should fetch new networkstatuses, descriptors, etc - * on a very passive schedule -- waiting long enough for ordinary clients - * to probably have the info we want. These would include bridge users, - * and maybe others in the future e.g. if a Tor client uses another Tor - * client as a directory guard. - */ -int -directory_fetches_dir_info_later(const or_options_t *options) -{ - return options->UseBridges != 0; -} - -/** Return true iff we want to fetch and keep certificates for authorities - * that we don't acknowledge as aurthorities ourself. - */ -int -directory_caches_unknown_auth_certs(const or_options_t *options) -{ - return options->DirPort_set || options->BridgeRelay; -} - -/** Return 1 if we want to keep descriptors, networkstatuses, etc around - * and we're willing to serve them to others. Else return 0. - */ -int -directory_caches_dir_info(const or_options_t *options) -{ - if (options->BridgeRelay || options->DirPort_set) - return 1; - if (!server_mode(options) || !advertised_server_mode()) - return 0; - /* We need an up-to-date view of network info if we're going to try to - * block exit attempts from unknown relays. */ - return ! router_my_exit_policy_is_reject_star() && - should_refuse_unknown_exits(options); -} - -/** Return 1 if we want to allow remote people to ask us directory - * requests via the "begin_dir" interface, which doesn't require - * having any separate port open. */ -int -directory_permits_begindir_requests(const or_options_t *options) -{ - return options->BridgeRelay != 0 || options->DirPort_set; -} - -/** Return 1 if we want to allow controllers to ask us directory - * requests via the controller interface, which doesn't require - * having any separate port open. */ -int -directory_permits_controller_requests(const or_options_t *options) -{ - return options->DirPort_set; -} - -/** Return 1 if we have no need to fetch new descriptors. This generally - * happens when we're not a dir cache and we haven't built any circuits - * lately. - */ -int -directory_too_idle_to_fetch_descriptors(const or_options_t *options, - time_t now) -{ - return !directory_caches_dir_info(options) && - !options->FetchUselessDescriptors && - rep_hist_circbuilding_dormant(now); -} - -/********************************************************************/ - -/* Used only by non-v1-auth dirservers: The v1 directory and - * runningrouters we'll serve when requested. */ - -/** The v1 directory we'll serve (as a cache or as an authority) if - * requested. */ -static cached_dir_t *cached_directory = NULL; -/** The v1 runningrouters document we'll serve (as a cache or as an authority) - * if requested. */ -static cached_dir_t cached_runningrouters; - -/** Map from flavor name to the cached_dir_t for the v3 consensuses that we're - * currently serving. */ -static strmap_t *cached_consensuses = NULL; - -/** Possibly replace the contents of d with the value of - * directory published on when, unless when is older than - * the last value, or too far in the future. - * - * Does not copy directory; frees it if it isn't used. - */ -static void -set_cached_dir(cached_dir_t *d, char *directory, time_t when) -{ - time_t now = time(NULL); - if (when<=d->published) { - log_info(LD_DIRSERV, "Ignoring old directory; not caching."); - tor_free(directory); - } else if (when>=now+ROUTER_MAX_AGE_TO_PUBLISH) { - log_info(LD_DIRSERV, "Ignoring future directory; not caching."); - tor_free(directory); - } else { - /* if (when>d->published && whendir); - d->dir = directory; - d->dir_len = strlen(directory); - tor_free(d->dir_z); - if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len, - ZLIB_METHOD)) { - log_warn(LD_BUG,"Error compressing cached directory"); - } - d->published = when; - } -} - -/** Decrement the reference count on d, and free it if it no longer has - * any references. */ -void -cached_dir_decref(cached_dir_t *d) -{ - if (!d || --d->refcnt > 0) - return; - clear_cached_dir(d); - tor_free(d); -} - -/** Allocate and return a new cached_dir_t containing the string s, - * published at published. */ -cached_dir_t * -new_cached_dir(char *s, time_t published) -{ - cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t)); - d->refcnt = 1; - d->dir = s; - d->dir_len = strlen(s); - d->published = published; - if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len, - ZLIB_METHOD)) { - log_warn(LD_BUG, "Error compressing directory"); - } - return d; -} - -/** Remove all storage held in d, but do not free d itself. */ -static void -clear_cached_dir(cached_dir_t *d) -{ - tor_free(d->dir); - tor_free(d->dir_z); - memset(d, 0, sizeof(cached_dir_t)); -} - -/** Free all storage held by the cached_dir_t in d. */ -static void -free_cached_dir_(void *_d) -{ - cached_dir_t *d; - if (!_d) - return; - - d = (cached_dir_t *)_d; - cached_dir_decref(d); -} - -/** If we have no cached v1 directory, or it is older than published, - * then replace it with directory, published at published. - * - * If published is too old, do nothing. - * - * If is_running_routers, this is really a v1 running_routers - * document rather than a v1 directory. - */ -static void -dirserv_set_cached_directory(const char *directory, time_t published) -{ - - cached_dir_decref(cached_directory); - cached_directory = new_cached_dir(tor_strdup(directory), published); -} - -/** Replace the v3 consensus networkstatus of type flavor_name that - * we're serving with networkstatus, published at published. No - * validation is performed. */ -void -dirserv_set_cached_consensus_networkstatus(const char *networkstatus, - const char *flavor_name, - const digests_t *digests, - time_t published) -{ - cached_dir_t *new_networkstatus; - cached_dir_t *old_networkstatus; - if (!cached_consensuses) - cached_consensuses = strmap_new(); - - new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published); - memcpy(&new_networkstatus->digests, digests, sizeof(digests_t)); - old_networkstatus = strmap_set(cached_consensuses, flavor_name, - new_networkstatus); - if (old_networkstatus) - cached_dir_decref(old_networkstatus); -} - -/** Remove any v1 info from the directory cache that was published - * too long ago. */ -void -dirserv_clear_old_v1_info(time_t now) -{ - if (cached_directory && - cached_directory->published < (now - MAX_V1_DIRECTORY_AGE)) { - cached_dir_decref(cached_directory); - cached_directory = NULL; - } - if (cached_runningrouters.published < (now - MAX_V1_RR_AGE)) { - clear_cached_dir(&cached_runningrouters); - } -} - -/** Helper: If we're an authority for the right directory version (v1) - * (based on auth_type), try to regenerate - * auth_src as appropriate and return it, falling back to cache_src on - * failure. If we're a cache, simply return cache_src. - */ -static cached_dir_t * -dirserv_pick_cached_dir_obj(cached_dir_t *cache_src, - cached_dir_t *auth_src, - time_t dirty, cached_dir_t *(*regenerate)(void), - const char *name, - dirinfo_type_t auth_type) -{ - const or_options_t *options = get_options(); - int authority = (auth_type == V1_DIRINFO && authdir_mode_v1(options)); - - if (!authority || authdir_mode_bridge(options)) { - return cache_src; - } else { - /* We're authoritative. */ - if (regenerate != NULL) { - if (dirty && dirty + DIR_REGEN_SLACK_TIME < time(NULL)) { - if (!(auth_src = regenerate())) { - log_err(LD_BUG, "Couldn't generate %s?", name); - exit(1); - } - } else { - log_info(LD_DIRSERV, "The %s is still clean; reusing.", name); - } - } - return auth_src ? auth_src : cache_src; - } -} - -/** Return the most recently generated encoded signed v1 directory, - * generating a new one as necessary. If not a v1 authoritative directory - * may return NULL if no directory is yet cached. */ -cached_dir_t * -dirserv_get_directory(void) -{ - return dirserv_pick_cached_dir_obj(cached_directory, the_directory, - the_directory_is_dirty, - dirserv_regenerate_directory, - "v1 server directory", V1_DIRINFO); -} - -/** Only called by v1 auth dirservers. - * Generate a fresh v1 directory; set the_directory and return a pointer - * to the new value. - */ -static cached_dir_t * -dirserv_regenerate_directory(void) -{ - /* XXXX 024 Get rid of this function if we can confirm that nobody's - * fetching these any longer */ - char *new_directory=NULL; - - if (dirserv_dump_directory_to_string(&new_directory, - get_server_identity_key())) { - log_warn(LD_BUG, "Error creating directory."); - tor_free(new_directory); - return NULL; - } - cached_dir_decref(the_directory); - the_directory = new_cached_dir(new_directory, time(NULL)); - log_info(LD_DIRSERV,"New directory (size %d) has been built.", - (int)the_directory->dir_len); - log_debug(LD_DIRSERV,"New directory (size %d):\n%s", - (int)the_directory->dir_len, the_directory->dir); - - the_directory_is_dirty = 0; - - /* Save the directory to disk so we re-load it quickly on startup. - */ - dirserv_set_cached_directory(the_directory->dir, time(NULL)); - - return the_directory; -} - -/** Only called by v1 auth dirservers. - * Replace the current running-routers list with a newly generated one. */ -static cached_dir_t * -generate_runningrouters(void) -{ - char *s=NULL; - char digest[DIGEST_LEN]; - char published[ISO_TIME_LEN+1]; - size_t len; - crypto_pk_t *private_key = get_server_identity_key(); - char *identity_pkey; /* Identity key, DER64-encoded. */ - size_t identity_pkey_len; - - if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey, - &identity_pkey_len)<0) { - log_warn(LD_BUG,"write identity_pkey to string failed!"); - goto err; - } - format_iso_time(published, time(NULL)); - - len = 2048; - s = tor_malloc_zero(len); - tor_snprintf(s, len, - "network-status\n" - "published %s\n" - "router-status %s\n" - "dir-signing-key\n%s" - "directory-signature %s\n", - published, "", identity_pkey, - get_options()->Nickname); - tor_free(identity_pkey); - if (router_get_runningrouters_hash(s,digest)) { - log_warn(LD_BUG,"couldn't compute digest"); - goto err; - } - note_crypto_pk_op(SIGN_DIR); - if (router_append_dirobj_signature(s, len, digest, DIGEST_LEN, - private_key)<0) - goto err; - - set_cached_dir(&the_runningrouters, s, time(NULL)); - runningrouters_is_dirty = 0; - - return &the_runningrouters; - err: - tor_free(s); - return NULL; -} - -/** Set *rr to the most recently generated encoded signed - * running-routers list, generating a new one as necessary. Return the - * size of the directory on success, and 0 on failure. */ -cached_dir_t * -dirserv_get_runningrouters(void) -{ - return dirserv_pick_cached_dir_obj( - &cached_runningrouters, &the_runningrouters, - runningrouters_is_dirty, - generate_runningrouters, - "v1 network status list", V1_DIRINFO); -} - -/** Return the latest downloaded consensus networkstatus in encoded, signed, - * optionally compressed format, suitable for sending to clients. */ -cached_dir_t * -dirserv_get_consensus(const char *flavor_name) -{ - if (!cached_consensuses) - return NULL; - return strmap_get(cached_consensuses, flavor_name); -} - -/** If a router's uptime is at least this value, then it is always - * considered stable, regardless of the rest of the network. This - * way we resist attacks where an attacker doubles the size of the - * network using allegedly high-uptime nodes, displacing all the - * current guards. */ -#define UPTIME_TO_GUARANTEE_STABLE (3600*24*30) -/** If a router's MTBF is at least this value, then it is always stable. - * See above. (Corresponds to about 7 days for current decay rates.) */ -#define MTBF_TO_GUARANTEE_STABLE (60*60*24*5) -/** Similarly, every node with at least this much weighted time known can be - * considered familiar enough to be a guard. Corresponds to about 20 days for - * current decay rates. - */ -#define TIME_KNOWN_TO_GUARANTEE_FAMILIAR (8*24*60*60) -/** Similarly, every node with sufficient WFU is around enough to be a guard. - */ -#define WFU_TO_GUARANTEE_GUARD (0.98) - -/* Thresholds for server performance: set by - * dirserv_compute_performance_thresholds, and used by - * generate_v2_networkstatus */ - -/** Any router with an uptime of at least this value is stable. */ -static uint32_t stable_uptime = 0; /* start at a safe value */ -/** Any router with an mtbf of at least this value is stable. */ -static double stable_mtbf = 0.0; -/** If true, we have measured enough mtbf info to look at stable_mtbf rather - * than stable_uptime. */ -static int enough_mtbf_info = 0; -/** Any router with a weighted fractional uptime of at least this much might - * be good as a guard. */ -static double guard_wfu = 0.0; -/** Don't call a router a guard unless we've known about it for at least this - * many seconds. */ -static long guard_tk = 0; -/** Any router with a bandwidth at least this high is "Fast" */ -static uint32_t fast_bandwidth_kb = 0; -/** If exits can be guards, then all guards must have a bandwidth this - * high. */ -static uint32_t guard_bandwidth_including_exits_kb = 0; -/** If exits can't be guards, then all guards must have a bandwidth this - * high. */ -static uint32_t guard_bandwidth_excluding_exits_kb = 0; - -/** Helper: estimate the uptime of a router given its stated uptime and the - * amount of time since it last stated its stated uptime. */ -static INLINE long -real_uptime(const routerinfo_t *router, time_t now) -{ - if (now < router->cache_info.published_on) - return router->uptime; - else - return router->uptime + (now - router->cache_info.published_on); -} - -/** Return 1 if router is not suitable for these parameters, else 0. - * If need_uptime is non-zero, we require a minimum uptime. - * If need_capacity is non-zero, we require a minimum advertised - * bandwidth. - */ -static int -dirserv_thinks_router_is_unreliable(time_t now, - routerinfo_t *router, - int need_uptime, int need_capacity) -{ - if (need_uptime) { - if (!enough_mtbf_info) { - /* XXX024 Once most authorities are on v3, we should change the rule from - * "use uptime if we don't have mtbf data" to "don't advertise Stable on - * v3 if we don't have enough mtbf data." Or maybe not, since if we ever - * hit a point where we need to reset a lot of authorities at once, - * none of them would be in a position to declare Stable. - */ - long uptime = real_uptime(router, now); - if ((unsigned)uptime < stable_uptime && - (unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE) - return 1; - } else { - double mtbf = - rep_hist_get_stability(router->cache_info.identity_digest, now); - if (mtbf < stable_mtbf && - mtbf < MTBF_TO_GUARANTEE_STABLE) - return 1; - } - } - if (need_capacity) { - uint32_t bw_kb = dirserv_get_credible_bandwidth_kb(router); - if (bw_kb < fast_bandwidth_kb) - return 1; - } - return 0; -} - -/** Return true iff router should be assigned the "HSDir" flag. - * Right now this means it advertises support for it, it has a high - * uptime, it has a DirPort open, and it's currently considered Running. - * - * This function needs to be called after router-\>is_running has - * been set. - */ -static int -dirserv_thinks_router_is_hs_dir(const routerinfo_t *router, - const node_t *node, time_t now) -{ - - long uptime; - - /* If we haven't been running for at least - * get_options()->MinUptimeHidServDirectoryV2 seconds, we can't - * have accurate data telling us a relay has been up for at least - * that long. We also want to allow a bit of slack: Reachability - * tests aren't instant. If we haven't been running long enough, - * trust the relay. */ - - if (stats_n_seconds_working > - get_options()->MinUptimeHidServDirectoryV2 * 1.1) - uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now), - real_uptime(router, now)); - else - uptime = real_uptime(router, now); - - /* XXX We shouldn't need to check dir_port, but we do because of - * bug 1693. In the future, once relays set wants_to_be_hs_dir - * correctly, we can revert to only checking dir_port if router's - * version is too old. */ - /* XXX Unfortunately, we need to keep checking dir_port until all - * *clients* suffering from bug 2722 are obsolete. The first version - * to fix the bug was 0.2.2.25-alpha. */ - return (router->wants_to_be_hs_dir && router->dir_port && - uptime >= get_options()->MinUptimeHidServDirectoryV2 && - node->is_running); -} - -/** Don't consider routers with less bandwidth than this when computing - * thresholds. */ -#define ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB 4 - -/** Helper for dirserv_compute_performance_thresholds(): Decide whether to - * include a router in our calculations, and return true iff we should; the - * require_mbw parameter is passed in by - * dirserv_compute_performance_thresholds() and controls whether we ever - * count routers with only advertised bandwidths */ -static int -router_counts_toward_thresholds(const node_t *node, time_t now, - const digestmap_t *omit_as_sybil, - int require_mbw) -{ - /* Have measured bw? */ - int have_mbw = - dirserv_has_measured_bw(node->identity); - uint64_t min_bw_kb = ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB; - const or_options_t *options = get_options(); - - if (options->TestingTorNetwork) { - min_bw_kb = (int64_t)options->TestingMinExitFlagThreshold / 1000; - } - - return node->ri && router_is_active(node->ri, node, now) && - !digestmap_get(omit_as_sybil, node->identity) && - (dirserv_get_credible_bandwidth_kb(node->ri) >= min_bw_kb) && - (have_mbw || !require_mbw); -} - -/** Look through the routerlist, the Mean Time Between Failure history, and - * the Weighted Fractional Uptime history, and use them to set thresholds for - * the Stable, Fast, and Guard flags. Update the fields stable_uptime, - * stable_mtbf, enough_mtbf_info, guard_wfu, guard_tk, fast_bandwidth, - * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits. - * - * Also, set the is_exit flag of each router appropriately. */ -static void -dirserv_compute_performance_thresholds(routerlist_t *rl, - digestmap_t *omit_as_sybil) -{ - int n_active, n_active_nonexit, n_familiar; - uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb; - long *tks; - double *mtbfs, *wfus; - time_t now = time(NULL); - const or_options_t *options = get_options(); - - /* Require mbw? */ - int require_mbw = - (routers_with_measured_bw > - options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0; - - /* initialize these all here, in case there are no routers */ - stable_uptime = 0; - stable_mtbf = 0; - fast_bandwidth_kb = 0; - guard_bandwidth_including_exits_kb = 0; - guard_bandwidth_excluding_exits_kb = 0; - guard_tk = 0; - guard_wfu = 0; - - /* Initialize arrays that will hold values for each router. We'll - * sort them and use that to compute thresholds. */ - n_active = n_active_nonexit = 0; - /* Uptime for every active router. */ - uptimes = tor_malloc(sizeof(uint32_t)*smartlist_len(rl->routers)); - /* Bandwidth for every active router. */ - bandwidths_kb = tor_malloc(sizeof(uint32_t)*smartlist_len(rl->routers)); - /* Bandwidth for every active non-exit router. */ - bandwidths_excluding_exits_kb = - tor_malloc(sizeof(uint32_t)*smartlist_len(rl->routers)); - /* Weighted mean time between failure for each active router. */ - mtbfs = tor_malloc(sizeof(double)*smartlist_len(rl->routers)); - /* Time-known for each active router. */ - tks = tor_malloc(sizeof(long)*smartlist_len(rl->routers)); - /* Weighted fractional uptime for each active router. */ - wfus = tor_malloc(sizeof(double)*smartlist_len(rl->routers)); - - nodelist_assert_ok(); - - /* Now, fill in the arrays. */ - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { - if (options->BridgeAuthoritativeDir && - node->ri && - node->ri->purpose != ROUTER_PURPOSE_BRIDGE) - continue; - if (router_counts_toward_thresholds(node, now, omit_as_sybil, - require_mbw)) { - routerinfo_t *ri = node->ri; - const char *id = node->identity; - uint32_t bw_kb; - node->is_exit = (!router_exit_policy_rejects_all(ri) && - exit_policy_is_general_exit(ri->exit_policy)); - uptimes[n_active] = (uint32_t)real_uptime(ri, now); - mtbfs[n_active] = rep_hist_get_stability(id, now); - tks [n_active] = rep_hist_get_weighted_time_known(id, now); - bandwidths_kb[n_active] = bw_kb = dirserv_get_credible_bandwidth_kb(ri); - if (!node->is_exit || node->is_bad_exit) { - bandwidths_excluding_exits_kb[n_active_nonexit] = bw_kb; - ++n_active_nonexit; - } - ++n_active; - } - } SMARTLIST_FOREACH_END(node); - - /* Now, compute thresholds. */ - if (n_active) { - /* The median uptime is stable. */ - stable_uptime = median_uint32(uptimes, n_active); - /* The median mtbf is stable, if we have enough mtbf info */ - stable_mtbf = median_double(mtbfs, n_active); - /* The 12.5th percentile bandwidth is fast. */ - fast_bandwidth_kb = find_nth_uint32(bandwidths_kb, n_active, n_active/8); - /* (Now bandwidths is sorted.) */ - if (fast_bandwidth_kb < ROUTER_REQUIRED_MIN_BANDWIDTH/(2 * 1000)) - fast_bandwidth_kb = bandwidths_kb[n_active/4]; - guard_bandwidth_including_exits_kb = bandwidths_kb[(n_active-1)/2]; - guard_tk = find_nth_long(tks, n_active, n_active/8); - } - - if (guard_tk > TIME_KNOWN_TO_GUARANTEE_FAMILIAR) - guard_tk = TIME_KNOWN_TO_GUARANTEE_FAMILIAR; - - { - /* We can vote on a parameter for the minimum and maximum. */ -#define ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG 4 - int32_t min_fast_kb, max_fast_kb, min_fast, max_fast; - min_fast = networkstatus_get_param(NULL, "FastFlagMinThreshold", - ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG, - ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG, - INT32_MAX); - if (options->TestingTorNetwork) { - min_fast = (int32_t)options->TestingMinFastFlagThreshold; - } - max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold", - INT32_MAX, min_fast, INT32_MAX); - min_fast_kb = min_fast / 1000; - max_fast_kb = max_fast / 1000; - - if (fast_bandwidth_kb < (uint32_t)min_fast_kb) - fast_bandwidth_kb = min_fast_kb; - if (fast_bandwidth_kb > (uint32_t)max_fast_kb) - fast_bandwidth_kb = max_fast_kb; - } - /* Protect sufficiently fast nodes from being pushed out of the set - * of Fast nodes. */ - if (options->AuthDirFastGuarantee && - fast_bandwidth_kb > options->AuthDirFastGuarantee/1000) - fast_bandwidth_kb = (uint32_t)options->AuthDirFastGuarantee/1000; - - /* Now that we have a time-known that 7/8 routers are known longer than, - * fill wfus with the wfu of every such "familiar" router. */ - n_familiar = 0; - - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { - if (router_counts_toward_thresholds(node, now, - omit_as_sybil, require_mbw)) { - routerinfo_t *ri = node->ri; - const char *id = ri->cache_info.identity_digest; - long tk = rep_hist_get_weighted_time_known(id, now); - if (tk < guard_tk) - continue; - wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now); - } - } SMARTLIST_FOREACH_END(node); - if (n_familiar) - guard_wfu = median_double(wfus, n_familiar); - if (guard_wfu > WFU_TO_GUARANTEE_GUARD) - guard_wfu = WFU_TO_GUARANTEE_GUARD; - - enough_mtbf_info = rep_hist_have_measured_enough_stability(); - - if (n_active_nonexit) { - guard_bandwidth_excluding_exits_kb = - median_uint32(bandwidths_excluding_exits_kb, n_active_nonexit); - } - - log_info(LD_DIRSERV, - "Cutoffs: For Stable, %lu sec uptime, %lu sec MTBF. " - "For Fast: %lu kilobytes/sec. " - "For Guard: WFU %.03f%%, time-known %lu sec, " - "and bandwidth %lu or %lu kilobytes/sec. " - "We%s have enough stability data.", - (unsigned long)stable_uptime, - (unsigned long)stable_mtbf, - (unsigned long)fast_bandwidth_kb, - guard_wfu*100, - (unsigned long)guard_tk, - (unsigned long)guard_bandwidth_including_exits_kb, - (unsigned long)guard_bandwidth_excluding_exits_kb, - enough_mtbf_info ? "" : " don't "); - - tor_free(uptimes); - tor_free(mtbfs); - tor_free(bandwidths_kb); - tor_free(bandwidths_excluding_exits_kb); - tor_free(tks); - tor_free(wfus); -} - -/* Use dirserv_compute_performance_thresholds() to compute the thresholds - * for the status flags, specifically for bridges. - * - * This is only called by a Bridge Authority from - * networkstatus_getinfo_by_purpose(). - */ -void -dirserv_compute_bridge_flag_thresholds(routerlist_t *rl) -{ - - digestmap_t *omit_as_sybil = digestmap_new(); - dirserv_compute_performance_thresholds(rl, omit_as_sybil); - digestmap_free(omit_as_sybil, NULL); -} - -/** Measured bandwidth cache entry */ -typedef struct mbw_cache_entry_s { - long mbw_kb; - time_t as_of; -} mbw_cache_entry_t; - -/** Measured bandwidth cache - keys are identity_digests, values are - * mbw_cache_entry_t *. */ -static digestmap_t *mbw_cache = NULL; - -/** Store a measured bandwidth cache entry when reading the measured - * bandwidths file. */ -STATIC void -dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, - time_t as_of) -{ - mbw_cache_entry_t *e = NULL; - - tor_assert(parsed_line); - - /* Allocate a cache if we need */ - if (!mbw_cache) mbw_cache = digestmap_new(); - - /* Check if we have an existing entry */ - e = digestmap_get(mbw_cache, parsed_line->node_id); - /* If we do, we can re-use it */ - if (e) { - /* Check that we really are newer, and update */ - if (as_of > e->as_of) { - e->mbw_kb = parsed_line->bw_kb; - e->as_of = as_of; - } - } else { - /* We'll have to insert a new entry */ - e = tor_malloc(sizeof(*e)); - e->mbw_kb = parsed_line->bw_kb; - e->as_of = as_of; - digestmap_set(mbw_cache, parsed_line->node_id, e); - } -} - -/** Clear and free the measured bandwidth cache */ -STATIC void -dirserv_clear_measured_bw_cache(void) -{ - if (mbw_cache) { - /* Free the map and all entries */ - digestmap_free(mbw_cache, tor_free_); - mbw_cache = NULL; - } -} - -/** Scan the measured bandwidth cache and remove expired entries */ -STATIC void -dirserv_expire_measured_bw_cache(time_t now) -{ - - if (mbw_cache) { - /* Iterate through the cache and check each entry */ - DIGESTMAP_FOREACH_MODIFY(mbw_cache, k, mbw_cache_entry_t *, e) { - if (now > e->as_of + MAX_MEASUREMENT_AGE) { - tor_free(e); - MAP_DEL_CURRENT(k); - } - } DIGESTMAP_FOREACH_END; - - /* Check if we cleared the whole thing and free if so */ - if (digestmap_size(mbw_cache) == 0) { - digestmap_free(mbw_cache, tor_free_); - mbw_cache = 0; - } - } -} - -/** Get the current size of the measured bandwidth cache */ -STATIC int -dirserv_get_measured_bw_cache_size(void) -{ - if (mbw_cache) return digestmap_size(mbw_cache); - else return 0; -} - -/** Query the cache by identity digest, return value indicates whether - * we found it. The bw_out and as_of_out pointers receive the cached - * bandwidth value and the time it was cached if not NULL. */ -STATIC int -dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, - time_t *as_of_out) -{ - mbw_cache_entry_t *v = NULL; - int rv = 0; - - if (mbw_cache && node_id) { - v = digestmap_get(mbw_cache, node_id); - if (v) { - /* Found something */ - rv = 1; - if (bw_kb_out) *bw_kb_out = v->mbw_kb; - if (as_of_out) *as_of_out = v->as_of; - } - } - - return rv; -} - -/** Predicate wrapper for dirserv_query_measured_bw_cache() */ -STATIC int -dirserv_has_measured_bw(const char *node_id) -{ - return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL); -} - -/** Get the best estimate of a router's bandwidth for dirauth purposes, - * preferring measured to advertised values if available. */ - -static uint32_t -dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) -{ - uint32_t bw_kb = 0; - /* - * Yeah, measured bandwidths in measured_bw_line_t are (implicitly - * signed) longs and the ones router_get_advertised_bandwidth() returns - * are uint32_t. - */ - long mbw_kb = 0; - - if (ri) { - /* - * * First try to see if we have a measured bandwidth; don't bother with - * as_of_out here, on the theory that a stale measured bandwidth is still - * better to trust than an advertised one. - */ - if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, - &mbw_kb, NULL)) { - /* Got one! */ - bw_kb = (uint32_t)mbw_kb; - } else { - /* If not, fall back to advertised */ - bw_kb = router_get_advertised_bandwidth(ri) / 1000; - } - } - - return bw_kb; -} - -/** Look through the routerlist, and using the measured bandwidth cache count - * how many measured bandwidths we know. This is used to decide whether we - * ever trust advertised bandwidths for purposes of assigning flags. */ -static void -dirserv_count_measured_bws(routerlist_t *rl) -{ - /* Initialize this first */ - routers_with_measured_bw = 0; - - tor_assert(rl); - tor_assert(rl->routers); - - /* Iterate over the routerlist and count measured bandwidths */ - SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { - /* Check if we know a measured bandwidth for this one */ - if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) { - ++routers_with_measured_bw; - } - } SMARTLIST_FOREACH_END(ri); -} - -/** Return the bandwidth we believe for assigning flags; prefer measured - * over advertised, and if we have above a threshold quantity of measured - * bandwidths, we don't want to ever give flags to unmeasured routers, so - * return 0. */ -static uint32_t -dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri) -{ - int threshold; - uint32_t bw_kb = 0; - long mbw_kb; - - tor_assert(ri); - /* Check if we have a measured bandwidth, and check the threshold if not */ - if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, - &mbw_kb, NULL))) { - threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; - if (routers_with_measured_bw > threshold) { - /* Return zero for unmeasured bandwidth if we are above threshold */ - bw_kb = 0; - } else { - /* Return an advertised bandwidth otherwise */ - bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000; - } - } else { - /* We have the measured bandwidth in mbw */ - bw_kb = (uint32_t)mbw_kb; - } - - return bw_kb; -} - -/** Give a statement of our current performance thresholds for inclusion - * in a vote document. */ -char * -dirserv_get_flag_thresholds_line(void) -{ - char *result=NULL; - const int measured_threshold = - get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; - const int enough_measured_bw = routers_with_measured_bw > measured_threshold; - - tor_asprintf(&result, - "stable-uptime=%lu stable-mtbf=%lu " - "fast-speed=%lu " - "guard-wfu=%.03f%% guard-tk=%lu " - "guard-bw-inc-exits=%lu guard-bw-exc-exits=%lu " - "enough-mtbf=%d ignoring-advertised-bws=%d", - (unsigned long)stable_uptime, - (unsigned long)stable_mtbf, - (unsigned long)fast_bandwidth_kb*1000, - guard_wfu*100, - (unsigned long)guard_tk, - (unsigned long)guard_bandwidth_including_exits_kb*1000, - (unsigned long)guard_bandwidth_excluding_exits_kb*1000, - enough_mtbf_info ? 1 : 0, - enough_measured_bw ? 1 : 0); - - return result; -} - -/** Given a platform string as in a routerinfo_t (possibly null), return a - * newly allocated version string for a networkstatus document, or NULL if the - * platform doesn't give a Tor version. */ -static char * -version_from_platform(const char *platform) -{ - if (platform && !strcmpstart(platform, "Tor ")) { - const char *eos = find_whitespace(platform+4); - if (eos && !strcmpstart(eos, " (r")) { - /* XXXX Unify this logic with the other version extraction - * logic in routerparse.c. */ - eos = find_whitespace(eos+1); - } - if (eos) { - return tor_strndup(platform, eos-platform); - } - } - return NULL; -} - -/** Helper: write the router-status information in rs into a newly - * allocated character buffer. Use the same format as in network-status - * documents. If version is non-NULL, add a "v" line for the platform. - * Return 0 on success, -1 on failure. - * - * The format argument has one of the following values: - * NS_V2 - Output an entry suitable for a V2 NS opinion document - * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry - * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc - * consensus entry. - * NS_V3_VOTE - Output a complete V3 NS vote. If vrs is present, - * it contains additional information for the vote. - * NS_CONTROL_PORT - Output a NS document for the control port - */ -char * -routerstatus_format_entry(const routerstatus_t *rs, const char *version, - routerstatus_format_type_t format, - const vote_routerstatus_t *vrs) -{ - char *summary; - char *result = NULL; - - char published[ISO_TIME_LEN+1]; - char identity64[BASE64_DIGEST_LEN+1]; - char digest64[BASE64_DIGEST_LEN+1]; - smartlist_t *chunks = NULL; - - format_iso_time(published, rs->published_on); - digest_to_base64(identity64, rs->identity_digest); - digest_to_base64(digest64, rs->descriptor_digest); - - chunks = smartlist_new(); - smartlist_add_asprintf(chunks, - "r %s %s %s%s%s %s %d %d\n", - rs->nickname, - identity64, - (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, - (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", - published, - fmt_addr32(rs->addr), - (int)rs->or_port, - (int)rs->dir_port); - - /* 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 */ - - /* V3 microdesc consensuses don't have "a" lines. */ - if (format == NS_V3_CONSENSUS_MICRODESC) - goto done; - - /* Possible "a" line. At most one for now. */ - if (!tor_addr_is_null(&rs->ipv6_addr)) { - smartlist_add_asprintf(chunks, "a %s\n", - fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); - } - - if (format == NS_V3_CONSENSUS) - goto done; - - smartlist_add_asprintf(chunks, - "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - /* These must stay in alphabetical order. */ - rs->is_authority?" Authority":"", - rs->is_bad_directory?" BadDirectory":"", - rs->is_bad_exit?" BadExit":"", - rs->is_exit?" Exit":"", - rs->is_fast?" Fast":"", - rs->is_possible_guard?" Guard":"", - rs->is_hs_dir?" HSDir":"", - rs->is_named?" Named":"", - rs->is_flagged_running?" Running":"", - rs->is_stable?" Stable":"", - rs->is_unnamed?" Unnamed":"", - (rs->dir_port!=0)?" V2Dir":"", - rs->is_valid?" Valid":""); - - /* length of "opt v \n" */ -#define V_LINE_OVERHEAD 7 - if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { - smartlist_add_asprintf(chunks, "v %s\n", version); - } - - if (format != NS_V2) { - const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest); - uint32_t bw_kb; - - if (format != NS_CONTROL_PORT) { - /* Blow up more or less nicely if we didn't get anything or not the - * thing we expected. - */ - if (!desc) { - char id[HEX_DIGEST_LEN+1]; - char dd[HEX_DIGEST_LEN+1]; - - base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); - base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN); - log_warn(LD_BUG, "Cannot get any descriptor for %s " - "(wanted descriptor %s).", - id, dd); - goto err; - } - - /* This assert could fire for the control port, because - * it can request NS documents before all descriptors - * have been fetched. Therefore, we only do this test when - * format != NS_CONTROL_PORT. */ - if (tor_memneq(desc->cache_info.signed_descriptor_digest, - rs->descriptor_digest, - DIGEST_LEN)) { - char rl_d[HEX_DIGEST_LEN+1]; - char rs_d[HEX_DIGEST_LEN+1]; - char id[HEX_DIGEST_LEN+1]; - - base16_encode(rl_d, sizeof(rl_d), - desc->cache_info.signed_descriptor_digest, DIGEST_LEN); - base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN); - base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); - log_err(LD_BUG, "descriptor digest in routerlist does not match " - "the one in routerstatus: %s vs %s " - "(router %s)\n", - rl_d, rs_d, id); - - tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest, - rs->descriptor_digest, - DIGEST_LEN)); - } - } - - if (format == NS_CONTROL_PORT && rs->has_bandwidth) { - bw_kb = rs->bandwidth_kb; - } else { - tor_assert(desc); - bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000; - } - smartlist_add_asprintf(chunks, - "w Bandwidth=%d", bw_kb); - - if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) { - smartlist_add_asprintf(chunks, - " Measured=%d", vrs->measured_bw_kb); - } - smartlist_add(chunks, tor_strdup("\n")); - - if (desc) { - summary = policy_summarize(desc->exit_policy, AF_INET); - smartlist_add_asprintf(chunks, "p %s\n", summary); - tor_free(summary); - } - } - - done: - result = smartlist_join_strings(chunks, "", 0, NULL); - - err: - if (chunks) { - SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); - smartlist_free(chunks); - } - - return result; -} - -/** Helper for sorting: compares two routerinfos first by address, and then by - * descending order of "usefulness". (An authority is more useful than a - * non-authority; a running router is more useful than a non-running router; - * and a router with more bandwidth is more useful than one with less.) - **/ -static int -compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) -{ - routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; - int first_is_auth, second_is_auth; - uint32_t bw_kb_first, bw_kb_second; - const node_t *node_first, *node_second; - int first_is_running, second_is_running; - - /* we return -1 if first should appear before second... that is, - * if first is a better router. */ - if (first->addr < second->addr) - return -1; - else if (first->addr > second->addr) - return 1; - - /* Potentially, this next bit could cause k n lg n memeq calls. But in - * reality, we will almost never get here, since addresses will usually be - * different. */ - - first_is_auth = - router_digest_is_trusted_dir(first->cache_info.identity_digest); - second_is_auth = - router_digest_is_trusted_dir(second->cache_info.identity_digest); - - if (first_is_auth && !second_is_auth) - return -1; - else if (!first_is_auth && second_is_auth) - return 1; - - node_first = node_get_by_id(first->cache_info.identity_digest); - node_second = node_get_by_id(second->cache_info.identity_digest); - first_is_running = node_first && node_first->is_running; - second_is_running = node_second && node_second->is_running; - - if (first_is_running && !second_is_running) - return -1; - else if (!first_is_running && second_is_running) - return 1; - - bw_kb_first = dirserv_get_bandwidth_for_router_kb(first); - bw_kb_second = dirserv_get_bandwidth_for_router_kb(second); - - if (bw_kb_first > bw_kb_second) - return -1; - else if (bw_kb_first < bw_kb_second) - return 1; - - /* They're equal! Compare by identity digest, so there's a - * deterministic order and we avoid flapping. */ - return fast_memcmp(first->cache_info.identity_digest, - second->cache_info.identity_digest, - DIGEST_LEN); -} - -/** Given a list of routerinfo_t in routers, return a new digestmap_t - * whose keys are the identity digests of those routers that we're going to - * exclude for Sybil-like appearance. */ -static digestmap_t * -get_possible_sybil_list(const smartlist_t *routers) -{ - const or_options_t *options = get_options(); - digestmap_t *omit_as_sybil; - smartlist_t *routers_by_ip = smartlist_new(); - uint32_t last_addr; - int addr_count; - /* Allow at most this number of Tor servers on a single IP address, ... */ - int max_with_same_addr = options->AuthDirMaxServersPerAddr; - /* ... unless it's a directory authority, in which case allow more. */ - int max_with_same_addr_on_authority = options->AuthDirMaxServersPerAuthAddr; - if (max_with_same_addr <= 0) - max_with_same_addr = INT_MAX; - if (max_with_same_addr_on_authority <= 0) - max_with_same_addr_on_authority = INT_MAX; - - smartlist_add_all(routers_by_ip, routers); - smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); - omit_as_sybil = digestmap_new(); - - last_addr = 0; - addr_count = 0; - SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (last_addr != ri->addr) { - last_addr = ri->addr; - addr_count = 1; - } else if (++addr_count > max_with_same_addr) { - if (!router_addr_is_trusted_dir(ri->addr) || - addr_count > max_with_same_addr_on_authority) - digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); - } - } SMARTLIST_FOREACH_END(ri); - - smartlist_free(routers_by_ip); - return omit_as_sybil; -} - -/** Return non-zero iff a relay running the Tor version specified in - * platform is suitable for use as a potential entry guard. */ -static int -is_router_version_good_for_possible_guard(const char *platform) -{ - static int parsed_versions_initialized = 0; - static tor_version_t first_good_0_2_1_guard_version; - static tor_version_t first_good_0_2_2_guard_version; - static tor_version_t first_good_later_guard_version; - - tor_version_t router_version; - - /* XXX024 This block should be extracted into its own function. */ - /* XXXX Begin code copied from tor_version_as_new_as (in routerparse.c) */ - { - char *s, *s2, *start; - char tmp[128]; - - tor_assert(platform); - - /* nonstandard Tor; be safe and say yes */ - if (strcmpstart(platform,"Tor ")) - return 1; - - start = (char *)eat_whitespace(platform+3); - if (!*start) return 0; - s = (char *)find_whitespace(start); /* also finds '\0', which is fine */ - s2 = (char*)eat_whitespace(s); - if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-")) - s = (char*)find_whitespace(s2); - - if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */ - return 0; - strlcpy(tmp, start, s-start+1); - - if (tor_version_parse(tmp, &router_version)<0) { - log_info(LD_DIR,"Router version '%s' unparseable.",tmp); - return 1; /* be safe and say yes */ - } - } - /* XXXX End code copied from tor_version_as_new_as (in routerparse.c) */ - - if (!parsed_versions_initialized) { - /* CVE-2011-2769 was fixed on the relay side in Tor versions - * 0.2.1.31, 0.2.2.34, and 0.2.3.6-alpha. */ - tor_assert(tor_version_parse("0.2.1.31", - &first_good_0_2_1_guard_version)>=0); - tor_assert(tor_version_parse("0.2.2.34", - &first_good_0_2_2_guard_version)>=0); - tor_assert(tor_version_parse("0.2.3.6-alpha", - &first_good_later_guard_version)>=0); - - /* Don't parse these constant version strings once for every relay - * for every vote. */ - parsed_versions_initialized = 1; - } - - return ((tor_version_same_series(&first_good_0_2_1_guard_version, - &router_version) && - tor_version_compare(&first_good_0_2_1_guard_version, - &router_version) <= 0) || - (tor_version_same_series(&first_good_0_2_2_guard_version, - &router_version) && - tor_version_compare(&first_good_0_2_2_guard_version, - &router_version) <= 0) || - (tor_version_compare(&first_good_later_guard_version, - &router_version) <= 0)); -} - -/** Extract status information from ri and from other authority - * functions and store it in rs>. If naming, consider setting - * the named flag in rs. - * - * We assume that ri-\>is_running has already been set, e.g. by - * dirserv_set_router_is_running(ri, now); - */ -void -set_routerstatus_from_routerinfo(routerstatus_t *rs, - node_t *node, - routerinfo_t *ri, - time_t now, - int naming, int listbadexits, - int listbaddirs, int vote_on_hsdirs) -{ - const or_options_t *options = get_options(); - uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri); - - memset(rs, 0, sizeof(routerstatus_t)); - - rs->is_authority = - router_digest_is_trusted_dir(ri->cache_info.identity_digest); - - /* Already set by compute_performance_thresholds. */ - 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); - rs->is_fast = node->is_fast = - router_is_active(ri, node, now) && - !dirserv_thinks_router_is_unreliable(now, ri, 0, 1); - rs->is_flagged_running = node->is_running; /* computed above */ - - if (naming) { - uint32_t name_status = dirserv_get_name_status( - node->identity, ri->nickname); - rs->is_named = (naming && (name_status & FP_NAMED)) ? 1 : 0; - rs->is_unnamed = (naming && (name_status & FP_UNNAMED)) ? 1 : 0; - } - rs->is_valid = node->is_valid; - - if (node->is_fast && - ((options->AuthDirGuardBWGuarantee && - routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) || - routerbw_kb >= MIN(guard_bandwidth_including_exits_kb, - guard_bandwidth_excluding_exits_kb)) && - is_router_version_good_for_possible_guard(ri->platform)) { - long tk = rep_hist_get_weighted_time_known( - node->identity, now); - double wfu = rep_hist_get_weighted_fractional_uptime( - node->identity, now); - rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0; - } else { - rs->is_possible_guard = 0; - } - if (options->TestingTorNetwork && - routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, - rs, 0)) { - rs->is_possible_guard = 1; - } - - rs->is_bad_directory = listbaddirs && node->is_bad_directory; - rs->is_bad_exit = listbadexits && node->is_bad_exit; - node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now); - rs->is_hs_dir = vote_on_hsdirs && node->is_hs_dir; - - if (!strcasecmp(ri->nickname, UNNAMED_ROUTER_NICKNAME)) - rs->is_named = rs->is_unnamed = 0; - - rs->published_on = ri->cache_info.published_on; - memcpy(rs->identity_digest, node->identity, DIGEST_LEN); - memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest, - DIGEST_LEN); - rs->addr = ri->addr; - 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 rs is part of a group of routers that are on - * too narrow an IP-space. Clear out its flags: we don't want people - * using it. - */ -static void -clear_status_flags_on_sybil(routerstatus_t *rs) -{ - rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = - rs->is_flagged_running = rs->is_named = rs->is_valid = - rs->is_hs_dir = rs->is_possible_guard = rs->is_bad_exit = - rs->is_bad_directory = 0; - /* FFFF we might want some mechanism to check later on if we - * missed zeroing any flags: it's easy to add a new flag but - * forget to add it to this clause. */ -} - -/** - * Helper function to parse out a line in the measured bandwidth file - * into a measured_bw_line_t output structure. Returns -1 on failure - * or 0 on success. - */ -STATIC int -measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line) -{ - char *line = tor_strdup(orig_line); - char *cp = line; - int got_bw = 0; - int got_node_id = 0; - char *strtok_state; /* lame sauce d'jour */ - cp = tor_strtok_r(cp, " \t", &strtok_state); - - if (!cp) { - log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - - if (orig_line[strlen(orig_line)-1] != '\n') { - log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - - do { - if (strcmpstart(cp, "bw=") == 0) { - int parse_ok = 0; - char *endptr; - if (got_bw) { - log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - cp+=strlen("bw="); - - out->bw_kb = tor_parse_long(cp, 0, 0, LONG_MAX, &parse_ok, &endptr); - if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) { - log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - got_bw=1; - } else if (strcmpstart(cp, "node_id=$") == 0) { - if (got_node_id) { - log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - cp+=strlen("node_id=$"); - - if (strlen(cp) != HEX_DIGEST_LEN || - base16_decode(out->node_id, DIGEST_LEN, cp, HEX_DIGEST_LEN)) { - log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } - strlcpy(out->node_hex, cp, sizeof(out->node_hex)); - got_node_id=1; - } - } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state))); - - if (got_bw && got_node_id) { - tor_free(line); - return 0; - } else { - log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", - escaped(orig_line)); - tor_free(line); - return -1; - } -} - -/** - * Helper function to apply a parsed measurement line to a list - * of bandwidth statuses. Returns true if a line is found, - * false otherwise. - */ -STATIC int -measured_bw_line_apply(measured_bw_line_t *parsed_line, - smartlist_t *routerstatuses) -{ - vote_routerstatus_t *rs = NULL; - if (!routerstatuses) - return 0; - - rs = smartlist_bsearch(routerstatuses, parsed_line->node_id, - compare_digest_to_vote_routerstatus_entry); - - if (rs) { - rs->has_measured_bw = 1; - rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb; - } else { - log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list", - parsed_line->node_hex); - } - - return rs != NULL; -} - -/** - * Read the measured bandwidth file and apply it to the list of - * vote_routerstatus_t. Returns -1 on error, 0 otherwise. - */ -int -dirserv_read_measured_bandwidths(const char *from_file, - smartlist_t *routerstatuses) -{ - char line[256]; - FILE *fp = tor_fopen_cloexec(from_file, "r"); - int applied_lines = 0; - time_t file_time, now; - int ok; - - if (fp == NULL) { - log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s", - from_file); - return -1; - } - - if (!fgets(line, sizeof(line), fp) - || !strlen(line) || line[strlen(line)-1] != '\n') { - log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", - escaped(line)); - fclose(fp); - return -1; - } - - line[strlen(line)-1] = '\0'; - file_time = tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s", - escaped(line)); - fclose(fp); - return -1; - } - - now = time(NULL); - if ((now - file_time) > MAX_MEASUREMENT_AGE) { - log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u", - (unsigned)(time(NULL) - file_time)); - fclose(fp); - return -1; - } - - if (routerstatuses) - smartlist_sort(routerstatuses, compare_vote_routerstatus_entries); - - while (!feof(fp)) { - measured_bw_line_t parsed_line; - if (fgets(line, sizeof(line), fp) && strlen(line)) { - if (measured_bw_line_parse(&parsed_line, line) != -1) { - /* Also cache the line for dirserv_get_bandwidth_for_router() */ - dirserv_cache_measured_bw(&parsed_line, file_time); - if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0) - applied_lines++; - } - } - } - - /* Now would be a nice time to clean the cache, too */ - dirserv_expire_measured_bw_cache(now); - - fclose(fp); - log_info(LD_DIRSERV, - "Bandwidth measurement file successfully read. " - "Applied %d measurements.", applied_lines); - return 0; -} - -/** Return a new networkstatus_t* containing our current opinion. (For v3 - * authorities) */ -networkstatus_t * -dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, - authority_cert_t *cert) -{ - const or_options_t *options = get_options(); - networkstatus_t *v3_out = NULL; - uint32_t addr; - char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; - const char *contact; - smartlist_t *routers, *routerstatuses; - char identity_digest[DIGEST_LEN]; - char signing_key_digest[DIGEST_LEN]; - int naming = options->NamingAuthoritativeDir; - int listbadexits = options->AuthDirListBadExits; - int listbaddirs = options->AuthDirListBadDirs; - int vote_on_hsdirs = options->VoteOnHidServDirectoriesV2; - routerlist_t *rl = router_get_routerlist(); - time_t now = time(NULL); - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - networkstatus_voter_info_t *voter = NULL; - vote_timing_t timing; - digestmap_t *omit_as_sybil = NULL; - const int vote_on_reachability = running_long_enough_to_decide_unreachable(); - smartlist_t *microdescriptors = NULL; - - tor_assert(private_key); - tor_assert(cert); - - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { - log_warn(LD_NET, "Couldn't resolve my hostname"); - return NULL; - } - if (!hostname || !strchr(hostname, '.')) { - tor_free(hostname); - hostname = tor_dup_ip(addr); - } - if (crypto_pk_get_digest(private_key, signing_key_digest)<0) { - log_err(LD_BUG, "Error computing signing key digest"); - return NULL; - } - if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) { - log_err(LD_BUG, "Error computing identity key digest"); - return NULL; - } - - if (options->VersioningAuthoritativeDir) { - client_versions = format_versions_list(options->RecommendedClientVersions); - server_versions = format_versions_list(options->RecommendedServerVersions); - } - - contact = get_options()->ContactInfo; - if (!contact) - contact = "(none)"; - - /* - * Do this so dirserv_compute_performance_thresholds() and - * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. - */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - /* precompute this part, since we need it to decide what "stable" - * means. */ - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { - dirserv_set_router_is_running(ri, now); - }); - - routers = smartlist_new(); - smartlist_add_all(routers, rl->routers); - routers_sort_by_identity(routers); - omit_as_sybil = get_possible_sybil_list(routers); - - DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { - (void) ignore; - rep_hist_make_router_pessimal(sybil_id, now); - } DIGESTMAP_FOREACH_END; - - /* Count how many have measured bandwidths so we know how to assign flags; - * this must come before dirserv_compute_performance_thresholds() */ - dirserv_count_measured_bws(rl); - - dirserv_compute_performance_thresholds(rl, omit_as_sybil); - - routerstatuses = smartlist_new(); - microdescriptors = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - if (ri->cache_info.published_on >= cutoff) { - routerstatus_t *rs; - vote_routerstatus_t *vrs; - node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); - if (!node) - continue; - - vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); - rs = &vrs->status; - set_routerstatus_from_routerinfo(rs, node, ri, now, - naming, listbadexits, listbaddirs, - vote_on_hsdirs); - - if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) - clear_status_flags_on_sybil(rs); - - if (!vote_on_reachability) - rs->is_flagged_running = 0; - - vrs->version = version_from_platform(ri->platform); - vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, - microdescriptors); - - smartlist_add(routerstatuses, vrs); - } - } SMARTLIST_FOREACH_END(ri); - - { - smartlist_t *added = - microdescs_add_list_to_cache(get_microdesc_cache(), - microdescriptors, SAVED_NOWHERE, 0); - smartlist_free(added); - smartlist_free(microdescriptors); - } - - smartlist_free(routers); - digestmap_free(omit_as_sybil, NULL); - - /* This pass through applies the measured bw lines to the routerstatuses */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, - routerstatuses); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - v3_out = tor_malloc_zero(sizeof(networkstatus_t)); - - v3_out->type = NS_TYPE_VOTE; - dirvote_get_preferred_voting_intervals(&timing); - v3_out->published = now; - { - char tbuf[ISO_TIME_LEN+1]; - networkstatus_t *current_consensus = - networkstatus_get_live_consensus(now); - long last_consensus_interval; /* only used to pick a valid_after */ - if (current_consensus) - last_consensus_interval = current_consensus->fresh_until - - current_consensus->valid_after; - else - last_consensus_interval = options->TestingV3AuthInitialVotingInterval; - v3_out->valid_after = - dirvote_get_start_of_next_interval(now, (int)last_consensus_interval, - options->TestingV3AuthVotingStartOffset); - format_iso_time(tbuf, v3_out->valid_after); - log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " - "consensus_set=%d, last_interval=%d", - tbuf, current_consensus?1:0, (int)last_consensus_interval); - } - v3_out->fresh_until = v3_out->valid_after + timing.vote_interval; - v3_out->valid_until = v3_out->valid_after + - (timing.vote_interval * timing.n_intervals_valid); - v3_out->vote_seconds = timing.vote_delay; - v3_out->dist_seconds = timing.dist_delay; - tor_assert(v3_out->vote_seconds > 0); - tor_assert(v3_out->dist_seconds > 0); - tor_assert(timing.n_intervals_valid > 0); - - v3_out->client_versions = client_versions; - v3_out->server_versions = server_versions; - v3_out->known_flags = smartlist_new(); - smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Stable V2Dir Valid", - 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (vote_on_reachability) - smartlist_add(v3_out->known_flags, tor_strdup("Running")); - if (listbaddirs) - smartlist_add(v3_out->known_flags, tor_strdup("BadDirectory")); - if (listbadexits) - smartlist_add(v3_out->known_flags, tor_strdup("BadExit")); - if (naming) { - smartlist_add(v3_out->known_flags, tor_strdup("Named")); - smartlist_add(v3_out->known_flags, tor_strdup("Unnamed")); - } - if (vote_on_hsdirs) - smartlist_add(v3_out->known_flags, tor_strdup("HSDir")); - smartlist_sort_strings(v3_out->known_flags); - - if (options->ConsensusParams) { - v3_out->net_params = smartlist_new(); - smartlist_split_string(v3_out->net_params, - options->ConsensusParams, NULL, 0, 0); - smartlist_sort_strings(v3_out->net_params); - } - - voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); - voter->nickname = tor_strdup(options->Nickname); - memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); - voter->sigs = smartlist_new(); - voter->address = hostname; - voter->addr = addr; - voter->dir_port = router_get_advertised_dir_port(options, 0); - voter->or_port = router_get_advertised_or_port(options); - voter->contact = tor_strdup(contact); - if (options->V3AuthUseLegacyKey) { - authority_cert_t *c = get_my_v3_legacy_cert(); - if (c) { - if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) { - log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key"); - memset(voter->legacy_id_digest, 0, DIGEST_LEN); - } - } - } - - v3_out->voters = smartlist_new(); - smartlist_add(v3_out->voters, voter); - v3_out->cert = authority_cert_dup(cert); - v3_out->routerstatus_list = routerstatuses; - /* Note: networkstatus_digest is unset; it won't get set until we actually - * format the vote. */ - - return v3_out; -} - -/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t - * pointers, adds copies of digests to fps_out, and doesn't use the - * /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other - * requests, adds identity digests. - */ -int -dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, - const char **msg, int for_unencrypted_conn, - int is_extrainfo) -{ - int by_id = 1; - *msg = NULL; - - if (!strcmp(key, "all")) { - routerlist_t *rl = router_get_routerlist(); - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, - smartlist_add(fps_out, - tor_memdup(r->cache_info.identity_digest, DIGEST_LEN))); - /* Treat "all" requests as if they were unencrypted */ - for_unencrypted_conn = 1; - } else if (!strcmp(key, "authority")) { - const routerinfo_t *ri = router_get_my_routerinfo(); - if (ri) - smartlist_add(fps_out, - tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN)); - } else if (!strcmpstart(key, "d/")) { - by_id = 0; - key += strlen("d/"); - dir_split_resource_into_fingerprints(key, fps_out, NULL, - DSR_HEX|DSR_SORT_UNIQ); - } else if (!strcmpstart(key, "fp/")) { - key += strlen("fp/"); - dir_split_resource_into_fingerprints(key, fps_out, NULL, - DSR_HEX|DSR_SORT_UNIQ); - } else { - *msg = "Key not recognized"; - return -1; - } - - if (for_unencrypted_conn) { - /* Remove anything that insists it not be sent unencrypted. */ - SMARTLIST_FOREACH_BEGIN(fps_out, char *, cp) { - const signed_descriptor_t *sd; - if (by_id) - sd = get_signed_descriptor_by_fp(cp,is_extrainfo,0); - else if (is_extrainfo) - sd = extrainfo_get_by_descriptor_digest(cp); - else - sd = router_get_by_descriptor_digest(cp); - if (sd && !sd->send_unencrypted) { - tor_free(cp); - SMARTLIST_DEL_CURRENT(fps_out, cp); - } - } SMARTLIST_FOREACH_END(cp); - } - - if (!smartlist_len(fps_out)) { - *msg = "Servers unavailable"; - return -1; - } - return 0; -} - -/** Add a signed_descriptor_t to descs_out for each router matching - * key. The key should be either - * - "/tor/server/authority" for our own routerinfo; - * - "/tor/server/all" for all the routerinfos we have, concatenated; - * - "/tor/server/fp/FP" where FP is a plus-separated sequence of - * hex identity digests; or - * - "/tor/server/d/D" where D is a plus-separated sequence - * of server descriptor digests, in hex. - * - * Return 0 if we found some matching descriptors, or -1 if we do not - * have any descriptors, no matching descriptors, or if we did not - * recognize the key (URL). - * If -1 is returned *msg will be set to an appropriate error - * message. - * - * XXXX rename this function. It's only called from the controller. - * XXXX in fact, refactor this function, merging as much as possible. - */ -int -dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, - const char **msg) -{ - *msg = NULL; - - if (!strcmp(key, "/tor/server/all")) { - routerlist_t *rl = router_get_routerlist(); - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, - smartlist_add(descs_out, &(r->cache_info))); - } else if (!strcmp(key, "/tor/server/authority")) { - const routerinfo_t *ri = router_get_my_routerinfo(); - if (ri) - smartlist_add(descs_out, (void*) &(ri->cache_info)); - } else if (!strcmpstart(key, "/tor/server/d/")) { - smartlist_t *digests = smartlist_new(); - key += strlen("/tor/server/d/"); - dir_split_resource_into_fingerprints(key, digests, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(digests, const char *, d, - { - signed_descriptor_t *sd = router_get_by_descriptor_digest(d); - if (sd) - smartlist_add(descs_out,sd); - }); - SMARTLIST_FOREACH(digests, char *, d, tor_free(d)); - smartlist_free(digests); - } else if (!strcmpstart(key, "/tor/server/fp/")) { - smartlist_t *digests = smartlist_new(); - time_t cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH; - key += strlen("/tor/server/fp/"); - dir_split_resource_into_fingerprints(key, digests, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH_BEGIN(digests, const char *, d) { - if (router_digest_is_me(d)) { - /* make sure desc_routerinfo exists */ - const routerinfo_t *ri = router_get_my_routerinfo(); - if (ri) - smartlist_add(descs_out, (void*) &(ri->cache_info)); - } else { - const routerinfo_t *ri = router_get_by_id_digest(d); - /* Don't actually serve a descriptor that everyone will think is - * expired. This is an (ugly) workaround to keep buggy 0.1.1.10 - * Tors from downloading descriptors that they will throw away. - */ - if (ri && ri->cache_info.published_on > cutoff) - smartlist_add(descs_out, (void*) &(ri->cache_info)); - } - } SMARTLIST_FOREACH_END(d); - SMARTLIST_FOREACH(digests, char *, d, tor_free(d)); - smartlist_free(digests); - } else { - *msg = "Key not recognized"; - return -1; - } - - if (!smartlist_len(descs_out)) { - *msg = "Servers unavailable"; - return -1; - } - return 0; -} - -/** Called when a TLS handshake has completed successfully with a - * router listening at address:or_port, and has yielded - * a certificate with digest digest_rcvd. - * - * Inform the reachability checker that we could get to this guy. - */ -void -dirserv_orconn_tls_done(const tor_addr_t *addr, - uint16_t or_port, - const char *digest_rcvd) -{ - node_t *node = NULL; - tor_addr_port_t orport; - routerinfo_t *ri = NULL; - time_t now = time(NULL); - tor_assert(addr); - tor_assert(digest_rcvd); - - node = node_get_mutable_by_id(digest_rcvd); - if (node == NULL || node->ri == NULL) - return; - ri = node->ri; - - 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. */ - log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.", - router_describe(ri), - 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; - } - } - } -} - -/** Called when we, as an authority, receive a new router descriptor either as - * an upload or a download. Used to decide whether to relaunch reachability - * testing for the server. */ -int -dirserv_should_launch_reachability_test(const routerinfo_t *ri, - const routerinfo_t *ri_old) -{ - if (!authdir_mode_handles_descs(get_options(), ri->purpose)) - return 0; - if (!ri_old) { - /* New router: Launch an immediate reachability test, so we will have an - * opinion soon in case we're generating a consensus soon */ - return 1; - } - if (ri_old->is_hibernating && !ri->is_hibernating) { - /* It just came out of hibernation; launch a reachability test */ - return 1; - } - if (! routers_have_same_or_addrs(ri, ri_old)) { - /* Address or port changed; launch a reachability test */ - return 1; - } - return 0; -} - -/** Helper function for dirserv_test_reachability(). Start a TLS - * connection to router, and annotate it with when we started - * the test. */ -void -dirserv_single_reachability_test(time_t now, routerinfo_t *router) -{ - channel_t *chan = NULL; - node_t *node = NULL; - tor_addr_t router_addr; - (void) now; - - 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); - tor_addr_from_ipv4h(&router_addr, router->addr); - chan = channel_tls_connect(&router_addr, router->or_port, - router->cache_info.identity_digest); - if (chan) command_setup_channel(chan); - - /* Possible IPv6. */ - if (get_options()->AuthDirHasIPv6Connectivity == 1 && - !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); - chan = channel_tls_connect(&router->ipv6_addr, router->ipv6_orport, - router->cache_info.identity_digest); - if (chan) command_setup_channel(chan); - } -} - -/** Auth dir server only: load balance such that we only - * try a few connections per call. - * - * The load balancing is such that if we get called once every ten - * seconds, we will cycle through all the tests in - * REACHABILITY_TEST_CYCLE_PERIOD seconds (a bit over 20 minutes). - */ -void -dirserv_test_reachability(time_t now) -{ - /* XXX decide what to do here; see or-talk thread "purging old router - * information, revocation." -NM - * We can't afford to mess with this in 0.1.2.x. The reason is that - * if we stop doing reachability tests on some of routerlist, then - * we'll for-sure think they're down, which may have unexpected - * effects in other parts of the code. It doesn't hurt much to do - * the testing, and directory authorities are easy to upgrade. Let's - * wait til 0.2.0. -RD */ -// time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - routerlist_t *rl = router_get_routerlist(); - static char ctr = 0; - int bridge_auth = authdir_mode_bridge(get_options()); - - SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, router) { - const char *id_digest = router->cache_info.identity_digest; - if (router_is_me(router)) - continue; - if (bridge_auth && router->purpose != ROUTER_PURPOSE_BRIDGE) - continue; /* bridge authorities only test reachability on bridges */ -// if (router->cache_info.published_on > cutoff) -// continue; - if ((((uint8_t)id_digest[0]) % REACHABILITY_MODULO_PER_TEST) == ctr) { - dirserv_single_reachability_test(now, router); - } - } SMARTLIST_FOREACH_END(router); - ctr = (ctr + 1) % REACHABILITY_MODULO_PER_TEST; /* increment ctr */ -} - -/** Given a fingerprint fp which is either set if we're looking for a - * v2 status, or zeroes if we're looking for a v3 status, or a NUL-padded - * flavor name if we want a flavored v3 status, return a pointer to the - * appropriate cached dir object, or NULL if there isn't one available. */ -static cached_dir_t * -lookup_cached_dir_by_fp(const char *fp) -{ - cached_dir_t *d = NULL; - if (tor_digest_is_zero(fp) && cached_consensuses) { - d = strmap_get(cached_consensuses, "ns"); - } else if (memchr(fp, '\0', DIGEST_LEN) && cached_consensuses && - (d = strmap_get(cached_consensuses, fp))) { - /* this here interface is a nasty hack XXXX024 */; - } - return d; -} - -/** Remove from fps every networkstatus key where both - * a) we have a networkstatus document and - * b) it is not newer than cutoff. - * - * Return 1 if any items were present at all; else return 0. - */ -int -dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff) -{ - int found_any = 0; - SMARTLIST_FOREACH_BEGIN(fps, char *, digest) { - cached_dir_t *d = lookup_cached_dir_by_fp(digest); - if (!d) - continue; - found_any = 1; - if (d->published <= cutoff) { - tor_free(digest); - SMARTLIST_DEL_CURRENT(fps, digest); - } - } SMARTLIST_FOREACH_END(digest); - - return found_any; -} - -/** Return the cache-info for identity fingerprint fp, or - * its extra-info document if extrainfo is true. Return - * NULL if not found or if the descriptor is older than - * publish_cutoff. */ -static const signed_descriptor_t * -get_signed_descriptor_by_fp(const char *fp, int extrainfo, - time_t publish_cutoff) -{ - if (router_digest_is_me(fp)) { - if (extrainfo) - return &(router_get_my_extrainfo()->cache_info); - else - return &(router_get_my_routerinfo()->cache_info); - } else { - const routerinfo_t *ri = router_get_by_id_digest(fp); - if (ri && - ri->cache_info.published_on > publish_cutoff) { - if (extrainfo) - return extrainfo_get_by_descriptor_digest( - ri->cache_info.extra_info_digest); - else - return &ri->cache_info; - } - } - return NULL; -} - -/** Return true iff we have any of the documents (extrainfo or routerdesc) - * specified by the fingerprints in fps and spool_src. Used to - * decide whether to send a 404. */ -int -dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src) -{ - time_t publish_cutoff = time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH; - SMARTLIST_FOREACH_BEGIN(fps, const char *, fp) { - switch (spool_src) - { - case DIR_SPOOL_EXTRA_BY_DIGEST: - if (extrainfo_get_by_descriptor_digest(fp)) return 1; - break; - case DIR_SPOOL_SERVER_BY_DIGEST: - if (router_get_by_descriptor_digest(fp)) return 1; - break; - case DIR_SPOOL_EXTRA_BY_FP: - case DIR_SPOOL_SERVER_BY_FP: - if (get_signed_descriptor_by_fp(fp, - spool_src == DIR_SPOOL_EXTRA_BY_FP, publish_cutoff)) - return 1; - break; - } - } SMARTLIST_FOREACH_END(fp); - return 0; -} - -/** Return true iff any of the 256-bit elements in fps is the digest of - * a microdescriptor we have. */ -int -dirserv_have_any_microdesc(const smartlist_t *fps) -{ - microdesc_cache_t *cache = get_microdesc_cache(); - SMARTLIST_FOREACH(fps, const char *, fp, - if (microdesc_cache_lookup_by_digest256(cache, fp)) - return 1); - return 0; -} - -/** Return an approximate estimate of the number of bytes that will - * be needed to transmit the server descriptors (if is_serverdescs -- - * they can be either d/ or fp/ queries) or networkstatus objects (if - * !is_serverdescs) listed in fps. If compressed is set, - * we guess how large the data will be after compression. - * - * The return value is an estimate; it might be larger or smaller. - **/ -size_t -dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs, - int compressed) -{ - size_t result; - tor_assert(fps); - if (is_serverdescs) { - int n = smartlist_len(fps); - const routerinfo_t *me = router_get_my_routerinfo(); - result = (me?me->cache_info.signed_descriptor_len:2048) * n; - if (compressed) - result /= 2; /* observed compressibility is between 35 and 55%. */ - } else { - result = 0; - SMARTLIST_FOREACH(fps, const char *, digest, { - cached_dir_t *dir = lookup_cached_dir_by_fp(digest); - if (dir) - result += compressed ? dir->dir_z_len : dir->dir_len; - }); - } - return result; -} - -/** Given a list of microdescriptor hashes, guess how many bytes will be - * needed to transmit them, and return the guess. */ -size_t -dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed) -{ - size_t result = smartlist_len(fps) * microdesc_average_size(NULL); - if (compressed) - result /= 2; - return result; -} - -/** When we're spooling data onto our outbuf, add more whenever we dip - * below this threshold. */ -#define DIRSERV_BUFFER_MIN 16384 - -/** Spooling helper: called when we have no more data to spool to conn. - * Flushes any remaining data to be (un)compressed, and changes the spool - * source to NONE. Returns 0 on success, negative on failure. */ -static int -connection_dirserv_finish_spooling(dir_connection_t *conn) -{ - if (conn->zlib_state) { - connection_write_to_buf_zlib("", 0, conn, 1); - tor_zlib_free(conn->zlib_state); - conn->zlib_state = NULL; - } - conn->dir_spool_src = DIR_SPOOL_NONE; - return 0; -} - -/** Spooling helper: called when we're sending a bunch of server descriptors, - * and the outbuf has become too empty. Pulls some entries from - * fingerprint_stack, and writes the corresponding servers onto outbuf. If we - * run out of entries, flushes the zlib state and sets the spool source to - * NONE. Returns 0 on success, negative on failure. - */ -static int -connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn) -{ - int by_fp = (conn->dir_spool_src == DIR_SPOOL_SERVER_BY_FP || - conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_FP); - int extra = (conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_FP || - conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_DIGEST); - time_t publish_cutoff = time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH; - - const or_options_t *options = get_options(); - - while (smartlist_len(conn->fingerprint_stack) && - connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) { - const char *body; - char *fp = smartlist_pop_last(conn->fingerprint_stack); - const signed_descriptor_t *sd = NULL; - if (by_fp) { - sd = get_signed_descriptor_by_fp(fp, extra, publish_cutoff); - } else { - sd = extra ? extrainfo_get_by_descriptor_digest(fp) - : router_get_by_descriptor_digest(fp); - } - tor_free(fp); - if (!sd) - continue; - if (!connection_dir_is_encrypted(conn) && !sd->send_unencrypted) { - /* we did this check once before (so we could have an accurate size - * estimate and maybe send a 404 if somebody asked for only bridges on a - * connection), but we need to do it again in case a previously - * unknown bridge descriptor has shown up between then and now. */ - continue; - } - - /** If we are the bridge authority and the descriptor is a bridge - * descriptor, remember that we served this descriptor for desc stats. */ - if (options->BridgeAuthoritativeDir && by_fp) { - const routerinfo_t *router = - router_get_by_id_digest(sd->identity_digest); - /* router can be NULL here when the bridge auth is asked for its own - * descriptor. */ - if (router && router->purpose == ROUTER_PURPOSE_BRIDGE) - rep_hist_note_desc_served(sd->identity_digest); - } - body = signed_descriptor_get_body(sd); - if (conn->zlib_state) { - /* XXXX024 This 'last' business should actually happen on the last - * routerinfo, not on the last fingerprint. */ - int last = ! smartlist_len(conn->fingerprint_stack); - connection_write_to_buf_zlib(body, sd->signed_descriptor_len, conn, - last); - if (last) { - tor_zlib_free(conn->zlib_state); - conn->zlib_state = NULL; - } - } else { - connection_write_to_buf(body, - sd->signed_descriptor_len, - TO_CONN(conn)); - } - } - - if (!smartlist_len(conn->fingerprint_stack)) { - /* We just wrote the last one; finish up. */ - conn->dir_spool_src = DIR_SPOOL_NONE; - smartlist_free(conn->fingerprint_stack); - conn->fingerprint_stack = NULL; - } - return 0; -} - -/** Spooling helper: called when we're sending a bunch of microdescriptors, - * and the outbuf has become too empty. Pulls some entries from - * fingerprint_stack, and writes the corresponding microdescs onto outbuf. If - * we run out of entries, flushes the zlib state and sets the spool source to - * NONE. Returns 0 on success, negative on failure. - */ -static int -connection_dirserv_add_microdescs_to_outbuf(dir_connection_t *conn) -{ - microdesc_cache_t *cache = get_microdesc_cache(); - while (smartlist_len(conn->fingerprint_stack) && - connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) { - char *fp256 = smartlist_pop_last(conn->fingerprint_stack); - microdesc_t *md = microdesc_cache_lookup_by_digest256(cache, fp256); - tor_free(fp256); - if (!md || !md->body) - continue; - if (conn->zlib_state) { - /* XXXX024 This 'last' business should actually happen on the last - * routerinfo, not on the last fingerprint. */ - int last = !smartlist_len(conn->fingerprint_stack); - connection_write_to_buf_zlib(md->body, md->bodylen, conn, last); - if (last) { - tor_zlib_free(conn->zlib_state); - conn->zlib_state = NULL; - } - } else { - connection_write_to_buf(md->body, md->bodylen, TO_CONN(conn)); - } - } - if (!smartlist_len(conn->fingerprint_stack)) { - conn->dir_spool_src = DIR_SPOOL_NONE; - smartlist_free(conn->fingerprint_stack); - conn->fingerprint_stack = NULL; - } - return 0; -} - -/** Spooling helper: Called when we're sending a directory or networkstatus, - * and the outbuf has become too empty. Pulls some bytes from - * conn-\>cached_dir-\>dir_z, uncompresses them if appropriate, and - * puts them on the outbuf. If we run out of entries, flushes the zlib state - * and sets the spool source to NONE. Returns 0 on success, negative on - * failure. */ -static int -connection_dirserv_add_dir_bytes_to_outbuf(dir_connection_t *conn) -{ - ssize_t bytes; - int64_t remaining; - - bytes = DIRSERV_BUFFER_MIN - connection_get_outbuf_len(TO_CONN(conn)); - tor_assert(bytes > 0); - tor_assert(conn->cached_dir); - if (bytes < 8192) - bytes = 8192; - remaining = conn->cached_dir->dir_z_len - conn->cached_dir_offset; - if (bytes > remaining) - bytes = (ssize_t) remaining; - - if (conn->zlib_state) { - connection_write_to_buf_zlib( - conn->cached_dir->dir_z + conn->cached_dir_offset, - bytes, conn, bytes == remaining); - } else { - connection_write_to_buf(conn->cached_dir->dir_z + conn->cached_dir_offset, - bytes, TO_CONN(conn)); - } - conn->cached_dir_offset += bytes; - if (conn->cached_dir_offset == (int)conn->cached_dir->dir_z_len) { - /* We just wrote the last one; finish up. */ - connection_dirserv_finish_spooling(conn); - cached_dir_decref(conn->cached_dir); - conn->cached_dir = NULL; - } - return 0; -} - -/** Spooling helper: Called when we're spooling networkstatus objects on - * conn, and the outbuf has become too empty. If the current - * networkstatus object (in conn-\>cached_dir) has more data, pull data - * from there. Otherwise, pop the next fingerprint from fingerprint_stack, - * and start spooling the next networkstatus. (A digest of all 0 bytes is - * treated as a request for the current consensus.) If we run out of entries, - * flushes the zlib state and sets the spool source to NONE. Returns 0 on - * success, negative on failure. */ -static int -connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn) -{ - - while (connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) { - if (conn->cached_dir) { - int uncompressing = (conn->zlib_state != NULL); - int r = connection_dirserv_add_dir_bytes_to_outbuf(conn); - if (conn->dir_spool_src == DIR_SPOOL_NONE) { - /* add_dir_bytes thinks we're done with the cached_dir. But we - * may have more cached_dirs! */ - conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS; - /* This bit is tricky. If we were uncompressing the last - * networkstatus, we may need to make a new zlib object to - * uncompress the next one. */ - if (uncompressing && ! conn->zlib_state && - conn->fingerprint_stack && - smartlist_len(conn->fingerprint_stack)) { - conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); - } - } - if (r) return r; - } else if (conn->fingerprint_stack && - smartlist_len(conn->fingerprint_stack)) { - /* Add another networkstatus; start serving it. */ - char *fp = smartlist_pop_last(conn->fingerprint_stack); - cached_dir_t *d = lookup_cached_dir_by_fp(fp); - tor_free(fp); - if (d) { - ++d->refcnt; - conn->cached_dir = d; - conn->cached_dir_offset = 0; - } - } else { - connection_dirserv_finish_spooling(conn); - smartlist_free(conn->fingerprint_stack); - conn->fingerprint_stack = NULL; - return 0; - } - } - return 0; -} - -/** Called whenever we have flushed some directory data in state - * SERVER_WRITING. */ -int -connection_dirserv_flushed_some(dir_connection_t *conn) -{ - tor_assert(conn->base_.state == DIR_CONN_STATE_SERVER_WRITING); - - if (connection_get_outbuf_len(TO_CONN(conn)) >= DIRSERV_BUFFER_MIN) - return 0; - - switch (conn->dir_spool_src) { - case DIR_SPOOL_EXTRA_BY_DIGEST: - case DIR_SPOOL_EXTRA_BY_FP: - case DIR_SPOOL_SERVER_BY_DIGEST: - case DIR_SPOOL_SERVER_BY_FP: - return connection_dirserv_add_servers_to_outbuf(conn); - case DIR_SPOOL_MICRODESC: - return connection_dirserv_add_microdescs_to_outbuf(conn); - case DIR_SPOOL_CACHED_DIR: - return connection_dirserv_add_dir_bytes_to_outbuf(conn); - case DIR_SPOOL_NETWORKSTATUS: - return connection_dirserv_add_networkstatus_bytes_to_outbuf(conn); - case DIR_SPOOL_NONE: - default: - return 0; - } -} - -/** Release all storage used by the directory server. */ -void -dirserv_free_all(void) -{ - dirserv_free_fingerprint_list(); - - cached_dir_decref(the_directory); - clear_cached_dir(&the_runningrouters); - cached_dir_decref(cached_directory); - clear_cached_dir(&cached_runningrouters); - - strmap_free(cached_consensuses, free_cached_dir_); - cached_consensuses = NULL; - - dirserv_clear_measured_bw_cache(); -} - diff --git a/src/tor/dirserv.h b/src/tor/dirserv.h deleted file mode 100644 index 8591c4c..0000000 --- a/src/tor/dirserv.h +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dirserv.h - * \brief Header file for dirserv.c. - **/ - -#ifndef TOR_DIRSERV_H -#define TOR_DIRSERV_H - -#include "testsupport.h" - -/** What fraction (1 over this number) of the relay ID space do we - * (as a directory authority) launch connections to at each reachability - * test? */ -#define REACHABILITY_MODULO_PER_TEST 128 - -/** How often (in seconds) do we launch reachability tests? */ -#define REACHABILITY_TEST_INTERVAL 10 - -/** How many seconds apart are the reachability tests for a given relay? */ -#define REACHABILITY_TEST_CYCLE_PERIOD \ - (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST) - -/** Maximum length of an exit policy summary. */ -#define MAX_EXITPOLICY_SUMMARY_LEN 1000 - -/** Maximum allowable length of a version line in a networkstatus. */ -#define MAX_V_LINE_LEN 128 - -int connection_dirserv_flushed_some(dir_connection_t *conn); - -int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_t *pk); -int dirserv_load_fingerprint_file(void); -void dirserv_free_fingerprint_list(void); -const char *dirserv_get_nickname_by_digest(const char *digest); -enum was_router_added_t dirserv_add_multiple_descriptors( - const char *desc, uint8_t purpose, - const char *source, - const char **msg); -enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri, - const char **msg, - const char *source); -void dirserv_set_router_is_running(routerinfo_t *router, time_t now); -int list_server_status_v1(smartlist_t *routers, char **router_status_out, - int for_controller); -int dirserv_dump_directory_to_string(char **dir_out, - crypto_pk_t *private_key); -char *dirserv_get_flag_thresholds_line(void); -void dirserv_compute_bridge_flag_thresholds(routerlist_t *rl); - -int directory_fetches_from_authorities(const or_options_t *options); -int directory_fetches_dir_info_early(const or_options_t *options); -int directory_fetches_dir_info_later(const or_options_t *options); -int directory_caches_unknown_auth_certs(const or_options_t *options); -int directory_caches_dir_info(const or_options_t *options); -int directory_permits_begindir_requests(const or_options_t *options); -int directory_permits_controller_requests(const or_options_t *options); -int directory_too_idle_to_fetch_descriptors(const or_options_t *options, - time_t now); - -void directory_set_dirty(void); -cached_dir_t *dirserv_get_directory(void); -cached_dir_t *dirserv_get_runningrouters(void); -cached_dir_t *dirserv_get_consensus(const char *flavor_name); -void dirserv_set_cached_consensus_networkstatus(const char *consensus, - const char *flavor_name, - const digests_t *digests, - time_t published); -void dirserv_clear_old_networkstatuses(time_t cutoff); -void dirserv_clear_old_v1_info(time_t now); -int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, - const char **msg, - int for_unencrypted_conn, - int is_extrainfo); -int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, - const char **msg); -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, - const routerinfo_t *ri_old); -void dirserv_single_reachability_test(time_t now, routerinfo_t *router); -void dirserv_test_reachability(time_t now); -int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, - int complain, - int *valid_out); -uint32_t dirserv_router_get_status(const routerinfo_t *router, - const char **msg); -void dirserv_set_node_flags_from_authoritative_status(node_t *node, - uint32_t authstatus); - -int dirserv_would_reject_router(const routerstatus_t *rs); -int dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff); -int dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src); -int dirserv_have_any_microdesc(const smartlist_t *fps); -size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs, - int compressed); -size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed); - -char *routerstatus_format_entry( - const routerstatus_t *rs, const char *platform, - routerstatus_format_type_t format, - const vote_routerstatus_t *vrs); -void dirserv_free_all(void); -void cached_dir_decref(cached_dir_t *d); -cached_dir_t *new_cached_dir(char *s, time_t published); - -#ifdef DIRSERV_PRIVATE - -/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */ -#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */ - -STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line); - -STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, - smartlist_t *routerstatuses); - -STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, - time_t as_of); -STATIC void dirserv_clear_measured_bw_cache(void); -STATIC void dirserv_expire_measured_bw_cache(time_t now); -STATIC int dirserv_get_measured_bw_cache_size(void); -STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id, - long *bw_out, - time_t *as_of_out); -STATIC int dirserv_has_measured_bw(const char *node_id); -#endif - -int dirserv_read_measured_bandwidths(const char *from_file, - smartlist_t *routerstatuses); - -#endif - diff --git a/src/tor/dirvote.c b/src/tor/dirvote.c deleted file mode 100644 index 4d3ee9c..0000000 --- a/src/tor/dirvote.c +++ /dev/null @@ -1,3778 +0,0 @@ -/* Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#define DIRVOTE_PRIVATE -#include "or.h" -#include "config.h" -#include "directory.h" -#include "dirserv.h" -#include "dirvote.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "policies.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" - -/** - * \file dirvote.c - * \brief Functions to compute directory consensus, and schedule voting. - **/ - -/** A consensus that we have built and are appending signatures to. Once it's - * time to publish it, it will become an active consensus if it accumulates - * enough signatures. */ -typedef struct pending_consensus_t { - /** The body of the consensus that we're currently building. Once we - * have it built, it goes into dirserv.c */ - char *body; - /** The parsed in-progress consensus document. */ - networkstatus_t *consensus; -} pending_consensus_t; - -/* DOCDOC dirvote_add_signatures_to_all_pending_consensuses */ -static int dirvote_add_signatures_to_all_pending_consensuses( - const char *detached_signatures_body, - const char *source, - const char **msg_out); -static int dirvote_add_signatures_to_pending_consensus( - pending_consensus_t *pc, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out); -static char *list_v3_auth_ids(void); -static void dirvote_fetch_missing_votes(void); -static void dirvote_fetch_missing_signatures(void); -static int dirvote_perform_vote(void); -static void dirvote_clear_votes(int all_votes); -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); - -/* ===== - * Voting - * =====*/ - -/** Return a new string containing the string representation of the vote in - * v3_ns, signed with our v3 signing key private_signing_key. - * For v3 authorities. */ -STATIC char * -format_networkstatus_vote(crypto_pk_t *private_signing_key, - networkstatus_t *v3_ns) -{ - smartlist_t *chunks; - const char *client_versions = NULL, *server_versions = NULL; - char fingerprint[FINGERPRINT_LEN+1]; - char digest[DIGEST_LEN]; - uint32_t addr; - char *client_versions_line = NULL, *server_versions_line = NULL; - networkstatus_voter_info_t *voter; - char *status = NULL; - - tor_assert(private_signing_key); - tor_assert(v3_ns->type == NS_TYPE_VOTE || v3_ns->type == NS_TYPE_OPINION); - - voter = smartlist_get(v3_ns->voters, 0); - - addr = voter->addr; - - base16_encode(fingerprint, sizeof(fingerprint), - v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); - client_versions = v3_ns->client_versions; - server_versions = v3_ns->server_versions; - - if (client_versions) { - tor_asprintf(&client_versions_line, "client-versions %s\n", - client_versions); - } else { - client_versions_line = tor_strdup(""); - } - if (server_versions) { - tor_asprintf(&server_versions_line, "server-versions %s\n", - server_versions); - } else { - server_versions_line = tor_strdup(""); - } - - chunks = smartlist_new(); - { - char published[ISO_TIME_LEN+1]; - char va[ISO_TIME_LEN+1]; - char fu[ISO_TIME_LEN+1]; - char vu[ISO_TIME_LEN+1]; - char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL); - /* XXXX Abstraction violation: should be pulling a field out of v3_ns.*/ - char *flag_thresholds = dirserv_get_flag_thresholds_line(); - char *params; - authority_cert_t *cert = v3_ns->cert; - char *methods = - make_consensus_method_list(1, MAX_SUPPORTED_CONSENSUS_METHOD, " "); - format_iso_time(published, v3_ns->published); - format_iso_time(va, v3_ns->valid_after); - format_iso_time(fu, v3_ns->fresh_until); - format_iso_time(vu, v3_ns->valid_until); - - if (v3_ns->net_params) - params = smartlist_join_strings(v3_ns->net_params, " ", 0, NULL); - else - params = tor_strdup(""); - - tor_assert(cert); - smartlist_add_asprintf(chunks, - "network-status-version 3\n" - "vote-status %s\n" - "consensus-methods %s\n" - "published %s\n" - "valid-after %s\n" - "fresh-until %s\n" - "valid-until %s\n" - "voting-delay %d %d\n" - "%s%s" /* versions */ - "known-flags %s\n" - "flag-thresholds %s\n" - "params %s\n" - "dir-source %s %s %s %s %d %d\n" - "contact %s\n", - v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", - methods, - published, va, fu, vu, - v3_ns->vote_seconds, v3_ns->dist_seconds, - client_versions_line, - server_versions_line, - flags, - flag_thresholds, - params, - voter->nickname, fingerprint, voter->address, - fmt_addr32(addr), voter->dir_port, voter->or_port, - voter->contact); - - tor_free(params); - tor_free(flags); - tor_free(flag_thresholds); - tor_free(methods); - - if (!tor_digest_is_zero(voter->legacy_id_digest)) { - char fpbuf[HEX_DIGEST_LEN+1]; - base16_encode(fpbuf, sizeof(fpbuf), voter->legacy_id_digest, DIGEST_LEN); - smartlist_add_asprintf(chunks, "legacy-dir-key %s\n", fpbuf); - } - - smartlist_add(chunks, tor_strndup(cert->cache_info.signed_descriptor_body, - cert->cache_info.signed_descriptor_len)); - } - - SMARTLIST_FOREACH_BEGIN(v3_ns->routerstatus_list, vote_routerstatus_t *, - vrs) { - char *rsf; - vote_microdesc_hash_t *h; - rsf = routerstatus_format_entry(&vrs->status, - vrs->version, NS_V3_VOTE, vrs); - if (rsf) - smartlist_add(chunks, rsf); - - for (h = vrs->microdesc; h; h = h->next) { - smartlist_add(chunks, tor_strdup(h->microdesc_hash_line)); - } - } SMARTLIST_FOREACH_END(vrs); - - smartlist_add(chunks, tor_strdup("directory-footer\n")); - - /* The digest includes everything up through the space after - * directory-signature. (Yuck.) */ - crypto_digest_smartlist(digest, DIGEST_LEN, chunks, - "directory-signature ", DIGEST_SHA1); - - { - char signing_key_fingerprint[FINGERPRINT_LEN+1]; - if (crypto_pk_get_fingerprint(private_signing_key, - signing_key_fingerprint, 0)<0) { - log_warn(LD_BUG, "Unable to get fingerprint for signing key"); - goto err; - } - - smartlist_add_asprintf(chunks, "directory-signature %s %s\n", fingerprint, - signing_key_fingerprint); - } - - note_crypto_pk_op(SIGN_DIR); - { - char *sig = router_get_dirobj_signature(digest, DIGEST_LEN, - private_signing_key); - if (!sig) { - log_warn(LD_BUG, "Unable to sign networkstatus vote."); - goto err; - } - smartlist_add(chunks, sig); - } - - status = smartlist_join_strings(chunks, "", 0, NULL); - - { - networkstatus_t *v; - if (!(v = networkstatus_parse_vote_from_string(status, NULL, - v3_ns->type))) { - log_err(LD_BUG,"Generated a networkstatus %s we couldn't parse: " - "<<%s>>", - v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", status); - goto err; - } - networkstatus_vote_free(v); - } - - goto done; - - err: - tor_free(status); - done: - tor_free(client_versions_line); - tor_free(server_versions_line); - if (chunks) { - SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); - smartlist_free(chunks); - } - return status; -} - -/* ===== - * Consensus generation - * ===== */ - -/** Given a vote vote (not a consensus!), return its associated - * networkstatus_voter_info_t. */ -static networkstatus_voter_info_t * -get_voter(const networkstatus_t *vote) -{ - tor_assert(vote); - tor_assert(vote->type == NS_TYPE_VOTE); - tor_assert(vote->voters); - tor_assert(smartlist_len(vote->voters) == 1); - return smartlist_get(vote->voters, 0); -} - -/** Return the signature made by voter using the algorithm - * alg, or NULL if none is found. */ -document_signature_t * -voter_get_sig_by_algorithm(const networkstatus_voter_info_t *voter, - digest_algorithm_t alg) -{ - if (!voter->sigs) - return NULL; - SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, - if (sig->alg == alg) - return sig); - return NULL; -} - -/** Temporary structure used in constructing a list of dir-source entries - * for a consensus. One of these is generated for every vote, and one more - * for every legacy key in each vote. */ -typedef struct dir_src_ent_t { - networkstatus_t *v; - const char *digest; - int is_legacy; -} dir_src_ent_t; - -/** Helper for sorting networkstatus_t votes (not consensuses) by the - * hash of their voters' identity digests. */ -static int -compare_votes_by_authority_id_(const void **_a, const void **_b) -{ - const networkstatus_t *a = *_a, *b = *_b; - return fast_memcmp(get_voter(a)->identity_digest, - get_voter(b)->identity_digest, DIGEST_LEN); -} - -/** Helper: Compare the dir_src_ent_ts in *_a and *_b by - * their identity digests, and return -1, 0, or 1 depending on their - * ordering */ -static int -compare_dir_src_ents_by_authority_id_(const void **_a, const void **_b) -{ - const dir_src_ent_t *a = *_a, *b = *_b; - const networkstatus_voter_info_t *a_v = get_voter(a->v), - *b_v = get_voter(b->v); - const char *a_id, *b_id; - a_id = a->is_legacy ? a_v->legacy_id_digest : a_v->identity_digest; - b_id = b->is_legacy ? b_v->legacy_id_digest : b_v->identity_digest; - - return fast_memcmp(a_id, b_id, DIGEST_LEN); -} - -/** Given a sorted list of strings in, add every member to out - * that occurs more than min times. */ -static void -get_frequent_members(smartlist_t *out, smartlist_t *in, int min) -{ - char *cur = NULL; - int count = 0; - SMARTLIST_FOREACH_BEGIN(in, char *, cp) { - if (cur && !strcmp(cp, cur)) { - ++count; - } else { - if (count > min) - smartlist_add(out, cur); - cur = cp; - count = 1; - } - } SMARTLIST_FOREACH_END(cp); - if (count > min) - smartlist_add(out, cur); -} - -/** Given a sorted list of strings lst, return the member that appears - * most. Break ties in favor of later-occurring members. */ -#define get_most_frequent_member(lst) \ - smartlist_get_most_frequent_string(lst) - -/** Return 0 if and only if a and b are routerstatuses - * that come from the same routerinfo, with the same derived elements. - */ -static int -compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b) -{ - int r; - if ((r = fast_memcmp(a->status.identity_digest, b->status.identity_digest, - DIGEST_LEN))) - return r; - if ((r = fast_memcmp(a->status.descriptor_digest, - b->status.descriptor_digest, - DIGEST_LEN))) - return r; - if ((r = (int)(b->status.published_on - a->status.published_on))) - return r; - if ((r = strcmp(b->status.nickname, a->status.nickname))) - return r; - if ((r = (((int)b->status.addr) - ((int)a->status.addr)))) - return r; - if ((r = (((int)b->status.or_port) - ((int)a->status.or_port)))) - return r; - if ((r = (((int)b->status.dir_port) - ((int)a->status.dir_port)))) - return r; - return 0; -} - -/** Helper for sorting routerlists based on compare_vote_rs. */ -static int -compare_vote_rs_(const void **_a, const void **_b) -{ - const vote_routerstatus_t *a = *_a, *b = *_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, - * in favor of smaller descriptor digest. - */ -static vote_routerstatus_t * -compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, - 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; - time_t most_published = 0; - - /* compare_vote_rs_() sorts the items by identity digest (all the same), - * then by SD digest. That way, if we have a tie that the published_on - * date cannot tie, we use the descriptor with the smaller digest. - */ - smartlist_sort(votes, compare_vote_rs_); - SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { - if (cur && !compare_vote_rs(cur, rs)) { - ++cur_n; - } else { - if (cur && (cur_n > most_n || - (cur_n == most_n && - cur->status.published_on > most_published))) { - most = cur; - most_n = cur_n; - most_published = cur->status.published_on; - } - cur_n = 1; - cur = rs; - } - } SMARTLIST_FOREACH_END(rs); - - if (cur_n > most_n || - (cur && cur_n == most_n && cur->status.published_on > most_published)) { - most = cur; - most_n = cur_n; - most_published = cur->status.published_on; - } - - 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", - most->status.nickname, - fmt_addrport(&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(); - const char *best_microdesc_digest; - SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { - char d[DIGEST256_LEN]; - if (compare_vote_rs(rs, most)) - continue; - if (!vote_routerstatus_find_microdesc_hash(d, rs, consensus_method, - DIGEST_SHA256)) - smartlist_add(digests, tor_memdup(d, sizeof(d))); - } SMARTLIST_FOREACH_END(rs); - smartlist_sort_digests256(digests); - best_microdesc_digest = smartlist_get_most_frequent_digest256(digests); - if (best_microdesc_digest) - memcpy(microdesc_digest256_out, best_microdesc_digest, DIGEST256_LEN); - SMARTLIST_FOREACH(digests, char *, cp, tor_free(cp)); - smartlist_free(digests); - } - - return most; -} - -/** Sorting helper: compare two strings based on their values as base-ten - * positive integers. (Non-integers are treated as prior to all integers, and - * compared lexically.) */ -static int -cmp_int_strings_(const void **_a, const void **_b) -{ - const char *a = *_a, *b = *_b; - int ai = (int)tor_parse_long(a, 10, 1, INT_MAX, NULL, NULL); - int bi = (int)tor_parse_long(b, 10, 1, INT_MAX, NULL, NULL); - if (aisupported_methods); - smartlist_add_all(tmp, vote->supported_methods); - smartlist_sort(tmp, cmp_int_strings_); - smartlist_uniq(tmp, cmp_int_strings_, NULL); - smartlist_add_all(all_methods, tmp); - smartlist_clear(tmp); - }); - - smartlist_sort(all_methods, cmp_int_strings_); - get_frequent_members(acceptable_methods, all_methods, min); - n_ok = smartlist_len(acceptable_methods); - if (n_ok) { - const char *best = smartlist_get(acceptable_methods, n_ok-1); - result = (int)tor_parse_long(best, 10, 1, INT_MAX, NULL, NULL); - } else { - result = 1; - } - smartlist_free(tmp); - smartlist_free(all_methods); - smartlist_free(acceptable_methods); - return result; -} - -/** Return true iff method is a consensus method that we support. */ -static int -consensus_method_is_supported(int method) -{ - return (method >= 1) && (method <= MAX_SUPPORTED_CONSENSUS_METHOD); -} - -/** Return a newly allocated string holding the numbers between low and high - * (inclusive) that are supported consensus methods. */ -static char * -make_consensus_method_list(int low, int high, const char *separator) -{ - char *list; - - int i; - smartlist_t *lst; - lst = smartlist_new(); - for (i = low; i <= high; ++i) { - if (!consensus_method_is_supported(i)) - continue; - smartlist_add_asprintf(lst, "%d", i); - } - list = smartlist_join_strings(lst, separator, 0, NULL); - tor_assert(list); - SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp)); - smartlist_free(lst); - return list; -} - -/** Helper: given lst, a list of version strings such that every - * version appears once for every versioning voter who recommends it, return a - * newly allocated string holding the resulting client-versions or - * server-versions list. May change contents of lst */ -static char * -compute_consensus_versions_list(smartlist_t *lst, int n_versioning) -{ - int min = n_versioning / 2; - smartlist_t *good = smartlist_new(); - char *result; - sort_version_list(lst, 0); - get_frequent_members(good, lst, min); - result = smartlist_join_strings(good, ",", 0, NULL); - smartlist_free(good); - return result; -} - -/** Minimum number of directory authorities voting for a parameter to - * include it in the consensus, if consensus method 12 or later is to be - * used. See proposal 178 for details. */ -#define MIN_VOTES_FOR_PARAM 3 - -/** Helper: given a list of valid networkstatus_t, return a new string - * containing the contents of the consensus network parameter set. - */ -STATIC char * -dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) -{ - int i; - int32_t *vals; - - int cur_param_len; - const char *cur_param; - const char *eq; - char *result; - - const int n_votes = smartlist_len(votes); - smartlist_t *output; - smartlist_t *param_list = smartlist_new(); - - /* We require that the parameter lists in the votes are well-formed: that - is, that their keywords are unique and sorted, and that their values are - between INT32_MIN and INT32_MAX inclusive. This should be guaranteed by - the parsing code. */ - - vals = tor_malloc(sizeof(int)*n_votes); - - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - if (!v->net_params) - continue; - smartlist_add_all(param_list, v->net_params); - } SMARTLIST_FOREACH_END(v); - - if (smartlist_len(param_list) == 0) { - tor_free(vals); - smartlist_free(param_list); - return NULL; - } - - smartlist_sort_strings(param_list); - i = 0; - cur_param = smartlist_get(param_list, 0); - eq = strchr(cur_param, '='); - tor_assert(eq); - cur_param_len = (int)(eq+1 - cur_param); - - output = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) { - const char *next_param; - int ok=0; - eq = strchr(param, '='); - tor_assert(i total_authorities/2 || - i >= MIN_VOTES_FOR_PARAM) { - int32_t median = median_int32(vals, i); - char *out_string = tor_malloc(64+cur_param_len); - memcpy(out_string, param, cur_param_len); - tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); - smartlist_add(output, out_string); - } - - i = 0; - if (next_param) { - eq = strchr(next_param, '='); - cur_param_len = (int)(eq+1 - next_param); - } - } - } SMARTLIST_FOREACH_END(param); - - result = smartlist_join_strings(output, " ", 0, NULL); - SMARTLIST_FOREACH(output, char *, cp, tor_free(cp)); - smartlist_free(output); - smartlist_free(param_list); - tor_free(vals); - return result; -} - -#define RANGE_CHECK(a,b,c,d,e,f,g,mx) \ - ((a) >= 0 && (a) <= (mx) && (b) >= 0 && (b) <= (mx) && \ - (c) >= 0 && (c) <= (mx) && (d) >= 0 && (d) <= (mx) && \ - (e) >= 0 && (e) <= (mx) && (f) >= 0 && (f) <= (mx) && \ - (g) >= 0 && (g) <= (mx)) - -#define CHECK_EQ(a, b, margin) \ - ((a)-(b) >= 0 ? (a)-(b) <= (margin) : (b)-(a) <= (margin)) - -typedef enum { - BW_WEIGHTS_NO_ERROR = 0, - BW_WEIGHTS_RANGE_ERROR = 1, - BW_WEIGHTS_SUMG_ERROR = 2, - BW_WEIGHTS_SUME_ERROR = 3, - BW_WEIGHTS_SUMD_ERROR = 4, - BW_WEIGHTS_BALANCE_MID_ERROR = 5, - BW_WEIGHTS_BALANCE_EG_ERROR = 6 -} bw_weights_error_t; - -/** - * Verify that any weightings satisfy the balanced formulas. - */ -static bw_weights_error_t -networkstatus_check_weights(int64_t Wgg, int64_t Wgd, int64_t Wmg, - int64_t Wme, int64_t Wmd, int64_t Wee, - int64_t Wed, int64_t scale, int64_t G, - int64_t M, int64_t E, int64_t D, int64_t T, - int64_t margin, int do_balance) { - bw_weights_error_t berr = BW_WEIGHTS_NO_ERROR; - - // Wed + Wmd + Wgd == 1 - if (!CHECK_EQ(Wed + Wmd + Wgd, scale, margin)) { - berr = BW_WEIGHTS_SUMD_ERROR; - goto out; - } - - // Wmg + Wgg == 1 - if (!CHECK_EQ(Wmg + Wgg, scale, margin)) { - berr = BW_WEIGHTS_SUMG_ERROR; - goto out; - } - - // Wme + Wee == 1 - if (!CHECK_EQ(Wme + Wee, scale, margin)) { - berr = BW_WEIGHTS_SUME_ERROR; - goto out; - } - - // Verify weights within range 0->1 - if (!RANGE_CHECK(Wgg, Wgd, Wmg, Wme, Wmd, Wed, Wee, scale)) { - berr = BW_WEIGHTS_RANGE_ERROR; - goto out; - } - - if (do_balance) { - // Wgg*G + Wgd*D == Wee*E + Wed*D, already scaled - if (!CHECK_EQ(Wgg*G + Wgd*D, Wee*E + Wed*D, (margin*T)/3)) { - berr = BW_WEIGHTS_BALANCE_EG_ERROR; - goto out; - } - - // Wgg*G + Wgd*D == M*scale + Wmd*D + Wme*E + Wmg*G, already scaled - if (!CHECK_EQ(Wgg*G + Wgd*D, M*scale + Wmd*D + Wme*E + Wmg*G, - (margin*T)/3)) { - berr = BW_WEIGHTS_BALANCE_MID_ERROR; - goto out; - } - } - - out: - if (berr) { - log_info(LD_DIR, - "Bw weight mismatch %d. G="I64_FORMAT" M="I64_FORMAT - " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT - " Wmd=%d Wme=%d Wmg=%d Wed=%d Wee=%d" - " Wgd=%d Wgg=%d Wme=%d Wmg=%d", - berr, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T), - (int)Wmd, (int)Wme, (int)Wmg, (int)Wed, (int)Wee, - (int)Wgd, (int)Wgg, (int)Wme, (int)Wmg); - } - - return berr; -} - -/** - * This function computes the bandwidth weights for consensus method 10. - * - * It returns true if weights could be computed, false otherwise. - */ -static int -networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, - int64_t M, int64_t E, int64_t D, - int64_t T, int64_t weight_scale) -{ - bw_weights_error_t berr = 0; - int64_t Wgg = -1, Wgd = -1; - int64_t Wmg = -1, Wme = -1, Wmd = -1; - int64_t Wed = -1, Wee = -1; - const char *casename; - - if (G <= 0 || M <= 0 || E <= 0 || D <= 0) { - log_warn(LD_DIR, "Consensus with empty bandwidth: " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT - " D="I64_FORMAT" T="I64_FORMAT, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - return 0; - } - - /* - * Computed from cases in 3.4.3 of dir-spec.txt - * - * 1. Neither are scarce - * 2. Both Guard and Exit are scarce - * a. R+D <= S - * b. R+D > S - * 3. One of Guard or Exit is scarce - * a. S+D < T/3 - * b. S+D >= T/3 - */ - if (3*E >= T && 3*G >= T) { // E >= T/3 && G >= T/3 - /* Case 1: Neither are scarce. */ - casename = "Case 1 (Wgd=Wmd=Wed)"; - Wgd = weight_scale/3; - Wed = weight_scale/3; - Wmd = weight_scale/3; - Wee = (weight_scale*(E+G+M))/(3*E); - Wme = weight_scale - Wee; - Wmg = (weight_scale*(2*G-E-M))/(3*G); - Wgg = weight_scale - Wmg; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed, - weight_scale, G, M, E, D, T, 10, 1); - - if (berr) { - log_warn(LD_DIR, - "Bw Weights error %d for %s v10. G="I64_FORMAT" M="I64_FORMAT - " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT - " Wmd=%d Wme=%d Wmg=%d Wed=%d Wee=%d" - " Wgd=%d Wgg=%d Wme=%d Wmg=%d weight_scale=%d", - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T), - (int)Wmd, (int)Wme, (int)Wmg, (int)Wed, (int)Wee, - (int)Wgd, (int)Wgg, (int)Wme, (int)Wmg, (int)weight_scale); - return 0; - } - } else if (3*E < T && 3*G < T) { // E < T/3 && G < T/3 - int64_t R = MIN(E, G); - int64_t S = MAX(E, G); - /* - * Case 2: Both Guards and Exits are scarce - * Balance D between E and G, depending upon - * D capacity and scarcity. - */ - if (R+D < S) { // Subcase a - Wgg = weight_scale; - Wee = weight_scale; - Wmg = 0; - Wme = 0; - Wmd = 0; - if (E < G) { - casename = "Case 2a (E scarce)"; - Wed = weight_scale; - Wgd = 0; - } else { /* E >= G */ - casename = "Case 2a (G scarce)"; - Wed = 0; - Wgd = weight_scale; - } - } else { // Subcase b: R+D >= S - casename = "Case 2b1 (Wgg=1, Wmd=Wgd)"; - Wee = (weight_scale*(E - G + M))/E; - Wed = (weight_scale*(D - 2*E + 4*G - 2*M))/(3*D); - Wme = (weight_scale*(G-M))/E; - Wmg = 0; - Wgg = weight_scale; - Wmd = (weight_scale - Wed)/2; - Wgd = (weight_scale - Wed)/2; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed, - weight_scale, G, M, E, D, T, 10, 1); - - if (berr) { - casename = "Case 2b2 (Wgg=1, Wee=1)"; - Wgg = weight_scale; - Wee = weight_scale; - Wed = (weight_scale*(D - 2*E + G + M))/(3*D); - Wmd = (weight_scale*(D - 2*M + G + E))/(3*D); - Wme = 0; - Wmg = 0; - - if (Wmd < 0) { // Can happen if M > T/3 - casename = "Case 2b3 (Wmd=0)"; - Wmd = 0; - log_warn(LD_DIR, - "Too much Middle bandwidth on the network to calculate " - "balanced bandwidth-weights. Consider increasing the " - "number of Guard nodes by lowering the requirements."); - } - Wgd = weight_scale - Wed - Wmd; - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } - if (berr != BW_WEIGHTS_NO_ERROR && - berr != BW_WEIGHTS_BALANCE_MID_ERROR) { - log_warn(LD_DIR, - "Bw Weights error %d for %s v10. G="I64_FORMAT" M="I64_FORMAT - " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT - " Wmd=%d Wme=%d Wmg=%d Wed=%d Wee=%d" - " Wgd=%d Wgg=%d Wme=%d Wmg=%d weight_scale=%d", - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T), - (int)Wmd, (int)Wme, (int)Wmg, (int)Wed, (int)Wee, - (int)Wgd, (int)Wgg, (int)Wme, (int)Wmg, (int)weight_scale); - return 0; - } - } - } else { // if (E < T/3 || G < T/3) { - int64_t S = MIN(E, G); - // Case 3: Exactly one of Guard or Exit is scarce - if (!(3*E < T || 3*G < T) || !(3*G >= T || 3*E >= T)) { - log_warn(LD_BUG, - "Bw-Weights Case 3 v10 but with G="I64_FORMAT" M=" - I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - } - - if (3*(S+D) < T) { // Subcase a: S+D < T/3 - if (G < E) { - casename = "Case 3a (G scarce)"; - Wgg = Wgd = weight_scale; - Wmd = Wed = Wmg = 0; - // Minor subcase, if E is more scarce than M, - // keep its bandwidth in place. - if (E < M) Wme = 0; - else Wme = (weight_scale*(E-M))/(2*E); - Wee = weight_scale-Wme; - } else { // G >= E - casename = "Case 3a (E scarce)"; - Wee = Wed = weight_scale; - Wmd = Wgd = Wme = 0; - // Minor subcase, if G is more scarce than M, - // keep its bandwidth in place. - if (G < M) Wmg = 0; - else Wmg = (weight_scale*(G-M))/(2*G); - Wgg = weight_scale-Wmg; - } - } else { // Subcase b: S+D >= T/3 - // D != 0 because S+D >= T/3 - if (G < E) { - casename = "Case 3bg (G scarce, Wgg=1, Wmd == Wed)"; - Wgg = weight_scale; - Wgd = (weight_scale*(D - 2*G + E + M))/(3*D); - Wmg = 0; - Wee = (weight_scale*(E+M))/(2*E); - Wme = weight_scale - Wee; - Wmd = (weight_scale - Wgd)/2; - Wed = (weight_scale - Wgd)/2; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } else { // G >= E - casename = "Case 3be (E scarce, Wee=1, Wmd == Wgd)"; - Wee = weight_scale; - Wed = (weight_scale*(D - 2*E + G + M))/(3*D); - Wme = 0; - Wgg = (weight_scale*(G+M))/(2*G); - Wmg = weight_scale - Wgg; - Wmd = (weight_scale - Wed)/2; - Wgd = (weight_scale - Wed)/2; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } - if (berr) { - log_warn(LD_DIR, - "Bw Weights error %d for %s v10. G="I64_FORMAT" M="I64_FORMAT - " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT - " Wmd=%d Wme=%d Wmg=%d Wed=%d Wee=%d" - " Wgd=%d Wgg=%d Wme=%d Wmg=%d weight_scale=%d", - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T), - (int)Wmd, (int)Wme, (int)Wmg, (int)Wed, (int)Wee, - (int)Wgd, (int)Wgg, (int)Wme, (int)Wmg, (int)weight_scale); - return 0; - } - } - } - - /* We cast down the weights to 32 bit ints on the assumption that - * weight_scale is ~= 10000. We need to ensure a rogue authority - * doesn't break this assumption to rig our weights */ - tor_assert(0 < weight_scale && weight_scale <= INT32_MAX); - - /* - * Provide Wgm=Wgg, Wmm=1, Wem=Wee, Weg=Wed. May later determine - * that middle nodes need different bandwidth weights for dirport traffic, - * or that weird exit policies need special weight, or that bridges - * need special weight. - * - * NOTE: This list is sorted. - */ - smartlist_add_asprintf(chunks, - "bandwidth-weights Wbd=%d Wbe=%d Wbg=%d Wbm=%d " - "Wdb=%d " - "Web=%d Wed=%d Wee=%d Weg=%d Wem=%d " - "Wgb=%d Wgd=%d Wgg=%d Wgm=%d " - "Wmb=%d Wmd=%d Wme=%d Wmg=%d Wmm=%d\n", - (int)Wmd, (int)Wme, (int)Wmg, (int)weight_scale, - (int)weight_scale, - (int)weight_scale, (int)Wed, (int)Wee, (int)Wed, (int)Wee, - (int)weight_scale, (int)Wgd, (int)Wgg, (int)Wgg, - (int)weight_scale, (int)Wmd, (int)Wme, (int)Wmg, (int)weight_scale); - - log_notice(LD_CIRC, "Computed bandwidth weights for %s with v10: " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - return 1; -} -/** - * This function computes the bandwidth weights for consensus method 9. - * - * It has been obsoleted in favor of consensus method 10. - */ -static void -networkstatus_compute_bw_weights_v9(smartlist_t *chunks, int64_t G, int64_t M, - int64_t E, int64_t D, int64_t T, - int64_t weight_scale) -{ - int64_t Wgg = -1, Wgd = -1; - int64_t Wmg = -1, Wme = -1, Wmd = -1; - int64_t Wed = -1, Wee = -1; - const char *casename; - - if (G <= 0 || M <= 0 || E <= 0 || D <= 0) { - log_warn(LD_DIR, "Consensus with empty bandwidth: " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT - " D="I64_FORMAT" T="I64_FORMAT, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - return; - } - - /* - * Computed from cases in 3.4.3 of dir-spec.txt - * - * 1. Neither are scarce - * 2. Both Guard and Exit are scarce - * a. R+D <= S - * b. R+D > S - * 3. One of Guard or Exit is scarce - * a. S+D < T/3 - * b. S+D >= T/3 - */ - if (3*E >= T && 3*G >= T) { // E >= T/3 && G >= T/3 - bw_weights_error_t berr = 0; - /* Case 1: Neither are scarce. - * - * Attempt to ensure that we have a large amount of exit bandwidth - * in the middle position. - */ - casename = "Case 1 (Wme*E = Wmd*D)"; - Wgg = (weight_scale*(D+E+G+M))/(3*G); - if (D==0) Wmd = 0; - else Wmd = (weight_scale*(2*D + 2*E - G - M))/(6*D); - Wme = (weight_scale*(2*D + 2*E - G - M))/(6*E); - Wee = (weight_scale*(-2*D + 4*E + G + M))/(6*E); - Wgd = 0; - Wmg = weight_scale - Wgg; - Wed = weight_scale - Wmd; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed, - weight_scale, G, M, E, D, T, 10, 1); - - if (berr) { - log_warn(LD_DIR, "Bw Weights error %d for case %s. " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT - " D="I64_FORMAT" T="I64_FORMAT, - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - } - } else if (3*E < T && 3*G < T) { // E < T/3 && G < T/3 - int64_t R = MIN(E, G); - int64_t S = MAX(E, G); - /* - * Case 2: Both Guards and Exits are scarce - * Balance D between E and G, depending upon - * D capacity and scarcity. - */ - if (R+D < S) { // Subcase a - Wgg = weight_scale; - Wee = weight_scale; - Wmg = 0; - Wme = 0; - Wmd = 0; - if (E < G) { - casename = "Case 2a (E scarce)"; - Wed = weight_scale; - Wgd = 0; - } else { /* E >= G */ - casename = "Case 2a (G scarce)"; - Wed = 0; - Wgd = weight_scale; - } - } else { // Subcase b: R+D > S - bw_weights_error_t berr = 0; - casename = "Case 2b (Wme*E == Wmd*D)"; - if (D != 0) { - Wgg = weight_scale; - Wgd = (weight_scale*(D + E - 2*G + M))/(3*D); // T/3 >= G (Ok) - Wmd = (weight_scale*(D + E + G - 2*M))/(6*D); // T/3 >= M - Wme = (weight_scale*(D + E + G - 2*M))/(6*E); - Wee = (weight_scale*(-D + 5*E - G + 2*M))/(6*E); // 2E+M >= T/3 - Wmg = 0; - Wed = weight_scale - Wgd - Wmd; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed, - weight_scale, G, M, E, D, T, 10, 1); - } - - if (D == 0 || berr) { // Can happen if M > T/3 - casename = "Case 2b (E=G)"; - Wgg = weight_scale; - Wee = weight_scale; - Wmg = 0; - Wme = 0; - Wmd = 0; - if (D == 0) Wgd = 0; - else Wgd = (weight_scale*(D+E-G))/(2*D); - Wed = weight_scale - Wgd; - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } - if (berr != BW_WEIGHTS_NO_ERROR && - berr != BW_WEIGHTS_BALANCE_MID_ERROR) { - log_warn(LD_DIR, "Bw Weights error %d for case %s. " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT - " D="I64_FORMAT" T="I64_FORMAT, - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - } - } - } else { // if (E < T/3 || G < T/3) { - int64_t S = MIN(E, G); - // Case 3: Exactly one of Guard or Exit is scarce - if (!(3*E < T || 3*G < T) || !(3*G >= T || 3*E >= T)) { - log_warn(LD_BUG, - "Bw-Weights Case 3 but with G="I64_FORMAT" M=" - I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - } - - if (3*(S+D) < T) { // Subcase a: S+D < T/3 - if (G < E) { - casename = "Case 3a (G scarce)"; - Wgg = Wgd = weight_scale; - Wmd = Wed = Wmg = 0; - // Minor subcase, if E is more scarce than M, - // keep its bandwidth in place. - if (E < M) Wme = 0; - else Wme = (weight_scale*(E-M))/(2*E); - Wee = weight_scale-Wme; - } else { // G >= E - casename = "Case 3a (E scarce)"; - Wee = Wed = weight_scale; - Wmd = Wgd = Wme = 0; - // Minor subcase, if G is more scarce than M, - // keep its bandwidth in place. - if (G < M) Wmg = 0; - else Wmg = (weight_scale*(G-M))/(2*G); - Wgg = weight_scale-Wmg; - } - } else { // Subcase b: S+D >= T/3 - bw_weights_error_t berr = 0; - // D != 0 because S+D >= T/3 - if (G < E) { - casename = "Case 3b (G scarce, Wme*E == Wmd*D)"; - Wgd = (weight_scale*(D + E - 2*G + M))/(3*D); - Wmd = (weight_scale*(D + E + G - 2*M))/(6*D); - Wme = (weight_scale*(D + E + G - 2*M))/(6*E); - Wee = (weight_scale*(-D + 5*E - G + 2*M))/(6*E); - Wgg = weight_scale; - Wmg = 0; - Wed = weight_scale - Wgd - Wmd; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } else { // G >= E - casename = "Case 3b (E scarce, Wme*E == Wmd*D)"; - Wgg = (weight_scale*(D + E + G + M))/(3*G); - Wmd = (weight_scale*(2*D + 2*E - G - M))/(6*D); - Wme = (weight_scale*(2*D + 2*E - G - M))/(6*E); - Wee = (weight_scale*(-2*D + 4*E + G + M))/(6*E); - Wgd = 0; - Wmg = weight_scale - Wgg; - Wed = weight_scale - Wmd; - - berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, - Wed, weight_scale, G, M, E, D, T, 10, 1); - } - if (berr) { - log_warn(LD_DIR, "Bw Weights error %d for case %s. " - "G="I64_FORMAT" M="I64_FORMAT - " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT, - berr, casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - } - } - } - - /* We cast down the weights to 32 bit ints on the assumption that - * weight_scale is ~= 10000. We need to ensure a rogue authority - * doesn't break this assumption to rig our weights */ - tor_assert(0 < weight_scale && weight_scale <= INT32_MAX); - - if (Wgg < 0 || Wgg > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wgg="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wgg), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - - Wgg = MAX(MIN(Wgg, weight_scale), 0); - } - if (Wgd < 0 || Wgd > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wgd="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wgd), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wgd = MAX(MIN(Wgd, weight_scale), 0); - } - if (Wmg < 0 || Wmg > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wmg="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wmg), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wmg = MAX(MIN(Wmg, weight_scale), 0); - } - if (Wme < 0 || Wme > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wme="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wme), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wme = MAX(MIN(Wme, weight_scale), 0); - } - if (Wmd < 0 || Wmd > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wmd="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wmd), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wmd = MAX(MIN(Wmd, weight_scale), 0); - } - if (Wee < 0 || Wee > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wee="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wee), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wee = MAX(MIN(Wee, weight_scale), 0); - } - if (Wed < 0 || Wed > weight_scale) { - log_warn(LD_DIR, "Bw %s: Wed="I64_FORMAT"! G="I64_FORMAT - " M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, I64_PRINTF_ARG(Wed), - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); - Wed = MAX(MIN(Wed, weight_scale), 0); - } - - // Add consensus weight keywords - smartlist_add(chunks, tor_strdup("bandwidth-weights ")); - /* - * Provide Wgm=Wgg, Wmm=1, Wem=Wee, Weg=Wed. May later determine - * that middle nodes need different bandwidth weights for dirport traffic, - * or that weird exit policies need special weight, or that bridges - * need special weight. - * - * NOTE: This list is sorted. - */ - smartlist_add_asprintf(chunks, - "Wbd=%d Wbe=%d Wbg=%d Wbm=%d " - "Wdb=%d " - "Web=%d Wed=%d Wee=%d Weg=%d Wem=%d " - "Wgb=%d Wgd=%d Wgg=%d Wgm=%d " - "Wmb=%d Wmd=%d Wme=%d Wmg=%d Wmm=%d\n", - (int)Wmd, (int)Wme, (int)Wmg, (int)weight_scale, - (int)weight_scale, - (int)weight_scale, (int)Wed, (int)Wee, (int)Wed, (int)Wee, - (int)weight_scale, (int)Wgd, (int)Wgg, (int)Wgg, - (int)weight_scale, (int)Wmd, (int)Wme, (int)Wmg, (int)weight_scale); - - log_notice(LD_CIRC, "Computed bandwidth weights for %s with v9: " - "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT - " T="I64_FORMAT, - casename, - I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E), - I64_PRINTF_ARG(D), I64_PRINTF_ARG(T)); -} - -/** Given a list of vote networkstatus_t in votes, our public - * authority identity_key, our private authority signing_key, - * and the number of total_authorities that we believe exist in our - * voting quorum, generate the text of a new v3 consensus vote, and return the - * value in a newly allocated string. - * - * Note: this function DOES NOT check whether the votes are from - * recognized authorities. (dirvote_add_vote does that.) */ -char * -networkstatus_compute_consensus(smartlist_t *votes, - int total_authorities, - crypto_pk_t *identity_key, - crypto_pk_t *signing_key, - const char *legacy_id_key_digest, - crypto_pk_t *legacy_signing_key, - consensus_flavor_t flavor) -{ - smartlist_t *chunks; - char *result = NULL; - int consensus_method; - time_t valid_after, fresh_until, valid_until; - int vote_seconds, dist_seconds; - char *client_versions = NULL, *server_versions = NULL; - smartlist_t *flags; - const char *flavor_name; - uint32_t max_unmeasured_bw_kb = DEFAULT_MAX_UNMEASURED_BW_KB; - int64_t G=0, M=0, E=0, D=0, T=0; /* For bandwidth weights */ - const routerstatus_format_type_t rs_format = - flavor == FLAV_NS ? NS_V3_CONSENSUS : NS_V3_CONSENSUS_MICRODESC; - char *params = NULL; - int added_weights = 0; - tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); - tor_assert(total_authorities >= smartlist_len(votes)); - - flavor_name = networkstatus_get_flavor_name(flavor); - - if (!smartlist_len(votes)) { - log_warn(LD_DIR, "Can't compute a consensus from no votes."); - return NULL; - } - flags = smartlist_new(); - - consensus_method = compute_consensus_method(votes); - if (consensus_method_is_supported(consensus_method)) { - log_info(LD_DIR, "Generating consensus using method %d.", - consensus_method); - } else { - log_warn(LD_DIR, "The other authorities will use consensus method %d, " - "which I don't support. Maybe I should upgrade!", - consensus_method); - consensus_method = 1; - } - - /* Compute medians of time-related things, and figure out how many - * routers we might need to talk about. */ - { - int n_votes = smartlist_len(votes); - time_t *va_times = tor_malloc(n_votes * sizeof(time_t)); - time_t *fu_times = tor_malloc(n_votes * sizeof(time_t)); - time_t *vu_times = tor_malloc(n_votes * sizeof(time_t)); - int *votesec_list = tor_malloc(n_votes * sizeof(int)); - int *distsec_list = tor_malloc(n_votes * sizeof(int)); - int n_versioning_clients = 0, n_versioning_servers = 0; - smartlist_t *combined_client_versions = smartlist_new(); - smartlist_t *combined_server_versions = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - tor_assert(v->type == NS_TYPE_VOTE); - va_times[v_sl_idx] = v->valid_after; - fu_times[v_sl_idx] = v->fresh_until; - vu_times[v_sl_idx] = v->valid_until; - votesec_list[v_sl_idx] = v->vote_seconds; - distsec_list[v_sl_idx] = v->dist_seconds; - if (v->client_versions) { - smartlist_t *cv = smartlist_new(); - ++n_versioning_clients; - smartlist_split_string(cv, v->client_versions, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - sort_version_list(cv, 1); - smartlist_add_all(combined_client_versions, cv); - smartlist_free(cv); /* elements get freed later. */ - } - if (v->server_versions) { - smartlist_t *sv = smartlist_new(); - ++n_versioning_servers; - smartlist_split_string(sv, v->server_versions, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - sort_version_list(sv, 1); - smartlist_add_all(combined_server_versions, sv); - smartlist_free(sv); /* elements get freed later. */ - } - SMARTLIST_FOREACH(v->known_flags, const char *, cp, - smartlist_add(flags, tor_strdup(cp))); - } SMARTLIST_FOREACH_END(v); - valid_after = median_time(va_times, n_votes); - fresh_until = median_time(fu_times, n_votes); - valid_until = median_time(vu_times, n_votes); - vote_seconds = median_int(votesec_list, n_votes); - dist_seconds = median_int(distsec_list, n_votes); - - tor_assert(valid_after+MIN_VOTE_INTERVAL <= fresh_until); - tor_assert(fresh_until+MIN_VOTE_INTERVAL <= valid_until); - tor_assert(vote_seconds >= MIN_VOTE_SECONDS); - tor_assert(dist_seconds >= MIN_DIST_SECONDS); - - server_versions = compute_consensus_versions_list(combined_server_versions, - n_versioning_servers); - client_versions = compute_consensus_versions_list(combined_client_versions, - n_versioning_clients); - - SMARTLIST_FOREACH(combined_server_versions, char *, cp, tor_free(cp)); - SMARTLIST_FOREACH(combined_client_versions, char *, cp, tor_free(cp)); - smartlist_free(combined_server_versions); - smartlist_free(combined_client_versions); - - smartlist_sort_strings(flags); - smartlist_uniq_strings(flags); - - tor_free(va_times); - tor_free(fu_times); - tor_free(vu_times); - tor_free(votesec_list); - tor_free(distsec_list); - } - - chunks = smartlist_new(); - - { - char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1], - vu_buf[ISO_TIME_LEN+1]; - char *flaglist; - format_iso_time(va_buf, valid_after); - format_iso_time(fu_buf, fresh_until); - format_iso_time(vu_buf, valid_until); - flaglist = smartlist_join_strings(flags, " ", 0, NULL); - - smartlist_add_asprintf(chunks, "network-status-version 3%s%s\n" - "vote-status consensus\n", - flavor == FLAV_NS ? "" : " ", - flavor == FLAV_NS ? "" : flavor_name); - - if (consensus_method >= 2) { - smartlist_add_asprintf(chunks, "consensus-method %d\n", - consensus_method); - } - - smartlist_add_asprintf(chunks, - "valid-after %s\n" - "fresh-until %s\n" - "valid-until %s\n" - "voting-delay %d %d\n" - "client-versions %s\n" - "server-versions %s\n" - "known-flags %s\n", - va_buf, fu_buf, vu_buf, - vote_seconds, dist_seconds, - client_versions, server_versions, flaglist); - - tor_free(flaglist); - } - - if (consensus_method >= MIN_METHOD_FOR_PARAMS) { - params = dirvote_compute_params(votes, consensus_method, - total_authorities); - if (params) { - smartlist_add(chunks, tor_strdup("params ")); - smartlist_add(chunks, params); - smartlist_add(chunks, tor_strdup("\n")); - } - } - - /* Sort the votes. */ - smartlist_sort(votes, compare_votes_by_authority_id_); - /* Add the authority sections. */ - { - smartlist_t *dir_sources = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - dir_src_ent_t *e = tor_malloc_zero(sizeof(dir_src_ent_t)); - e->v = v; - e->digest = get_voter(v)->identity_digest; - e->is_legacy = 0; - smartlist_add(dir_sources, e); - if (consensus_method >= 3 && - !tor_digest_is_zero(get_voter(v)->legacy_id_digest)) { - dir_src_ent_t *e_legacy = tor_malloc_zero(sizeof(dir_src_ent_t)); - e_legacy->v = v; - e_legacy->digest = get_voter(v)->legacy_id_digest; - e_legacy->is_legacy = 1; - smartlist_add(dir_sources, e_legacy); - } - } SMARTLIST_FOREACH_END(v); - smartlist_sort(dir_sources, compare_dir_src_ents_by_authority_id_); - - SMARTLIST_FOREACH_BEGIN(dir_sources, const dir_src_ent_t *, e) { - char fingerprint[HEX_DIGEST_LEN+1]; - char votedigest[HEX_DIGEST_LEN+1]; - networkstatus_t *v = e->v; - networkstatus_voter_info_t *voter = get_voter(v); - - if (e->is_legacy) - tor_assert(consensus_method >= 2); - - base16_encode(fingerprint, sizeof(fingerprint), e->digest, DIGEST_LEN); - base16_encode(votedigest, sizeof(votedigest), voter->vote_digest, - DIGEST_LEN); - - smartlist_add_asprintf(chunks, - "dir-source %s%s %s %s %s %d %d\n", - voter->nickname, e->is_legacy ? "-legacy" : "", - fingerprint, voter->address, fmt_addr32(voter->addr), - voter->dir_port, - voter->or_port); - if (! e->is_legacy) { - smartlist_add_asprintf(chunks, - "contact %s\n" - "vote-digest %s\n", - voter->contact, - votedigest); - } - } SMARTLIST_FOREACH_END(e); - SMARTLIST_FOREACH(dir_sources, dir_src_ent_t *, e, tor_free(e)); - smartlist_free(dir_sources); - } - - if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) { - char *max_unmeasured_param = NULL; - /* XXXX Extract this code into a common function */ - if (params) { - if (strcmpstart(params, "maxunmeasuredbw=") == 0) - max_unmeasured_param = params; - else - max_unmeasured_param = strstr(params, " maxunmeasuredbw="); - } - if (max_unmeasured_param) { - int ok = 0; - char *eq = strchr(max_unmeasured_param, '='); - if (eq) { - max_unmeasured_bw_kb = (uint32_t) - tor_parse_ulong(eq+1, 10, 1, UINT32_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_DIR, "Bad element '%s' in max unmeasured bw param", - escaped(max_unmeasured_param)); - max_unmeasured_bw_kb = DEFAULT_MAX_UNMEASURED_BW_KB; - } - } - } - } - - /* Add the actual router entries. */ - { - int *index; /* index[j] is the current index into votes[j]. */ - int *size; /* size[j] is the number of routerstatuses in votes[j]. */ - int *flag_counts; /* The number of voters that list flag[j] for the - * currently considered router. */ - int i; - smartlist_t *matching_descs = smartlist_new(); - smartlist_t *chosen_flags = smartlist_new(); - smartlist_t *versions = smartlist_new(); - smartlist_t *exitsummaries = smartlist_new(); - uint32_t *bandwidths_kb = tor_malloc(sizeof(uint32_t) * - smartlist_len(votes)); - uint32_t *measured_bws_kb = tor_malloc(sizeof(uint32_t) * - smartlist_len(votes)); - int num_bandwidths; - int num_mbws; - - int *n_voter_flags; /* n_voter_flags[j] is the number of flags that - * votes[j] knows about. */ - int *n_flag_voters; /* n_flag_voters[f] is the number of votes that care - * about flags[f]. */ - int **flag_map; /* flag_map[j][b] is an index f such that flag_map[f] - * is the same flag as votes[j]->known_flags[b]. */ - int *named_flag; /* Index of the flag "Named" for votes[j] */ - int *unnamed_flag; /* Index of the flag "Unnamed" for votes[j] */ - int chosen_named_idx; - int n_authorities_measuring_bandwidth; - - strmap_t *name_to_id_map = strmap_new(); - char conflict[DIGEST_LEN]; - char unknown[DIGEST_LEN]; - memset(conflict, 0, sizeof(conflict)); - memset(unknown, 0xff, sizeof(conflict)); - - index = tor_malloc_zero(sizeof(int)*smartlist_len(votes)); - size = tor_malloc_zero(sizeof(int)*smartlist_len(votes)); - n_voter_flags = tor_malloc_zero(sizeof(int) * smartlist_len(votes)); - n_flag_voters = tor_malloc_zero(sizeof(int) * smartlist_len(flags)); - flag_map = tor_malloc_zero(sizeof(int*) * smartlist_len(votes)); - named_flag = tor_malloc_zero(sizeof(int) * smartlist_len(votes)); - unnamed_flag = tor_malloc_zero(sizeof(int) * smartlist_len(votes)); - for (i = 0; i < smartlist_len(votes); ++i) - unnamed_flag[i] = named_flag[i] = -1; - chosen_named_idx = smartlist_string_pos(flags, "Named"); - - /* Build the flag indexes. Note that no vote can have more than 64 members - * for known_flags, so no value will be greater than 63, so it's safe to - * do U64_LITERAL(1) << index on these values. But note also that - * named_flag and unnamed_flag are initialized to -1, so we need to check - * that they're actually set before doing U64_LITERAL(1) << index with - * them.*/ - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - flag_map[v_sl_idx] = tor_malloc_zero( - sizeof(int)*smartlist_len(v->known_flags)); - if (smartlist_len(v->known_flags) > MAX_KNOWN_FLAGS_IN_VOTE) { - log_warn(LD_BUG, "Somehow, a vote has %d entries in known_flags", - smartlist_len(v->known_flags)); - } - SMARTLIST_FOREACH_BEGIN(v->known_flags, const char *, fl) { - int p = smartlist_string_pos(flags, fl); - tor_assert(p >= 0); - flag_map[v_sl_idx][fl_sl_idx] = p; - ++n_flag_voters[p]; - if (!strcmp(fl, "Named")) - named_flag[v_sl_idx] = fl_sl_idx; - if (!strcmp(fl, "Unnamed")) - unnamed_flag[v_sl_idx] = fl_sl_idx; - } SMARTLIST_FOREACH_END(fl); - n_voter_flags[v_sl_idx] = smartlist_len(v->known_flags); - size[v_sl_idx] = smartlist_len(v->routerstatus_list); - } SMARTLIST_FOREACH_END(v); - - /* Named and Unnamed get treated specially */ - if (consensus_method >= 2) { - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - uint64_t nf; - if (named_flag[v_sl_idx]<0) - continue; - nf = U64_LITERAL(1) << named_flag[v_sl_idx]; - SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, - vote_routerstatus_t *, rs) { - - if ((rs->flags & nf) != 0) { - const char *d = strmap_get_lc(name_to_id_map, rs->status.nickname); - if (!d) { - /* We have no name officially mapped to this digest. */ - strmap_set_lc(name_to_id_map, rs->status.nickname, - rs->status.identity_digest); - } else if (d != conflict && - fast_memcmp(d, rs->status.identity_digest, DIGEST_LEN)) { - /* Authorities disagree about this nickname. */ - strmap_set_lc(name_to_id_map, rs->status.nickname, conflict); - } else { - /* It's already a conflict, or it's already this ID. */ - } - } - } SMARTLIST_FOREACH_END(rs); - } SMARTLIST_FOREACH_END(v); - - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - uint64_t uf; - if (unnamed_flag[v_sl_idx]<0) - continue; - uf = U64_LITERAL(1) << unnamed_flag[v_sl_idx]; - SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, - vote_routerstatus_t *, rs) { - if ((rs->flags & uf) != 0) { - const char *d = strmap_get_lc(name_to_id_map, rs->status.nickname); - if (d == conflict || d == unknown) { - /* Leave it alone; we know what it is. */ - } else if (!d) { - /* We have no name officially mapped to this digest. */ - strmap_set_lc(name_to_id_map, rs->status.nickname, unknown); - } else if (fast_memeq(d, rs->status.identity_digest, DIGEST_LEN)) { - /* Authorities disagree about this nickname. */ - strmap_set_lc(name_to_id_map, rs->status.nickname, conflict); - } else { - /* It's mapped to a different name. */ - } - } - } SMARTLIST_FOREACH_END(rs); - } SMARTLIST_FOREACH_END(v); - } - - /* We need to know how many votes measure bandwidth. */ - n_authorities_measuring_bandwidth = 0; - SMARTLIST_FOREACH(votes, networkstatus_t *, v, - if (v->has_measured_bws) { - ++n_authorities_measuring_bandwidth; - } - ); - - /* Now go through all the votes */ - flag_counts = tor_malloc(sizeof(int) * smartlist_len(flags)); - while (1) { - vote_routerstatus_t *rs; - routerstatus_t rs_out; - const char *lowest_id = NULL; - const char *chosen_version; - const char *chosen_name = NULL; - int exitsummary_disagreement = 0; - int is_named = 0, is_unnamed = 0, is_running = 0; - int is_guard = 0, is_exit = 0, is_bad_exit = 0; - int naming_conflict = 0; - 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, { - if (index[v_sl_idx] < size[v_sl_idx]) { - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (!lowest_id || - fast_memcmp(rs->status.identity_digest, - lowest_id, DIGEST_LEN) < 0) - lowest_id = rs->status.identity_digest; - } - }); - if (!lowest_id) /* we're out of routers. */ - break; - - memset(flag_counts, 0, sizeof(int)*smartlist_len(flags)); - smartlist_clear(matching_descs); - smartlist_clear(chosen_flags); - smartlist_clear(versions); - num_bandwidths = 0; - num_mbws = 0; - - /* Okay, go through all the entries for this digest. */ - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - if (index[v_sl_idx] >= size[v_sl_idx]) - continue; /* out of entries. */ - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (fast_memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN)) - continue; /* doesn't include this router. */ - /* At this point, we know that we're looking at a routerstatus with - * identity "lowest". - */ - ++index[v_sl_idx]; - ++n_listing; - - smartlist_add(matching_descs, rs); - if (rs->version && rs->version[0]) - smartlist_add(versions, rs->version); - - /* Tally up all the flags. */ - for (i = 0; i < n_voter_flags[v_sl_idx]; ++i) { - if (rs->flags & (U64_LITERAL(1) << i)) - ++flag_counts[flag_map[v_sl_idx][i]]; - } - if (named_flag[v_sl_idx] >= 0 && - (rs->flags & (U64_LITERAL(1) << named_flag[v_sl_idx]))) { - if (chosen_name && strcmp(chosen_name, rs->status.nickname)) { - log_notice(LD_DIR, "Conflict on naming for router: %s vs %s", - chosen_name, rs->status.nickname); - naming_conflict = 1; - } - chosen_name = rs->status.nickname; - } - - /* count bandwidths */ - if (rs->has_measured_bw) - measured_bws_kb[num_mbws++] = rs->measured_bw_kb; - - if (rs->status.has_bandwidth) - bandwidths_kb[num_bandwidths++] = rs->status.bandwidth_kb; - } SMARTLIST_FOREACH_END(v); - - /* We don't include this router at all unless more than half of - * the authorities we believe in list it. */ - if (n_listing <= total_authorities/2) - continue; - - /* Figure out the most popular opinion of what the most recent - * routerinfo and its contents are. */ - memset(microdesc_digest, 0, sizeof(microdesc_digest)); - rs = compute_routerstatus_consensus(matching_descs, consensus_method, - 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, - DIGEST_LEN); - rs_out.addr = rs->status.addr; - 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; - - if (chosen_name && !naming_conflict) { - strlcpy(rs_out.nickname, chosen_name, sizeof(rs_out.nickname)); - } else { - strlcpy(rs_out.nickname, rs->status.nickname, sizeof(rs_out.nickname)); - } - - if (consensus_method == 1) { - is_named = chosen_named_idx >= 0 && - (!naming_conflict && flag_counts[chosen_named_idx]); - } else { - const char *d = strmap_get_lc(name_to_id_map, rs_out.nickname); - if (!d) { - is_named = is_unnamed = 0; - } else if (fast_memeq(d, lowest_id, DIGEST_LEN)) { - is_named = 1; is_unnamed = 0; - } else { - is_named = 0; is_unnamed = 1; - } - } - - /* Set the flags. */ - smartlist_add(chosen_flags, (char*)"s"); /* for the start of the line. */ - SMARTLIST_FOREACH_BEGIN(flags, const char *, fl) { - if (!strcmp(fl, "Named")) { - if (is_named) - smartlist_add(chosen_flags, (char*)fl); - } else if (!strcmp(fl, "Unnamed") && consensus_method >= 2) { - if (is_unnamed) - smartlist_add(chosen_flags, (char*)fl); - } else { - if (flag_counts[fl_sl_idx] > n_flag_voters[fl_sl_idx]/2) { - smartlist_add(chosen_flags, (char*)fl); - if (!strcmp(fl, "Exit")) - is_exit = 1; - else if (!strcmp(fl, "Guard")) - is_guard = 1; - else if (!strcmp(fl, "Running")) - is_running = 1; - else if (!strcmp(fl, "BadExit")) - is_bad_exit = 1; - } - } - } SMARTLIST_FOREACH_END(fl); - - /* Starting with consensus method 4 we do not list servers - * that are not running in a consensus. See Proposal 138 */ - if (consensus_method >= 4 && !is_running) - continue; - - /* Pick the version. */ - if (smartlist_len(versions)) { - sort_version_list(versions, 0); - chosen_version = get_most_frequent_member(versions); - } else { - chosen_version = NULL; - } - - /* Pick a bandwidth */ - if (consensus_method >= 6 && num_mbws > 2) { - rs_out.has_bandwidth = 1; - rs_out.bw_is_unmeasured = 0; - rs_out.bandwidth_kb = median_uint32(measured_bws_kb, num_mbws); - } else if (consensus_method >= 5 && num_bandwidths > 0) { - rs_out.has_bandwidth = 1; - rs_out.bw_is_unmeasured = 1; - rs_out.bandwidth_kb = median_uint32(bandwidths_kb, num_bandwidths); - if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW && - n_authorities_measuring_bandwidth > 2) { - /* Cap non-measured bandwidths. */ - if (rs_out.bandwidth_kb > max_unmeasured_bw_kb) { - rs_out.bandwidth_kb = max_unmeasured_bw_kb; - } - } - } - - /* Fix bug 2203: Do not count BadExit nodes as Exits for bw weights */ - if (consensus_method >= MIN_METHOD_TO_CUT_BADEXIT_WEIGHT) { - is_exit = is_exit && !is_bad_exit; - } - - if (consensus_method >= MIN_METHOD_FOR_BW_WEIGHTS) { - if (rs_out.has_bandwidth) { - T += rs_out.bandwidth_kb; - if (is_exit && is_guard) - D += rs_out.bandwidth_kb; - else if (is_exit) - E += rs_out.bandwidth_kb; - else if (is_guard) - G += rs_out.bandwidth_kb; - else - M += rs_out.bandwidth_kb; - } else { - log_warn(LD_BUG, "Missing consensus bandwidth for router %s", - rs_out.nickname); - } - } - - /* Ok, we already picked a descriptor digest we want to list - * previously. Now we want to use the exit policy summary from - * that descriptor. If everybody plays nice all the voters who - * listed that descriptor will have the same summary. If not then - * something is fishy and we'll use the most common one (breaking - * ties in favor of lexicographically larger one (only because it - * lets me reuse more existing code)). - * - * The other case that can happen is that no authority that voted - * for that descriptor has an exit policy summary. That's - * probably quite unlikely but can happen. In that case we use - * the policy that was most often listed in votes, again breaking - * ties like in the previous case. - */ - if (consensus_method >= 5) { - /* Okay, go through all the votes for this router. We prepared - * that list previously */ - const char *chosen_exitsummary = NULL; - smartlist_clear(exitsummaries); - SMARTLIST_FOREACH_BEGIN(matching_descs, vote_routerstatus_t *, vsr) { - /* Check if the vote where this status comes from had the - * proper descriptor */ - tor_assert(fast_memeq(rs_out.identity_digest, - vsr->status.identity_digest, - DIGEST_LEN)); - if (vsr->status.has_exitsummary && - fast_memeq(rs_out.descriptor_digest, - vsr->status.descriptor_digest, - DIGEST_LEN)) { - tor_assert(vsr->status.exitsummary); - smartlist_add(exitsummaries, vsr->status.exitsummary); - if (!chosen_exitsummary) { - chosen_exitsummary = vsr->status.exitsummary; - } else if (strcmp(chosen_exitsummary, vsr->status.exitsummary)) { - /* Great. There's disagreement among the voters. That - * really shouldn't be */ - exitsummary_disagreement = 1; - } - } - } SMARTLIST_FOREACH_END(vsr); - - if (exitsummary_disagreement) { - char id[HEX_DIGEST_LEN+1]; - char dd[HEX_DIGEST_LEN+1]; - base16_encode(id, sizeof(dd), rs_out.identity_digest, DIGEST_LEN); - base16_encode(dd, sizeof(dd), rs_out.descriptor_digest, DIGEST_LEN); - log_warn(LD_DIR, "The voters disagreed on the exit policy summary " - " for router %s with descriptor %s. This really shouldn't" - " have happened.", id, dd); - - smartlist_sort_strings(exitsummaries); - chosen_exitsummary = get_most_frequent_member(exitsummaries); - } else if (!chosen_exitsummary) { - char id[HEX_DIGEST_LEN+1]; - char dd[HEX_DIGEST_LEN+1]; - base16_encode(id, sizeof(dd), rs_out.identity_digest, DIGEST_LEN); - base16_encode(dd, sizeof(dd), rs_out.descriptor_digest, DIGEST_LEN); - log_warn(LD_DIR, "Not one of the voters that made us select" - "descriptor %s for router %s had an exit policy" - "summary", dd, id); - - /* Ok, none of those voting for the digest we chose had an - * exit policy for us. Well, that kinda sucks. - */ - smartlist_clear(exitsummaries); - SMARTLIST_FOREACH(matching_descs, vote_routerstatus_t *, vsr, { - if (vsr->status.has_exitsummary) - smartlist_add(exitsummaries, vsr->status.exitsummary); - }); - smartlist_sort_strings(exitsummaries); - chosen_exitsummary = get_most_frequent_member(exitsummaries); - - if (!chosen_exitsummary) - log_warn(LD_DIR, "Wow, not one of the voters had an exit " - "policy summary for %s. Wow.", id); - } - - if (chosen_exitsummary) { - rs_out.has_exitsummary = 1; - /* yea, discards the const */ - rs_out.exitsummary = (char *)chosen_exitsummary; - } - } - - 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; - /* Okay!! Now we can write the descriptor... */ - /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, rs_format, NULL); - if (buf) - smartlist_add(chunks, buf); - } - /* Now an m line, if applicable. */ - if (flavor == FLAV_MICRODESC && - !tor_digest256_is_zero(microdesc_digest)) { - char m[BASE64_DIGEST256_LEN+1]; - digest256_to_base64(m, microdesc_digest); - smartlist_add_asprintf(chunks, "m %s\n", m); - } - /* Next line is all flags. The "\n" is missing. */ - smartlist_add(chunks, - smartlist_join_strings(chosen_flags, " ", 0, NULL)); - /* Now the version line. */ - if (chosen_version) { - smartlist_add(chunks, tor_strdup("\nv ")); - smartlist_add(chunks, tor_strdup(chosen_version)); - } - smartlist_add(chunks, tor_strdup("\n")); - /* Now the weight line. */ - if (rs_out.has_bandwidth) { - int unmeasured = rs_out.bw_is_unmeasured && - consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW; - smartlist_add_asprintf(chunks, "w Bandwidth=%d%s\n", - rs_out.bandwidth_kb, - unmeasured?" Unmeasured=1":""); - } - - /* Now the exitpolicy summary line. */ - if (rs_out.has_exitsummary && flavor == FLAV_NS) { - smartlist_add_asprintf(chunks, "p %s\n", rs_out.exitsummary); - } - - /* And the loop is over and we move on to the next router */ - } - - tor_free(index); - tor_free(size); - tor_free(n_voter_flags); - tor_free(n_flag_voters); - for (i = 0; i < smartlist_len(votes); ++i) - tor_free(flag_map[i]); - tor_free(flag_map); - tor_free(flag_counts); - tor_free(named_flag); - tor_free(unnamed_flag); - strmap_free(name_to_id_map, NULL); - smartlist_free(matching_descs); - smartlist_free(chosen_flags); - smartlist_free(versions); - smartlist_free(exitsummaries); - tor_free(bandwidths_kb); - tor_free(measured_bws_kb); - } - - if (consensus_method >= MIN_METHOD_FOR_FOOTER) { - /* Starting with consensus method 9, we clearly mark the directory - * footer region */ - smartlist_add(chunks, tor_strdup("directory-footer\n")); - } - - if (consensus_method >= MIN_METHOD_FOR_BW_WEIGHTS) { - int64_t weight_scale = BW_WEIGHT_SCALE; - char *bw_weight_param = NULL; - - // Parse params, extract BW_WEIGHT_SCALE if present - // DO NOT use consensus_param_bw_weight_scale() in this code! - // The consensus is not formed yet! - /* XXXX Extract this code into a common function */ - if (params) { - if (strcmpstart(params, "bwweightscale=") == 0) - bw_weight_param = params; - else - bw_weight_param = strstr(params, " bwweightscale="); - } - - if (bw_weight_param) { - int ok=0; - char *eq = strchr(bw_weight_param, '='); - if (eq) { - weight_scale = tor_parse_long(eq+1, 10, 1, INT32_MAX, &ok, - NULL); - if (!ok) { - log_warn(LD_DIR, "Bad element '%s' in bw weight param", - escaped(bw_weight_param)); - weight_scale = BW_WEIGHT_SCALE; - } - } else { - log_warn(LD_DIR, "Bad element '%s' in bw weight param", - escaped(bw_weight_param)); - weight_scale = BW_WEIGHT_SCALE; - } - } - - if (consensus_method < 10) { - networkstatus_compute_bw_weights_v9(chunks, G, M, E, D, T, weight_scale); - added_weights = 1; - } else { - added_weights = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, - T, weight_scale); - } - } - - /* Add a signature. */ - { - char digest[DIGEST256_LEN]; - char fingerprint[HEX_DIGEST_LEN+1]; - char signing_key_fingerprint[HEX_DIGEST_LEN+1]; - digest_algorithm_t digest_alg = - flavor == FLAV_NS ? DIGEST_SHA1 : DIGEST_SHA256; - size_t digest_len = - flavor == FLAV_NS ? DIGEST_LEN : DIGEST256_LEN; - const char *algname = crypto_digest_algorithm_get_name(digest_alg); - char *signature; - - smartlist_add(chunks, tor_strdup("directory-signature ")); - - /* Compute the hash of the chunks. */ - crypto_digest_smartlist(digest, digest_len, chunks, "", digest_alg); - - /* Get the fingerprints */ - crypto_pk_get_fingerprint(identity_key, fingerprint, 0); - crypto_pk_get_fingerprint(signing_key, signing_key_fingerprint, 0); - - /* add the junk that will go at the end of the line. */ - if (flavor == FLAV_NS) { - smartlist_add_asprintf(chunks, "%s %s\n", fingerprint, - signing_key_fingerprint); - } else { - smartlist_add_asprintf(chunks, "%s %s %s\n", - algname, fingerprint, - signing_key_fingerprint); - } - /* And the signature. */ - if (!(signature = router_get_dirobj_signature(digest, digest_len, - signing_key))) { - log_warn(LD_BUG, "Couldn't sign consensus networkstatus."); - goto done; - } - smartlist_add(chunks, signature); - - if (legacy_id_key_digest && legacy_signing_key && consensus_method >= 3) { - smartlist_add(chunks, tor_strdup("directory-signature ")); - base16_encode(fingerprint, sizeof(fingerprint), - legacy_id_key_digest, DIGEST_LEN); - crypto_pk_get_fingerprint(legacy_signing_key, - signing_key_fingerprint, 0); - if (flavor == FLAV_NS) { - smartlist_add_asprintf(chunks, "%s %s\n", fingerprint, - signing_key_fingerprint); - } else { - smartlist_add_asprintf(chunks, "%s %s %s\n", - algname, fingerprint, - signing_key_fingerprint); - } - - if (!(signature = router_get_dirobj_signature(digest, digest_len, - legacy_signing_key))) { - log_warn(LD_BUG, "Couldn't sign consensus networkstatus."); - goto done; - } - smartlist_add(chunks, signature); - } - } - - result = smartlist_join_strings(chunks, "", 0, NULL); - - { - networkstatus_t *c; - if (!(c = networkstatus_parse_vote_from_string(result, NULL, - NS_TYPE_CONSENSUS))) { - log_err(LD_BUG, "Generated a networkstatus consensus we couldn't " - "parse."); - tor_free(result); - goto done; - } - // Verify balancing parameters - if (consensus_method >= MIN_METHOD_FOR_BW_WEIGHTS && added_weights) { - networkstatus_verify_bw_weights(c, consensus_method); - } - networkstatus_vote_free(c); - } - - done: - - tor_free(client_versions); - tor_free(server_versions); - SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp)); - smartlist_free(flags); - SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); - smartlist_free(chunks); - - return result; -} - -/** Given a consensus vote target and a set of detached signatures in - * sigs that correspond to the same consensus, check whether there are - * any new signatures in src_voter_list that should be added to - * target. (A signature should be added if we have no signature for that - * voter in target yet, or if we have no verifiable signature and the - * new signature is verifiable.) Return the number of signatures added or - * changed, or -1 if the document signed by sigs isn't the same - * document as target. */ -int -networkstatus_add_detached_signatures(networkstatus_t *target, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out) -{ - int r = 0; - const char *flavor; - smartlist_t *siglist; - tor_assert(sigs); - tor_assert(target); - tor_assert(target->type == NS_TYPE_CONSENSUS); - - flavor = networkstatus_get_flavor_name(target->flavor); - - /* Do the times seem right? */ - if (target->valid_after != sigs->valid_after) { - *msg_out = "Valid-After times do not match " - "when adding detached signatures to consensus"; - return -1; - } - if (target->fresh_until != sigs->fresh_until) { - *msg_out = "Fresh-until times do not match " - "when adding detached signatures to consensus"; - return -1; - } - if (target->valid_until != sigs->valid_until) { - *msg_out = "Valid-until times do not match " - "when adding detached signatures to consensus"; - return -1; - } - siglist = strmap_get(sigs->signatures, flavor); - if (!siglist) { - *msg_out = "No signatures for given consensus flavor"; - return -1; - } - - /** Make sure all the digests we know match, and at least one matches. */ - { - digests_t *digests = strmap_get(sigs->digests, flavor); - int n_matches = 0; - int alg; - if (!digests) { - *msg_out = "No digests for given consensus flavor"; - return -1; - } - for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) { - if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) { - if (fast_memeq(target->digests.d[alg], digests->d[alg], - DIGEST256_LEN)) { - ++n_matches; - } else { - *msg_out = "Mismatched digest."; - return -1; - } - } - } - if (!n_matches) { - *msg_out = "No regognized digests for given consensus flavor"; - } - } - - /* For each voter in src... */ - SMARTLIST_FOREACH_BEGIN(siglist, document_signature_t *, sig) { - char voter_identity[HEX_DIGEST_LEN+1]; - networkstatus_voter_info_t *target_voter = - networkstatus_get_voter_by_id(target, sig->identity_digest); - authority_cert_t *cert = NULL; - const char *algorithm; - document_signature_t *old_sig = NULL; - - algorithm = crypto_digest_algorithm_get_name(sig->alg); - - base16_encode(voter_identity, sizeof(voter_identity), - sig->identity_digest, DIGEST_LEN); - log_info(LD_DIR, "Looking at signature from %s using %s", voter_identity, - algorithm); - /* If the target doesn't know about this voter, then forget it. */ - if (!target_voter) { - log_info(LD_DIR, "We do not know any voter with ID %s", voter_identity); - continue; - } - - old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg); - - /* If the target already has a good signature from this voter, then skip - * this one. */ - if (old_sig && old_sig->good_signature) { - log_info(LD_DIR, "We already have a good signature from %s using %s", - voter_identity, algorithm); - continue; - } - - /* Try checking the signature if we haven't already. */ - if (!sig->good_signature && !sig->bad_signature) { - cert = authority_cert_get_by_digests(sig->identity_digest, - sig->signing_key_digest); - if (cert) - networkstatus_check_document_signature(target, sig, cert); - } - - /* If this signature is good, or we don't have any signature yet, - * then maybe add it. */ - if (sig->good_signature || !old_sig || old_sig->bad_signature) { - log_info(LD_DIR, "Adding signature from %s with %s", voter_identity, - algorithm); - tor_log(severity, LD_DIR, "Added a signature for %s from %s.", - target_voter->nickname, source); - ++r; - if (old_sig) { - smartlist_remove(target_voter->sigs, old_sig); - document_signature_free(old_sig); - } - smartlist_add(target_voter->sigs, document_signature_dup(sig)); - } else { - log_info(LD_DIR, "Not adding signature from %s", voter_identity); - } - } SMARTLIST_FOREACH_END(sig); - - return r; -} - -/** Return a newly allocated string containing all the signatures on - * consensus by all voters. If for_detached_signatures is true, - * then the signatures will be put in a detached signatures document, so - * prefix any non-NS-flavored signatures with "additional-signature" rather - * than "directory-signature". */ -static char * -networkstatus_format_signatures(networkstatus_t *consensus, - int for_detached_signatures) -{ - smartlist_t *elements; - char buf[4096]; - char *result = NULL; - int n_sigs = 0; - const consensus_flavor_t flavor = consensus->flavor; - const char *flavor_name = networkstatus_get_flavor_name(flavor); - const char *keyword; - - if (for_detached_signatures && flavor != FLAV_NS) - keyword = "additional-signature"; - else - keyword = "directory-signature"; - - elements = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(consensus->voters, networkstatus_voter_info_t *, v) { - SMARTLIST_FOREACH_BEGIN(v->sigs, document_signature_t *, sig) { - char sk[HEX_DIGEST_LEN+1]; - char id[HEX_DIGEST_LEN+1]; - if (!sig->signature || sig->bad_signature) - continue; - ++n_sigs; - base16_encode(sk, sizeof(sk), sig->signing_key_digest, DIGEST_LEN); - base16_encode(id, sizeof(id), sig->identity_digest, DIGEST_LEN); - if (flavor == FLAV_NS) { - smartlist_add_asprintf(elements, - "%s %s %s\n-----BEGIN SIGNATURE-----\n", - keyword, id, sk); - } else { - const char *digest_name = - crypto_digest_algorithm_get_name(sig->alg); - smartlist_add_asprintf(elements, - "%s%s%s %s %s %s\n-----BEGIN SIGNATURE-----\n", - keyword, - for_detached_signatures ? " " : "", - for_detached_signatures ? flavor_name : "", - digest_name, id, sk); - } - base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len); - strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf)); - smartlist_add(elements, tor_strdup(buf)); - } SMARTLIST_FOREACH_END(sig); - } SMARTLIST_FOREACH_END(v); - - result = smartlist_join_strings(elements, "", 0, NULL); - SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); - smartlist_free(elements); - if (!n_sigs) - tor_free(result); - return result; -} - -/** Return a newly allocated string holding the detached-signatures document - * corresponding to the signatures on consensuses, which must contain - * exactly one FLAV_NS consensus, and no more than one consensus for each - * other flavor. */ -char * -networkstatus_get_detached_signatures(smartlist_t *consensuses) -{ - smartlist_t *elements; - char *result = NULL, *sigs = NULL; - networkstatus_t *consensus_ns = NULL; - tor_assert(consensuses); - - SMARTLIST_FOREACH(consensuses, networkstatus_t *, ns, { - tor_assert(ns); - tor_assert(ns->type == NS_TYPE_CONSENSUS); - if (ns && ns->flavor == FLAV_NS) - consensus_ns = ns; - }); - if (!consensus_ns) { - log_warn(LD_BUG, "No NS consensus given."); - return NULL; - } - - elements = smartlist_new(); - - { - char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1], - vu_buf[ISO_TIME_LEN+1]; - char d[HEX_DIGEST_LEN+1]; - - base16_encode(d, sizeof(d), - consensus_ns->digests.d[DIGEST_SHA1], DIGEST_LEN); - format_iso_time(va_buf, consensus_ns->valid_after); - format_iso_time(fu_buf, consensus_ns->fresh_until); - format_iso_time(vu_buf, consensus_ns->valid_until); - - smartlist_add_asprintf(elements, - "consensus-digest %s\n" - "valid-after %s\n" - "fresh-until %s\n" - "valid-until %s\n", d, va_buf, fu_buf, vu_buf); - } - - /* Get all the digests for the non-FLAV_NS consensuses */ - SMARTLIST_FOREACH_BEGIN(consensuses, networkstatus_t *, ns) { - const char *flavor_name = networkstatus_get_flavor_name(ns->flavor); - int alg; - if (ns->flavor == FLAV_NS) - continue; - - /* start with SHA256; we don't include SHA1 for anything but the basic - * consensus. */ - for (alg = DIGEST_SHA256; alg < N_DIGEST_ALGORITHMS; ++alg) { - char d[HEX_DIGEST256_LEN+1]; - const char *alg_name = - crypto_digest_algorithm_get_name(alg); - if (tor_mem_is_zero(ns->digests.d[alg], DIGEST256_LEN)) - continue; - base16_encode(d, sizeof(d), ns->digests.d[alg], DIGEST256_LEN); - smartlist_add_asprintf(elements, "additional-digest %s %s %s\n", - flavor_name, alg_name, d); - } - } SMARTLIST_FOREACH_END(ns); - - /* Now get all the sigs for non-FLAV_NS consensuses */ - SMARTLIST_FOREACH_BEGIN(consensuses, networkstatus_t *, ns) { - char *sigs; - if (ns->flavor == FLAV_NS) - continue; - sigs = networkstatus_format_signatures(ns, 1); - if (!sigs) { - log_warn(LD_DIR, "Couldn't format signatures"); - goto err; - } - smartlist_add(elements, sigs); - } SMARTLIST_FOREACH_END(ns); - - /* Now add the FLAV_NS consensus signatrures. */ - sigs = networkstatus_format_signatures(consensus_ns, 1); - if (!sigs) - goto err; - smartlist_add(elements, sigs); - - result = smartlist_join_strings(elements, "", 0, NULL); - err: - SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); - smartlist_free(elements); - return result; -} - -/** Return a newly allocated string holding a detached-signatures document for - * all of the in-progress consensuses in the n_flavors-element array at - * pending. */ -static char * -get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, - int n_flavors) -{ - int flav; - char *signatures; - smartlist_t *c = smartlist_new(); - for (flav = 0; flav < n_flavors; ++flav) { - if (pending[flav].consensus) - smartlist_add(c, pending[flav].consensus); - } - signatures = networkstatus_get_detached_signatures(c); - smartlist_free(c); - return signatures; -} - -/** Release all storage held in s. */ -void -ns_detached_signatures_free(ns_detached_signatures_t *s) -{ - if (!s) - return; - if (s->signatures) { - STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) { - SMARTLIST_FOREACH(sigs, document_signature_t *, sig, - document_signature_free(sig)); - smartlist_free(sigs); - } STRMAP_FOREACH_END; - strmap_free(s->signatures, NULL); - strmap_free(s->digests, tor_free_); - } - - tor_free(s); -} - -/* ===== - * Certificate functions - * ===== */ - -/** Allocate and return a new authority_cert_t with the same contents as - * cert. */ -authority_cert_t * -authority_cert_dup(authority_cert_t *cert) -{ - authority_cert_t *out = tor_malloc(sizeof(authority_cert_t)); - tor_assert(cert); - - memcpy(out, cert, sizeof(authority_cert_t)); - /* Now copy pointed-to things. */ - out->cache_info.signed_descriptor_body = - tor_strndup(cert->cache_info.signed_descriptor_body, - cert->cache_info.signed_descriptor_len); - out->cache_info.saved_location = SAVED_NOWHERE; - out->identity_key = crypto_pk_dup_key(cert->identity_key); - out->signing_key = crypto_pk_dup_key(cert->signing_key); - - return out; -} - -/* ===== - * Vote scheduling - * ===== */ - -/** Set *timing_out to the intervals at which we would like to vote. - * Note that these aren't the intervals we'll use to vote; they're the ones - * that we'll vote to use. */ -void -dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) -{ - const or_options_t *options = get_options(); - - tor_assert(timing_out); - - timing_out->vote_interval = options->V3AuthVotingInterval; - timing_out->n_intervals_valid = options->V3AuthNIntervalsValid; - timing_out->vote_delay = options->V3AuthVoteDelay; - timing_out->dist_delay = options->V3AuthDistDelay; -} - -/** Return the start of the next interval of size interval (in - * seconds) after now, plus offset. Midnight always - * starts a fresh interval, and if the last interval of a day would be - * truncated to less than half its size, it is rolled into the - * previous interval. */ -time_t -dirvote_get_start_of_next_interval(time_t now, int interval, int offset) -{ - struct tm tm; - time_t midnight_today=0; - time_t midnight_tomorrow; - time_t next; - - tor_gmtime_r(&now, &tm); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - - 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; - - /* Intervals never cross midnight. */ - if (next > midnight_tomorrow) - next = midnight_tomorrow; - - /* If the interval would only last half as long as it's supposed to, then - * skip over to the next day. */ - if (next + interval/2 > midnight_tomorrow) - next = midnight_tomorrow; - - next += offset; - if (next - interval > now) - next -= interval; - - return next; -} - -/** Scheduling information for a voting interval. */ -static struct { - /** When do we generate and distribute our vote for this interval? */ - time_t voting_starts; - /** When do we send an HTTP request for any votes that we haven't - * been posted yet?*/ - time_t fetch_missing_votes; - /** When do we give up on getting more votes and generate a consensus? */ - time_t voting_ends; - /** When do we send an HTTP request for any signatures we're expecting to - * see on the consensus? */ - time_t fetch_missing_signatures; - /** When do we publish the consensus? */ - time_t interval_starts; - - /* True iff we have generated and distributed our vote. */ - int have_voted; - /* True iff we've requested missing votes. */ - int have_fetched_missing_votes; - /* True iff we have built a consensus and sent the signatures around. */ - int have_built_consensus; - /* True iff we've fetched missing signatures. */ - int have_fetched_missing_signatures; - /* True iff we have published our consensus. */ - int have_published_consensus; -} voting_schedule = {0,0,0,0,0,0,0,0,0,0}; - -/** Set voting_schedule to hold the timing for the next vote we should be - * doing. */ -void -dirvote_recalculate_timing(const or_options_t *options, time_t now) -{ - int interval, vote_delay, dist_delay; - time_t start; - time_t end; - networkstatus_t *consensus; - - if (!authdir_mode_v3(options)) - return; - - consensus = networkstatus_get_live_consensus(now); - - memset(&voting_schedule, 0, sizeof(voting_schedule)); - - if (consensus) { - interval = (int)( consensus->fresh_until - consensus->valid_after ); - vote_delay = consensus->vote_seconds; - dist_delay = consensus->dist_seconds; - } else { - interval = options->TestingV3AuthInitialVotingInterval; - vote_delay = options->TestingV3AuthInitialVoteDelay; - dist_delay = options->TestingV3AuthInitialDistDelay; - } - - tor_assert(interval > 0); - - if (vote_delay + dist_delay > interval/2) - vote_delay = dist_delay = interval / 4; - - start = voting_schedule.interval_starts = - dirvote_get_start_of_next_interval(now,interval, - options->TestingV3AuthVotingStartOffset); - end = dirvote_get_start_of_next_interval(start+1, interval, - options->TestingV3AuthVotingStartOffset); - - tor_assert(end > start); - - voting_schedule.fetch_missing_signatures = start - (dist_delay/2); - voting_schedule.voting_ends = start - dist_delay; - voting_schedule.fetch_missing_votes = start - dist_delay - (vote_delay/2); - voting_schedule.voting_starts = start - dist_delay - vote_delay; - - { - char tbuf[ISO_TIME_LEN+1]; - format_iso_time(tbuf, voting_schedule.interval_starts); - log_notice(LD_DIR,"Choosing expected valid-after time as %s: " - "consensus_set=%d, interval=%d", - tbuf, consensus?1:0, interval); - } -} - -/** Entry point: Take whatever voting actions are pending as of now. */ -void -dirvote_act(const or_options_t *options, time_t now) -{ - if (!authdir_mode_v3(options)) - return; - if (!voting_schedule.voting_starts) { - char *keys = list_v3_auth_ids(); - authority_cert_t *c = get_my_v3_authority_cert(); - log_notice(LD_DIR, "Scheduling voting. Known authority IDs are %s. " - "Mine is %s.", - keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN)); - tor_free(keys); - dirvote_recalculate_timing(options, now); - } - if (voting_schedule.voting_starts < now && !voting_schedule.have_voted) { - log_notice(LD_DIR, "Time to vote."); - dirvote_perform_vote(); - voting_schedule.have_voted = 1; - } - if (voting_schedule.fetch_missing_votes < now && - !voting_schedule.have_fetched_missing_votes) { - log_notice(LD_DIR, "Time to fetch any votes that we're missing."); - dirvote_fetch_missing_votes(); - voting_schedule.have_fetched_missing_votes = 1; - } - if (voting_schedule.voting_ends < now && - !voting_schedule.have_built_consensus) { - log_notice(LD_DIR, "Time to compute a consensus."); - dirvote_compute_consensuses(); - /* XXXX We will want to try again later if we haven't got enough - * votes yet. Implement this if it turns out to ever happen. */ - voting_schedule.have_built_consensus = 1; - } - if (voting_schedule.fetch_missing_signatures < now && - !voting_schedule.have_fetched_missing_signatures) { - log_notice(LD_DIR, "Time to fetch any signatures that we're missing."); - dirvote_fetch_missing_signatures(); - voting_schedule.have_fetched_missing_signatures = 1; - } - if (voting_schedule.interval_starts < now && - !voting_schedule.have_published_consensus) { - log_notice(LD_DIR, "Time to publish the consensus and discard old votes"); - dirvote_publish_consensus(); - dirvote_clear_votes(0); - voting_schedule.have_published_consensus = 1; - /* XXXX We will want to try again later if we haven't got enough - * signatures yet. Implement this if it turns out to ever happen. */ - dirvote_recalculate_timing(options, now); - } -} - -/** A vote networkstatus_t and its unparsed body: held around so we can - * use it to generate a consensus (at voting_ends) and so we can serve it to - * other authorities that might want it. */ -typedef struct pending_vote_t { - cached_dir_t *vote_body; - networkstatus_t *vote; -} pending_vote_t; - -/** List of pending_vote_t for the current vote. Before we've used them to - * build a consensus, the votes go here. */ -static smartlist_t *pending_vote_list = NULL; -/** List of pending_vote_t for the previous vote. After we've used them to - * build a consensus, the votes go here for the next period. */ -static smartlist_t *previous_vote_list = NULL; - -/* DOCDOC pending_consensuses */ -static pending_consensus_t pending_consensuses[N_CONSENSUS_FLAVORS]; - -/** The detached signatures for the consensus that we're currently - * building. */ -static char *pending_consensus_signatures = NULL; - -/** List of ns_detached_signatures_t: hold signatures that get posted to us - * before we have generated the consensus on our own. */ -static smartlist_t *pending_consensus_signature_list = NULL; - -/** Generate a networkstatus vote and post it to all the v3 authorities. - * (V3 Authority only) */ -static int -dirvote_perform_vote(void) -{ - crypto_pk_t *key = get_my_v3_authority_signing_key(); - authority_cert_t *cert = get_my_v3_authority_cert(); - networkstatus_t *ns; - char *contents; - pending_vote_t *pending_vote; - time_t now = time(NULL); - - int status; - const char *msg = ""; - - if (!cert || !key) { - log_warn(LD_NET, "Didn't find key/certificate to generate v3 vote"); - return -1; - } else if (cert->expires < now) { - log_warn(LD_NET, "Can't generate v3 vote with expired certificate"); - return -1; - } - if (!(ns = dirserv_generate_networkstatus_vote_obj(key, cert))) - return -1; - - contents = format_networkstatus_vote(key, ns); - networkstatus_vote_free(ns); - if (!contents) - return -1; - - pending_vote = dirvote_add_vote(contents, &msg, &status); - tor_free(contents); - if (!pending_vote) { - log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)", - msg); - return -1; - } - - directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_VOTE, - ROUTER_PURPOSE_GENERAL, - V3_DIRINFO, - pending_vote->vote_body->dir, - pending_vote->vote_body->dir_len, 0); - log_notice(LD_DIR, "Vote posted."); - return 0; -} - -/** Send an HTTP request to every other v3 authority, for the votes of every - * authority for which we haven't received a vote yet in this period. (V3 - * authority only) */ -static void -dirvote_fetch_missing_votes(void) -{ - smartlist_t *missing_fps = smartlist_new(); - char *resource; - - SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(), - dir_server_t *, ds) { - if (!(ds->type & V3_DIRINFO)) - continue; - if (!dirvote_get_vote(ds->v3_identity_digest, - DGV_BY_ID|DGV_INCLUDE_PENDING)) { - char *cp = tor_malloc(HEX_DIGEST_LEN+1); - base16_encode(cp, HEX_DIGEST_LEN+1, ds->v3_identity_digest, - DIGEST_LEN); - smartlist_add(missing_fps, cp); - } - } SMARTLIST_FOREACH_END(ds); - - if (!smartlist_len(missing_fps)) { - smartlist_free(missing_fps); - return; - } - { - char *tmp = smartlist_join_strings(missing_fps, " ", 0, NULL); - log_notice(LOG_NOTICE, "We're missing votes from %d authorities (%s). " - "Asking every other authority for a copy.", - smartlist_len(missing_fps), tmp); - tor_free(tmp); - } - resource = smartlist_join_strings(missing_fps, "+", 0, NULL); - directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, - 0, resource); - tor_free(resource); - SMARTLIST_FOREACH(missing_fps, char *, cp, tor_free(cp)); - smartlist_free(missing_fps); -} - -/** Send a request to every other authority for its detached signatures, - * unless we have signatures from all other v3 authorities already. */ -static void -dirvote_fetch_missing_signatures(void) -{ - int need_any = 0; - int i; - for (i=0; i < N_CONSENSUS_FLAVORS; ++i) { - networkstatus_t *consensus = pending_consensuses[i].consensus; - if (!consensus || - networkstatus_check_consensus_signature(consensus, -1) == 1) { - /* We have no consensus, or we have one that's signed by everybody. */ - continue; - } - need_any = 1; - } - if (!need_any) - return; - - directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, - 0, NULL); -} - -/** Release all storage held by pending consensuses (those waiting for - * signatures). */ -static void -dirvote_clear_pending_consensuses(void) -{ - int i; - for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { - pending_consensus_t *pc = &pending_consensuses[i]; - tor_free(pc->body); - - networkstatus_vote_free(pc->consensus); - pc->consensus = NULL; - } -} - -/** Drop all currently pending votes, consensus, and detached signatures. */ -static void -dirvote_clear_votes(int all_votes) -{ - if (!previous_vote_list) - previous_vote_list = smartlist_new(); - if (!pending_vote_list) - pending_vote_list = smartlist_new(); - - /* All "previous" votes are now junk. */ - SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, v, { - cached_dir_decref(v->vote_body); - v->vote_body = NULL; - networkstatus_vote_free(v->vote); - tor_free(v); - }); - smartlist_clear(previous_vote_list); - - if (all_votes) { - /* If we're dumping all the votes, we delete the pending ones. */ - SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, { - cached_dir_decref(v->vote_body); - v->vote_body = NULL; - networkstatus_vote_free(v->vote); - tor_free(v); - }); - } else { - /* Otherwise, we move them into "previous". */ - smartlist_add_all(previous_vote_list, pending_vote_list); - } - smartlist_clear(pending_vote_list); - - if (pending_consensus_signature_list) { - SMARTLIST_FOREACH(pending_consensus_signature_list, char *, cp, - tor_free(cp)); - smartlist_clear(pending_consensus_signature_list); - } - tor_free(pending_consensus_signatures); - dirvote_clear_pending_consensuses(); -} - -/** Return a newly allocated string containing the hex-encoded v3 authority - identity digest of every recognized v3 authority. */ -static char * -list_v3_auth_ids(void) -{ - smartlist_t *known_v3_keys = smartlist_new(); - char *keys; - SMARTLIST_FOREACH(router_get_trusted_dir_servers(), - dir_server_t *, ds, - if ((ds->type & V3_DIRINFO) && - !tor_digest_is_zero(ds->v3_identity_digest)) - smartlist_add(known_v3_keys, - tor_strdup(hex_str(ds->v3_identity_digest, DIGEST_LEN)))); - keys = smartlist_join_strings(known_v3_keys, ", ", 0, NULL); - SMARTLIST_FOREACH(known_v3_keys, char *, cp, tor_free(cp)); - smartlist_free(known_v3_keys); - return keys; -} - -/** Called when we have received a networkstatus vote in vote_body. - * Parse and validate it, and on success store it as a pending vote (which we - * then return). Return NULL on failure. Sets *msg_out and - * *status_out to an HTTP response and status code. (V3 authority - * only) */ -pending_vote_t * -dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) -{ - networkstatus_t *vote; - networkstatus_voter_info_t *vi; - dir_server_t *ds; - pending_vote_t *pending_vote = NULL; - const char *end_of_vote = NULL; - int any_failed = 0; - tor_assert(vote_body); - tor_assert(msg_out); - tor_assert(status_out); - - if (!pending_vote_list) - pending_vote_list = smartlist_new(); - *status_out = 0; - *msg_out = NULL; - - again: - vote = networkstatus_parse_vote_from_string(vote_body, &end_of_vote, - NS_TYPE_VOTE); - if (!end_of_vote) - end_of_vote = vote_body + strlen(vote_body); - if (!vote) { - log_warn(LD_DIR, "Couldn't parse vote: length was %d", - (int)strlen(vote_body)); - *msg_out = "Unable to parse vote"; - goto err; - } - tor_assert(smartlist_len(vote->voters) == 1); - vi = get_voter(vote); - { - int any_sig_good = 0; - SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig, - if (sig->good_signature) - any_sig_good = 1); - tor_assert(any_sig_good); - } - ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest); - if (!ds) { - char *keys = list_v3_auth_ids(); - log_warn(LD_DIR, "Got a vote from an authority (nickname %s, address %s) " - "with authority key ID %s. " - "This key ID is not recognized. Known v3 key IDs are: %s", - vi->nickname, vi->address, - hex_str(vi->identity_digest, DIGEST_LEN), keys); - tor_free(keys); - *msg_out = "Vote not from a recognized v3 authority"; - goto err; - } - tor_assert(vote->cert); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - /* Hey, it's a new cert! */ - trusted_dirs_load_certs_from_string( - vote->cert->cache_info.signed_descriptor_body, - TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - log_warn(LD_BUG, "We added a cert, but still couldn't find it."); - } - } - - /* Is it for the right period? */ - if (vote->valid_after != voting_schedule.interval_starts) { - char tbuf1[ISO_TIME_LEN+1], tbuf2[ISO_TIME_LEN+1]; - format_iso_time(tbuf1, vote->valid_after); - format_iso_time(tbuf2, voting_schedule.interval_starts); - log_warn(LD_DIR, "Rejecting vote from %s with valid-after time of %s; " - "we were expecting %s", vi->address, tbuf1, tbuf2); - *msg_out = "Bad valid-after time"; - goto err; - } - - /* Fetch any new router descriptors we just learned about */ - update_consensus_router_descriptor_downloads(time(NULL), 1, vote); - - /* Now see whether we already have a vote from this authority. */ - SMARTLIST_FOREACH_BEGIN(pending_vote_list, pending_vote_t *, v) { - if (fast_memeq(v->vote->cert->cache_info.identity_digest, - vote->cert->cache_info.identity_digest, - DIGEST_LEN)) { - networkstatus_voter_info_t *vi_old = get_voter(v->vote); - if (fast_memeq(vi_old->vote_digest, vi->vote_digest, DIGEST_LEN)) { - /* Ah, it's the same vote. Not a problem. */ - log_info(LD_DIR, "Discarding a vote we already have (from %s).", - vi->address); - if (*status_out < 200) - *status_out = 200; - goto discard; - } else if (v->vote->published < vote->published) { - log_notice(LD_DIR, "Replacing an older pending vote from this " - "directory."); - cached_dir_decref(v->vote_body); - networkstatus_vote_free(v->vote); - v->vote_body = new_cached_dir(tor_strndup(vote_body, - end_of_vote-vote_body), - vote->published); - v->vote = vote; - if (end_of_vote && - !strcmpstart(end_of_vote, "network-status-version")) - goto again; - - if (*status_out < 200) - *status_out = 200; - if (!*msg_out) - *msg_out = "OK"; - return v; - } else { - *msg_out = "Already have a newer pending vote"; - goto err; - } - } - } SMARTLIST_FOREACH_END(v); - - pending_vote = tor_malloc_zero(sizeof(pending_vote_t)); - pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body, - end_of_vote-vote_body), - vote->published); - pending_vote->vote = vote; - smartlist_add(pending_vote_list, pending_vote); - - if (!strcmpstart(end_of_vote, "network-status-version ")) { - vote_body = end_of_vote; - goto again; - } - - goto done; - - err: - any_failed = 1; - if (!*msg_out) - *msg_out = "Error adding vote"; - if (*status_out < 400) - *status_out = 400; - - discard: - networkstatus_vote_free(vote); - - if (end_of_vote && !strcmpstart(end_of_vote, "network-status-version ")) { - vote_body = end_of_vote; - goto again; - } - - done: - - if (*status_out < 200) - *status_out = 200; - if (!*msg_out) { - if (!any_failed && !pending_vote) { - *msg_out = "Duplicate discarded"; - } else { - *msg_out = "ok"; - } - } - - return any_failed ? NULL : pending_vote; -} - -/** Try to compute a v3 networkstatus consensus from the currently pending - * votes. Return 0 on success, -1 on failure. Store the consensus in - * pending_consensus: it won't be ready to be published until we have - * everybody else's signatures collected too. (V3 Authority only) */ -static int -dirvote_compute_consensuses(void) -{ - /* Have we got enough votes to try? */ - int n_votes, n_voters, n_vote_running = 0; - smartlist_t *votes = NULL, *votestrings = NULL; - char *consensus_body = NULL, *signatures = NULL, *votefile; - networkstatus_t *consensus = NULL; - authority_cert_t *my_cert; - pending_consensus_t pending[N_CONSENSUS_FLAVORS]; - int flav; - - memset(pending, 0, sizeof(pending)); - - if (!pending_vote_list) - pending_vote_list = smartlist_new(); - - n_voters = get_n_authorities(V3_DIRINFO); - n_votes = smartlist_len(pending_vote_list); - if (n_votes <= n_voters/2) { - log_warn(LD_DIR, "We don't have enough votes to generate a consensus: " - "%d of %d", n_votes, n_voters/2+1); - goto err; - } - tor_assert(pending_vote_list); - SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, { - if (smartlist_contains_string(v->vote->known_flags, "Running")) - n_vote_running++; - }); - if (!n_vote_running) { - /* See task 1066. */ - log_warn(LD_DIR, "Nobody has voted on the Running flag. Generating " - "and publishing a consensus without Running nodes " - "would make many clients stop working. Not " - "generating a consensus!"); - goto err; - } - - if (!(my_cert = get_my_v3_authority_cert())) { - log_warn(LD_DIR, "Can't generate consensus without a certificate."); - goto err; - } - - votes = smartlist_new(); - votestrings = smartlist_new(); - SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, - { - sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t)); - c->bytes = v->vote_body->dir; - c->len = v->vote_body->dir_len; - smartlist_add(votestrings, c); /* collect strings to write to disk */ - - smartlist_add(votes, v->vote); /* collect votes to compute consensus */ - }); - - votefile = get_datadir_fname("v3-status-votes"); - write_chunks_to_file(votefile, votestrings, 0, 0); - tor_free(votefile); - SMARTLIST_FOREACH(votestrings, sized_chunk_t *, c, tor_free(c)); - smartlist_free(votestrings); - - { - char legacy_dbuf[DIGEST_LEN]; - crypto_pk_t *legacy_sign=NULL; - char *legacy_id_digest = NULL; - int n_generated = 0; - if (get_options()->V3AuthUseLegacyKey) { - authority_cert_t *cert = get_my_v3_legacy_cert(); - legacy_sign = get_my_v3_legacy_signing_key(); - if (cert) { - if (crypto_pk_get_digest(cert->identity_key, legacy_dbuf)) { - log_warn(LD_BUG, - "Unable to compute digest of legacy v3 identity key"); - } else { - legacy_id_digest = legacy_dbuf; - } - } - } - - for (flav = 0; flav < N_CONSENSUS_FLAVORS; ++flav) { - const char *flavor_name = networkstatus_get_flavor_name(flav); - consensus_body = networkstatus_compute_consensus( - votes, n_voters, - my_cert->identity_key, - get_my_v3_authority_signing_key(), legacy_id_digest, legacy_sign, - flav); - - if (!consensus_body) { - log_warn(LD_DIR, "Couldn't generate a %s consensus at all!", - flavor_name); - continue; - } - consensus = networkstatus_parse_vote_from_string(consensus_body, NULL, - NS_TYPE_CONSENSUS); - if (!consensus) { - log_warn(LD_DIR, "Couldn't parse %s consensus we generated!", - flavor_name); - tor_free(consensus_body); - continue; - } - - /* 'Check' our own signature, to mark it valid. */ - networkstatus_check_consensus_signature(consensus, -1); - - pending[flav].body = consensus_body; - pending[flav].consensus = consensus; - n_generated++; - consensus_body = NULL; - consensus = NULL; - } - if (!n_generated) { - log_warn(LD_DIR, "Couldn't generate any consensus flavors at all."); - goto err; - } - } - - signatures = get_detached_signatures_from_pending_consensuses( - pending, N_CONSENSUS_FLAVORS); - - if (!signatures) { - log_warn(LD_DIR, "Couldn't extract signatures."); - goto err; - } - - dirvote_clear_pending_consensuses(); - memcpy(pending_consensuses, pending, sizeof(pending)); - - tor_free(pending_consensus_signatures); - pending_consensus_signatures = signatures; - - if (pending_consensus_signature_list) { - int n_sigs = 0; - /* we may have gotten signatures for this consensus before we built - * it ourself. Add them now. */ - SMARTLIST_FOREACH_BEGIN(pending_consensus_signature_list, char *, sig) { - const char *msg = NULL; - int r = dirvote_add_signatures_to_all_pending_consensuses(sig, - "pending", &msg); - if (r >= 0) - n_sigs += r; - else - log_warn(LD_DIR, - "Could not add queued signature to new consensus: %s", - msg); - tor_free(sig); - } SMARTLIST_FOREACH_END(sig); - if (n_sigs) - log_notice(LD_DIR, "Added %d pending signatures while building " - "consensus.", n_sigs); - smartlist_clear(pending_consensus_signature_list); - } - - log_notice(LD_DIR, "Consensus computed; uploading signature(s)"); - - directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_SIGNATURES, - ROUTER_PURPOSE_GENERAL, - V3_DIRINFO, - pending_consensus_signatures, - strlen(pending_consensus_signatures), 0); - log_notice(LD_DIR, "Signature(s) posted."); - - smartlist_free(votes); - return 0; - err: - smartlist_free(votes); - tor_free(consensus_body); - tor_free(signatures); - networkstatus_vote_free(consensus); - - return -1; -} - -/** Helper: we just got the detached_signatures_body sent to us as - * signatures on the currently pending consensus. Add them to pc - * as appropriate. Return the number of signatures added. (?) */ -static int -dirvote_add_signatures_to_pending_consensus( - pending_consensus_t *pc, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out) -{ - const char *flavor_name; - int r = -1; - - /* Only call if we have a pending consensus right now. */ - tor_assert(pc->consensus); - tor_assert(pc->body); - tor_assert(pending_consensus_signatures); - - flavor_name = networkstatus_get_flavor_name(pc->consensus->flavor); - *msg_out = NULL; - - { - smartlist_t *sig_list = strmap_get(sigs->signatures, flavor_name); - log_info(LD_DIR, "Have %d signatures for adding to %s consensus.", - sig_list ? smartlist_len(sig_list) : 0, flavor_name); - } - r = networkstatus_add_detached_signatures(pc->consensus, sigs, - source, severity, msg_out); - log_info(LD_DIR,"Added %d signatures to consensus.", r); - - if (r >= 1) { - char *new_signatures = - networkstatus_format_signatures(pc->consensus, 0); - char *dst, *dst_end; - size_t new_consensus_len; - if (!new_signatures) { - *msg_out = "No signatures to add"; - goto err; - } - new_consensus_len = - strlen(pc->body) + strlen(new_signatures) + 1; - pc->body = tor_realloc(pc->body, new_consensus_len); - dst_end = pc->body + new_consensus_len; - dst = strstr(pc->body, "directory-signature "); - tor_assert(dst); - strlcpy(dst, new_signatures, dst_end-dst); - - /* We remove this block once it has failed to crash for a while. But - * unless it shows up in profiles, we're probably better leaving it in, - * just in case we break detached signature processing at some point. */ - { - networkstatus_t *v = networkstatus_parse_vote_from_string( - pc->body, NULL, - NS_TYPE_CONSENSUS); - tor_assert(v); - networkstatus_vote_free(v); - } - *msg_out = "Signatures added"; - tor_free(new_signatures); - } else if (r == 0) { - *msg_out = "Signatures ignored"; - } else { - goto err; - } - - goto done; - err: - if (!*msg_out) - *msg_out = "Unrecognized error while adding detached signatures."; - done: - return r; -} - -static int -dirvote_add_signatures_to_all_pending_consensuses( - const char *detached_signatures_body, - const char *source, - const char **msg_out) -{ - int r=0, i, n_added = 0, errors = 0; - ns_detached_signatures_t *sigs; - tor_assert(detached_signatures_body); - tor_assert(msg_out); - tor_assert(pending_consensus_signatures); - - if (!(sigs = networkstatus_parse_detached_signatures( - detached_signatures_body, NULL))) { - *msg_out = "Couldn't parse detached signatures."; - goto err; - } - - for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { - int res; - int severity = i == FLAV_NS ? LOG_NOTICE : LOG_INFO; - pending_consensus_t *pc = &pending_consensuses[i]; - if (!pc->consensus) - continue; - res = dirvote_add_signatures_to_pending_consensus(pc, sigs, source, - severity, msg_out); - if (res < 0) - errors++; - else - n_added += res; - } - - if (errors && !n_added) { - r = -1; - goto err; - } - - if (n_added && pending_consensuses[FLAV_NS].consensus) { - char *new_detached = - get_detached_signatures_from_pending_consensuses( - pending_consensuses, N_CONSENSUS_FLAVORS); - if (new_detached) { - tor_free(pending_consensus_signatures); - pending_consensus_signatures = new_detached; - } - } - - r = n_added; - goto done; - err: - if (!*msg_out) - *msg_out = "Unrecognized error while adding detached signatures."; - done: - ns_detached_signatures_free(sigs); - /* XXXX NM Check how return is used. We can now have an error *and* - signatures added. */ - return r; -} - -/** Helper: we just got the detached_signatures_body sent to us as - * signatures on the currently pending consensus. Add them to the pending - * consensus (if we have one); otherwise queue them until we have a - * consensus. Return negative on failure, nonnegative on success. */ -int -dirvote_add_signatures(const char *detached_signatures_body, - const char *source, - const char **msg) -{ - if (pending_consensuses[FLAV_NS].consensus) { - log_notice(LD_DIR, "Got a signature from %s. " - "Adding it to the pending consensus.", source); - return dirvote_add_signatures_to_all_pending_consensuses( - detached_signatures_body, source, msg); - } else { - log_notice(LD_DIR, "Got a signature from %s. " - "Queuing it for the next consensus.", source); - if (!pending_consensus_signature_list) - pending_consensus_signature_list = smartlist_new(); - smartlist_add(pending_consensus_signature_list, - tor_strdup(detached_signatures_body)); - *msg = "Signature queued"; - return 0; - } -} - -/** Replace the consensus that we're currently serving with the one that we've - * been building. (V3 Authority only) */ -static int -dirvote_publish_consensus(void) -{ - int i; - - /* Now remember all the other consensuses as if we were a directory cache. */ - for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { - pending_consensus_t *pending = &pending_consensuses[i]; - const char *name; - name = networkstatus_get_flavor_name(i); - tor_assert(name); - if (!pending->consensus || - networkstatus_check_consensus_signature(pending->consensus, 1)<0) { - log_warn(LD_DIR, "Not enough info to publish pending %s consensus",name); - continue; - } - - if (networkstatus_set_current_consensus(pending->body, name, 0)) - log_warn(LD_DIR, "Error publishing %s consensus", name); - else - log_notice(LD_DIR, "Published %s consensus", name); - } - - return 0; -} - -/** Release all static storage held in dirvote.c */ -void -dirvote_free_all(void) -{ - dirvote_clear_votes(1); - /* now empty as a result of dirvote_clear_votes(). */ - smartlist_free(pending_vote_list); - pending_vote_list = NULL; - smartlist_free(previous_vote_list); - previous_vote_list = NULL; - - dirvote_clear_pending_consensuses(); - tor_free(pending_consensus_signatures); - if (pending_consensus_signature_list) { - /* now empty as a result of dirvote_clear_votes(). */ - smartlist_free(pending_consensus_signature_list); - pending_consensus_signature_list = NULL; - } -} - -/* ==== - * Access to pending items. - * ==== */ - -/** Return the body of the consensus that we're currently trying to build. */ -const char * -dirvote_get_pending_consensus(consensus_flavor_t flav) -{ - tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS); - return pending_consensuses[flav].body; -} - -/** Return the signatures that we know for the consensus that we're currently - * trying to build. */ -const char * -dirvote_get_pending_detached_signatures(void) -{ - return pending_consensus_signatures; -} - -/** Return a given vote specified by fp. If by_id, return the - * vote for the authority with the v3 authority identity key digest fp; - * if by_id is false, return the vote whose digest is fp. If - * fp is NULL, return our own vote. If include_previous is - * false, do not consider any votes for a consensus that's already been built. - * If include_pending is false, do not consider any votes for the - * consensus that's in progress. May return NULL if we have no vote for the - * authority in question. */ -const cached_dir_t * -dirvote_get_vote(const char *fp, int flags) -{ - int by_id = flags & DGV_BY_ID; - const int include_pending = flags & DGV_INCLUDE_PENDING; - const int include_previous = flags & DGV_INCLUDE_PREVIOUS; - - if (!pending_vote_list && !previous_vote_list) - return NULL; - if (fp == NULL) { - authority_cert_t *c = get_my_v3_authority_cert(); - if (c) { - fp = c->cache_info.identity_digest; - by_id = 1; - } else - return NULL; - } - if (by_id) { - if (pending_vote_list && include_pending) { - SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, pv, - if (fast_memeq(get_voter(pv->vote)->identity_digest, fp, DIGEST_LEN)) - return pv->vote_body); - } - if (previous_vote_list && include_previous) { - SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, pv, - if (fast_memeq(get_voter(pv->vote)->identity_digest, fp, DIGEST_LEN)) - return pv->vote_body); - } - } else { - if (pending_vote_list && include_pending) { - SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, pv, - if (fast_memeq(pv->vote->digests.d[DIGEST_SHA1], fp, DIGEST_LEN)) - return pv->vote_body); - } - if (previous_vote_list && include_previous) { - SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, pv, - if (fast_memeq(pv->vote->digests.d[DIGEST_SHA1], fp, DIGEST_LEN)) - return pv->vote_body); - } - } - return NULL; -} - -/** Construct and return a new microdescriptor from a routerinfo ri - * according to consensus_method. - **/ -microdesc_t * -dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) -{ - microdesc_t *result = NULL; - char *key = NULL, *summary = NULL, *family = NULL; - size_t keylen; - smartlist_t *chunks = smartlist_new(); - char *output = NULL; - - if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0) - goto done; - summary = policy_summarize(ri->exit_policy, AF_INET); - if (ri->declared_family) - family = smartlist_join_strings(ri->declared_family, " ", 0, NULL); - - smartlist_add_asprintf(chunks, "onion-key\n%s", key); - - if (consensus_method >= MIN_METHOD_FOR_NTOR_KEY && - ri->onion_curve25519_pkey) { - char kbuf[128]; - base64_encode(kbuf, sizeof(kbuf), - (const char*)ri->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN); - smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); - } - - if (consensus_method >= MIN_METHOD_FOR_A_LINES && - !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) - smartlist_add_asprintf(chunks, "a %s\n", - fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); - - 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); - - if (consensus_method >= MIN_METHOD_FOR_P6_LINES && - ri->ipv6_exit_policy) { - /* XXXX024 This doesn't match proposal 208, which says these should - * be taken unchanged from the routerinfo. That's bogosity, IMO: - * the proposal should have said to do this instead.*/ - char *p6 = write_short_policy(ri->ipv6_exit_policy); - if (p6 && strcmp(p6, "reject 1-65535")) - smartlist_add_asprintf(chunks, "p6 %s\n", p6); - tor_free(p6); - } - - output = smartlist_join_strings(chunks, "", 0, NULL); - - { - smartlist_t *lst = microdescs_parse_from_string(output, - output+strlen(output), 0, - SAVED_NOWHERE); - 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)); - smartlist_free(lst); - goto done; - } - result = smartlist_get(lst, 0); - smartlist_free(lst); - } - - 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; -} - -/** Format the appropriate vote line to describe the microdescriptor md - * in a consensus vote document. Write it into the out_len-byte buffer - * in out. Return -1 on failure and the number of characters written - * on success. */ -ssize_t -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]; - 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) - goto out; - - if (tor_snprintf(out_buf, out_buf_len, "m %s sha256=%s\n", - microdesc_consensus_methods, d64)<0) - goto out; - - ret = strlen(out_buf); - - out: - tor_free(microdesc_consensus_methods); - return ret; -} - -/** 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, MIN_METHOD_FOR_P6_LINES - 1}, - {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, - {MIN_METHOD_FOR_NTOR_KEY, MAX_SUPPORTED_CONSENSUS_METHOD}, - {-1, -1} -}; - -/** Helper type used when generating the microdescriptor lines in a directory - * vote. */ -typedef struct microdesc_vote_line_t { - int low; - int high; - microdesc_t *md; - struct microdesc_vote_line_t *next; -} microdesc_vote_line_t; - -/** Generate and return a linked list of all the lines that should appear to - * describe a router's microdescriptor versions in a directory vote. - * Add the generated microdescriptors to microdescriptors_out. */ -vote_microdesc_hash_t * -dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now, - smartlist_t *microdescriptors_out) -{ - const struct consensus_method_range_t *cmr; - microdesc_vote_line_t *entries = NULL, *ep; - vote_microdesc_hash_t *result = NULL; - - /* Generate the microdescriptors. */ - for (cmr = microdesc_consensus_methods; - cmr->low != -1 && cmr->high != -1; - cmr++) { - microdesc_t *md = dirvote_create_microdescriptor(ri, cmr->low); - if (md) { - microdesc_vote_line_t *e = - tor_malloc_zero(sizeof(microdesc_vote_line_t)); - e->md = md; - e->low = cmr->low; - e->high = cmr->high; - e->next = entries; - entries = e; - } - } - - /* Compress adjacent identical ones */ - for (ep = entries; ep; ep = ep->next) { - while (ep->next && - fast_memeq(ep->md->digest, ep->next->md->digest, DIGEST256_LEN) && - ep->low == ep->next->high + 1) { - microdesc_vote_line_t *next = ep->next; - ep->low = next->low; - microdesc_free(next->md); - ep->next = next->next; - tor_free(next); - } - } - - /* Format them into vote_microdesc_hash_t, and add to microdescriptors_out.*/ - while ((ep = entries)) { - char buf[128]; - vote_microdesc_hash_t *h; - dirvote_format_microdesc_vote_line(buf, sizeof(buf), ep->md, - ep->low, ep->high); - h = tor_malloc_zero(sizeof(vote_microdesc_hash_t)); - h->microdesc_hash_line = tor_strdup(buf); - h->next = result; - result = h; - ep->md->last_listed = now; - smartlist_add(microdescriptors_out, ep->md); - entries = ep->next; - tor_free(ep); - } - - return result; -} - -/** If vrs has a hash made for the consensus method method with - * the digest algorithm alg, decode it and copy it into - * digest256_out and return 0. Otherwise return -1. */ -int -vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg) -{ - /* XXXX only returns the sha256 method. */ - const vote_microdesc_hash_t *h; - char mstr[64]; - size_t mlen; - char dstr[64]; - - tor_snprintf(mstr, sizeof(mstr), "%d", method); - mlen = strlen(mstr); - tor_snprintf(dstr, sizeof(dstr), " %s=", - crypto_digest_algorithm_get_name(alg)); - - for (h = vrs->microdesc; h; h = h->next) { - const char *cp = h->microdesc_hash_line; - size_t num_len; - /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in - * the first part. */ - while (1) { - num_len = strspn(cp, "1234567890"); - if (num_len == mlen && fast_memeq(mstr, cp, mlen)) { - /* This is the line. */ - char buf[BASE64_DIGEST256_LEN+1]; - /* XXXX ignores extraneous stuff if the digest is too long. This - * seems harmless enough, right? */ - cp = strstr(cp, dstr); - if (!cp) - return -1; - cp += strlen(dstr); - strlcpy(buf, cp, sizeof(buf)); - return digest256_from_base64(digest256_out, buf); - } - if (num_len == 0 || cp[num_len] != ',') - break; - cp += num_len + 1; - } - } - return -1; -} - diff --git a/src/tor/dirvote.h b/src/tor/dirvote.h deleted file mode 100644 index 3a4951a..0000000 --- a/src/tor/dirvote.h +++ /dev/null @@ -1,148 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dirvote.h - * \brief Header file for dirvote.c. - **/ - -#ifndef TOR_DIRVOTE_H -#define TOR_DIRVOTE_H - -#include "testsupport.h" - -/** Lowest allowable value for VoteSeconds. */ -#define MIN_VOTE_SECONDS 2 -/** Lowest allowable value for DistSeconds. */ -#define MIN_DIST_SECONDS 2 -/** Smallest allowable voting interval. */ -#define MIN_VOTE_INTERVAL 300 - -/** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 17 - -/** 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 doesn't count bad exits as exits for weight */ -#define MIN_METHOD_TO_CUT_BADEXIT_WEIGHT 11 - -/** 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 - -/** Lowest consensus method where microdescs may include a "p6" line. */ -#define MIN_METHOD_FOR_P6_LINES 15 - -/** Lowest consensus method where microdescs may include an onion-key-ntor - * line */ -#define MIN_METHOD_FOR_NTOR_KEY 16 - -/** Lowest consensus method that ensures that authorities output an - * Unmeasured=1 flag for unmeasured bandwidths */ -#define MIN_METHOD_TO_CLIP_UNMEASURED_BW 17 - -/** Default bandwidth to clip unmeasured bandwidths to using method >= - * MIN_METHOD_TO_CLIP_UNMEASURED_BW */ -#define DEFAULT_MAX_UNMEASURED_BW_KB 20 - -void dirvote_free_all(void); - -/* vote manipulation */ -char *networkstatus_compute_consensus(smartlist_t *votes, - int total_authorities, - crypto_pk_t *identity_key, - crypto_pk_t *signing_key, - const char *legacy_identity_key_digest, - crypto_pk_t *legacy_signing_key, - consensus_flavor_t flavor); -int networkstatus_add_detached_signatures(networkstatus_t *target, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out); -char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free(ns_detached_signatures_t *s); - -/* cert manipulation */ -authority_cert_t *authority_cert_dup(authority_cert_t *cert); - -/* vote scheduling */ -void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out); -time_t dirvote_get_start_of_next_interval(time_t now, - int interval, - int offset); -void dirvote_recalculate_timing(const or_options_t *options, time_t now); -void dirvote_act(const or_options_t *options, time_t now); - -/* invoked on timers and by outside triggers. */ -struct pending_vote_t * dirvote_add_vote(const char *vote_body, - const char **msg_out, - int *status_out); -int dirvote_add_signatures(const char *detached_signatures_body, - const char *source, - const char **msg_out); - -/* Item access */ -const char *dirvote_get_pending_consensus(consensus_flavor_t flav); -const char *dirvote_get_pending_detached_signatures(void); -#define DGV_BY_ID 1 -#define DGV_INCLUDE_PENDING 2 -#define DGV_INCLUDE_PREVIOUS 4 -const cached_dir_t *dirvote_get_vote(const char *fp, int flags); -void set_routerstatus_from_routerinfo(routerstatus_t *rs, - node_t *node, - routerinfo_t *ri, time_t now, - int naming, int listbadexits, - int listbaddirs, int vote_on_hsdirs); -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, - int consensus_method); -ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md, - int consensus_method_low, - int consensus_method_high); -vote_microdesc_hash_t *dirvote_format_all_microdesc_vote_lines( - const routerinfo_t *ri, - time_t now, - smartlist_t *microdescriptors_out); - -int vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg); -document_signature_t *voter_get_sig_by_algorithm( - const networkstatus_voter_info_t *voter, - digest_algorithm_t alg); - -#ifdef DIRVOTE_PRIVATE -STATIC char *format_networkstatus_vote(crypto_pk_t *private_key, - networkstatus_t *v3_ns); -STATIC char *dirvote_compute_params(smartlist_t *votes, int method, - int total_authorities); -#endif - -#endif - diff --git a/src/tor/dns.c b/src/tor/dns.c deleted file mode 100644 index 9abf550..0000000 --- a/src/tor/dns.c +++ /dev/null @@ -1,2203 +0,0 @@ -/* Copyright (c) 2003-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dns.c - * \brief Implements a local cache for DNS results for Tor servers. - * This is implemented as a wrapper around Adam Langley's eventdns.c code. - * (We can't just use gethostbyname() and friends because we really need to - * be nonblocking.) - **/ - -#include "or.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "control.h" -#include "dns.h" -#include "onion_main.h" -#include "policies.h" -#include "relay.h" -#include "router.h" -#include "ht.h" -#include "sandbox.h" -#ifdef HAVE_EVENT2_DNS_H -#include -#include -#else -#include -#include "eventdns.h" -#ifndef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS -#define HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS -#endif -#endif - -#ifndef HAVE_EVENT2_DNS_H -struct evdns_base; -struct evdns_request; -#define evdns_base_new(x,y) tor_malloc(1) -#define evdns_base_clear_nameservers_and_suspend(base) \ - evdns_clear_nameservers_and_suspend() -#define evdns_base_search_clear(base) evdns_search_clear() -#define evdns_base_set_default_outgoing_bind_address(base, a, len) \ - evdns_set_default_outgoing_bind_address((a),(len)) -#define evdns_base_resolv_conf_parse(base, options, fname) \ - evdns_resolv_conf_parse((options), (fname)) -#define evdns_base_count_nameservers(base) \ - evdns_count_nameservers() -#define evdns_base_resume(base) \ - evdns_resume() -#define evdns_base_config_windows_nameservers(base) \ - evdns_config_windows_nameservers() -#define evdns_base_set_option_(base, opt, val) \ - evdns_set_option((opt),(val),DNS_OPTIONS_ALL) -/* Note: our internal eventdns.c, plus Libevent 1.4, used a 1 return to - * signify failure to launch a resolve. Libevent 2.0 uses a -1 return to - * signify a failure on a resolve, though if we're on Libevent 2.0, we should - * have event2/dns.h and never hit these macros. Regardless, 0 is success. */ -#define evdns_base_resolve_ipv4(base, addr, options, cb, ptr) \ - ((evdns_resolve_ipv4((addr), (options), (cb), (ptr))!=0) \ - ? NULL : ((void*)1)) -#define evdns_base_resolve_ipv6(base, addr, options, cb, ptr) \ - ((evdns_resolve_ipv6((addr), (options), (cb), (ptr))!=0) \ - ? NULL : ((void*)1)) -#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \ - ((evdns_resolve_reverse((addr), (options), (cb), (ptr))!=0) \ - ? NULL : ((void*)1)) -#define evdns_base_resolve_reverse_ipv6(base, addr, options, cb, ptr) \ - ((evdns_resolve_reverse_ipv6((addr), (options), (cb), (ptr))!=0) \ - ? NULL : ((void*)1)) - -#elif defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER < 0x02000303 -#define evdns_base_set_option_(base, opt, val) \ - evdns_base_set_option((base), (opt),(val),DNS_OPTIONS_ALL) - -#else -#define evdns_base_set_option_ evdns_base_set_option - -#endif - -/** Longest hostname we're willing to resolve. */ -#define MAX_ADDRESSLEN 256 - -/** How long will we wait for an answer from the resolver before we decide - * that the resolver is wedged? */ -#define RESOLVE_MAX_TIMEOUT 300 - -/** Our evdns_base; this structure handles all our name lookups. */ -static struct evdns_base *the_evdns_base = NULL; - -/** Have we currently configured nameservers with eventdns? */ -static int nameservers_configured = 0; -/** Did our most recent attempt to configure nameservers with eventdns fail? */ -static int nameserver_config_failed = 0; -/** What was the resolv_conf fname we last used when configuring the - * nameservers? Used to check whether we need to reconfigure. */ -static char *resolv_conf_fname = NULL; -/** What was the mtime on the resolv.conf file we last used when configuring - * the nameservers? Used to check whether we need to reconfigure. */ -static time_t resolv_conf_mtime = 0; - -/** Linked list of connections waiting for a DNS answer. */ -typedef struct pending_connection_t { - edge_connection_t *conn; - struct pending_connection_t *next; -} pending_connection_t; - -/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad - * pointers and memory stomping. */ -#define CACHED_RESOLVE_MAGIC 0x1234F00D - -/* Possible states for a cached resolve_t */ -/** We are waiting for the resolver system to tell us an answer here. - * When we get one, or when we time out, the state of this cached_resolve_t - * will become "DONE" and we'll possibly add a CACHED - * entry. This cached_resolve_t will be in the hash table so that we will - * know not to launch more requests for this addr, but rather to add more - * connections to the pending list for the addr. */ -#define CACHE_STATE_PENDING 0 -/** This used to be a pending cached_resolve_t, and we got an answer for it. - * Now we're waiting for this cached_resolve_t to expire. This should - * have no pending connections, and should not appear in the hash table. */ -#define CACHE_STATE_DONE 1 -/** We are caching an answer for this address. This should have no pending - * connections, and should appear in the hash table. */ -#define CACHE_STATE_CACHED 2 - -/** @name status values for a single DNS request. - * - * @{ */ -/** The DNS request is in progress. */ -#define RES_STATUS_INFLIGHT 1 -/** The DNS request finished and gave an answer */ -#define RES_STATUS_DONE_OK 2 -/** The DNS request finished and gave an error */ -#define RES_STATUS_DONE_ERR 3 -/**@}*/ - -/** A DNS request: possibly completed, possibly pending; cached_resolve - * structs are stored at the OR side in a hash table, and as a linked - * list from oldest to newest. - */ -typedef struct cached_resolve_t { - HT_ENTRY(cached_resolve_t) node; - uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */ - char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ - - union { - uint32_t addr_ipv4; /**< IPv4 addr for address, if successful. - * (In host order.) */ - int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ - } result_ipv4; /**< Outcome of IPv4 lookup */ - union { - struct in6_addr addr_ipv6; /**< IPv6 addr for address, if - * successful */ - int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */ - } result_ipv6; /**< Outcome of IPv6 lookup, if any */ - union { - char *hostname; /** A hostname, if PTR lookup happened successfully*/ - int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */ - } result_ptr; - /** @name Status fields - * - * These take one of the RES_STATUS_* values, depending on the state - * of the corresponding lookup. - * - * @{ */ - unsigned int res_status_ipv4 : 2; - unsigned int res_status_ipv6 : 2; - unsigned int res_status_hostname : 2; - /**@}*/ - uint8_t state; /**< Is this cached entry pending/done/informative? */ - - time_t expire; /**< Remove items from cache after this time. */ - uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */ - /** Connections that want to know when we get an answer for this resolve. */ - pending_connection_t *pending_connections; - /** Position of this element in the heap*/ - int minheap_idx; -} cached_resolve_t; - -static void purge_expired_resolves(time_t now); -static void dns_found_answer(const char *address, uint8_t query_type, - int dns_answer, - const tor_addr_t *addr, - const char *hostname, - uint32_t ttl); -static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolve); -static int launch_resolve(cached_resolve_t *resolve); -static void add_wildcarded_test_address(const char *address); -static int configure_nameservers(int force); -static int answer_is_wildcarded(const char *ip); -static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, - or_circuit_t *oncirc, char **resolved_to_hostname, - int *made_connection_pending_out, - cached_resolve_t **resolve_out); -static int set_exitconn_info_from_resolve(edge_connection_t *exitconn, - const cached_resolve_t *resolve, - char **hostname_out); -static int evdns_err_is_transient(int err); -static void inform_pending_connections(cached_resolve_t *resolve); -static void make_pending_resolve_cached(cached_resolve_t *cached); - -#ifdef DEBUG_DNS_CACHE -static void assert_cache_ok_(void); -#define assert_cache_ok() assert_cache_ok_() -#else -#define assert_cache_ok() STMT_NIL -#endif -static void assert_resolve_ok(cached_resolve_t *resolve); - -/** Hash table of cached_resolve objects. */ -static HT_HEAD(cache_map, cached_resolve_t) cache_root; - -/** Global: how many IPv6 requests have we made in all? */ -static uint64_t n_ipv6_requests_made = 0; -/** Global: how many IPv6 requests have timed out? */ -static uint64_t n_ipv6_timeouts = 0; -/** Global: Do we think that IPv6 DNS is broken? */ -static int dns_is_broken_for_ipv6 = 0; - -/** Function to compare hashed resolves on their addresses; used to - * implement hash tables. */ -static INLINE int -cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b) -{ - /* make this smarter one day? */ - assert_resolve_ok(a); // Not b; b may be just a search. - return !strncmp(a->address, b->address, MAX_ADDRESSLEN); -} - -/** Hash function for cached_resolve objects */ -static INLINE unsigned int -cached_resolve_hash(cached_resolve_t *a) -{ - return ht_string_hash(a->address); -} - -HT_PROTOTYPE(cache_map, cached_resolve_t, node, cached_resolve_hash, - cached_resolves_eq) -HT_GENERATE(cache_map, cached_resolve_t, node, cached_resolve_hash, - cached_resolves_eq, 0.6, malloc, realloc, free) - -/** Initialize the DNS cache. */ -static void -init_cache_map(void) -{ - HT_INIT(cache_map, &cache_root); -} - -/** Helper: called by eventdns when eventdns wants to log something. */ -static void -evdns_log_cb(int warn, const char *msg) -{ - const char *cp; - static int all_down = 0; - int severity = warn ? LOG_WARN : LOG_INFO; - if (!strcmpstart(msg, "Resolve requested for") && - get_options()->SafeLogging) { - log_info(LD_EXIT, "eventdns: Resolve requested."); - return; - } else if (!strcmpstart(msg, "Search: ")) { - return; - } - if (!strcmpstart(msg, "Nameserver ") && (cp=strstr(msg, " has failed: "))) { - char *ns = tor_strndup(msg+11, cp-(msg+11)); - const char *err = strchr(cp, ':')+2; - tor_assert(err); - /* Don't warn about a single failed nameserver; we'll warn with 'all - * nameservers have failed' if we're completely out of nameservers; - * otherwise, the situation is tolerable. */ - severity = LOG_INFO; - control_event_server_status(LOG_NOTICE, - "NAMESERVER_STATUS NS=%s STATUS=DOWN ERR=%s", - ns, escaped(err)); - tor_free(ns); - } else if (!strcmpstart(msg, "Nameserver ") && - (cp=strstr(msg, " is back up"))) { - char *ns = tor_strndup(msg+11, cp-(msg+11)); - severity = (all_down && warn) ? LOG_NOTICE : LOG_INFO; - all_down = 0; - control_event_server_status(LOG_NOTICE, - "NAMESERVER_STATUS NS=%s STATUS=UP", ns); - tor_free(ns); - } else if (!strcmp(msg, "All nameservers have failed")) { - control_event_server_status(LOG_WARN, "NAMESERVER_ALL_DOWN"); - all_down = 1; - } - tor_log(severity, LD_EXIT, "eventdns: %s", msg); -} - -/** Helper: passed to eventdns.c as a callback so it can generate random - * numbers for transaction IDs and 0x20-hack coding. */ -static void -dns_randfn_(char *b, size_t n) -{ - crypto_rand(b,n); -} - -/** Initialize the DNS subsystem; called by the OR process. */ -int -dns_init(void) -{ - init_cache_map(); - evdns_set_random_bytes_fn(dns_randfn_); - if (server_mode(get_options())) { - int r = configure_nameservers(1); - return r; - } - return 0; -} - -/** Called when DNS-related options change (or may have changed). Returns -1 - * on failure, 0 on success. */ -int -dns_reset(void) -{ - const or_options_t *options = get_options(); - if (! server_mode(options)) { - - if (!the_evdns_base) { - if (!(the_evdns_base = evdns_base_new(tor_libevent_get_base(), 0))) { - log_err(LD_BUG, "Couldn't create an evdns_base"); - return -1; - } - } - - evdns_base_clear_nameservers_and_suspend(the_evdns_base); - evdns_base_search_clear(the_evdns_base); - nameservers_configured = 0; - tor_free(resolv_conf_fname); - resolv_conf_mtime = 0; - } else { - if (configure_nameservers(0) < 0) { - return -1; - } - } - return 0; -} - -/** Return true iff the most recent attempt to initialize the DNS subsystem - * failed. */ -int -has_dns_init_failed(void) -{ - return nameserver_config_failed; -} - -/** Helper: Given a TTL from a DNS response, determine what TTL to give the - * OP that asked us to resolve it. */ -uint32_t -dns_clip_ttl(uint32_t ttl) -{ - if (ttl < MIN_DNS_TTL) - return MIN_DNS_TTL; - else if (ttl > MAX_DNS_TTL) - return MAX_DNS_TTL; - else - return ttl; -} - -/** Helper: Given a TTL from a DNS response, determine how long to hold it in - * our cache. */ -static uint32_t -dns_get_expiry_ttl(uint32_t ttl) -{ - if (ttl < MIN_DNS_TTL) - return MIN_DNS_TTL; - else if (ttl > MAX_DNS_ENTRY_AGE) - return MAX_DNS_ENTRY_AGE; - else - return ttl; -} - -/** Helper: free storage held by an entry in the DNS cache. */ -static void -free_cached_resolve_(cached_resolve_t *r) -{ - if (!r) - return; - while (r->pending_connections) { - pending_connection_t *victim = r->pending_connections; - r->pending_connections = victim->next; - tor_free(victim); - } - if (r->res_status_hostname == RES_STATUS_DONE_OK) - tor_free(r->result_ptr.hostname); - r->magic = 0xFF00FF00; - tor_free(r); -} - -/** Compare two cached_resolve_t pointers by expiry time, and return - * less-than-zero, zero, or greater-than-zero as appropriate. Used for - * the priority queue implementation. */ -static int -compare_cached_resolves_by_expiry_(const void *_a, const void *_b) -{ - const cached_resolve_t *a = _a, *b = _b; - if (a->expire < b->expire) - return -1; - else if (a->expire == b->expire) - return 0; - else - return 1; -} - -/** Priority queue of cached_resolve_t objects to let us know when they - * will expire. */ -static smartlist_t *cached_resolve_pqueue = NULL; - -static void -cached_resolve_add_answer(cached_resolve_t *resolve, - int query_type, - int dns_result, - const tor_addr_t *answer_addr, - const char *answer_hostname, - uint32_t ttl) -{ - if (query_type == DNS_PTR) { - if (resolve->res_status_hostname != RES_STATUS_INFLIGHT) - return; - - if (dns_result == DNS_ERR_NONE && answer_hostname) { - resolve->result_ptr.hostname = tor_strdup(answer_hostname); - resolve->res_status_hostname = RES_STATUS_DONE_OK; - } else { - resolve->result_ptr.err_hostname = dns_result; - resolve->res_status_hostname = RES_STATUS_DONE_ERR; - } - resolve->ttl_hostname = ttl; - } else if (query_type == DNS_IPv4_A) { - if (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT) - return; - - if (dns_result == DNS_ERR_NONE && answer_addr && - tor_addr_family(answer_addr) == AF_INET) { - resolve->result_ipv4.addr_ipv4 = tor_addr_to_ipv4h(answer_addr); - resolve->res_status_ipv4 = RES_STATUS_DONE_OK; - } else { - resolve->result_ipv4.err_ipv4 = dns_result; - resolve->res_status_ipv4 = RES_STATUS_DONE_ERR; - } - - } else if (query_type == DNS_IPv6_AAAA) { - if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT) - return; - - if (dns_result == DNS_ERR_NONE && answer_addr && - tor_addr_family(answer_addr) == AF_INET6) { - memcpy(&resolve->result_ipv6.addr_ipv6, - tor_addr_to_in6(answer_addr), - sizeof(struct in6_addr)); - resolve->res_status_ipv6 = RES_STATUS_DONE_OK; - } else { - resolve->result_ipv6.err_ipv6 = dns_result; - resolve->res_status_ipv6 = RES_STATUS_DONE_ERR; - } - } -} - -/** Return true iff there are no in-flight requests for resolve. */ -static int -cached_resolve_have_all_answers(const cached_resolve_t *resolve) -{ - return (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT && - resolve->res_status_ipv6 != RES_STATUS_INFLIGHT && - resolve->res_status_hostname != RES_STATUS_INFLIGHT); -} - -/** Set an expiry time for a cached_resolve_t, and add it to the expiry - * priority queue */ -static void -set_expiry(cached_resolve_t *resolve, time_t expires) -{ - tor_assert(resolve && resolve->expire == 0); - if (!cached_resolve_pqueue) - cached_resolve_pqueue = smartlist_new(); - resolve->expire = expires; - smartlist_pqueue_add(cached_resolve_pqueue, - compare_cached_resolves_by_expiry_, - STRUCT_OFFSET(cached_resolve_t, minheap_idx), - resolve); -} - -/** Free all storage held in the DNS cache and related structures. */ -void -dns_free_all(void) -{ - cached_resolve_t **ptr, **next, *item; - assert_cache_ok(); - if (cached_resolve_pqueue) { - SMARTLIST_FOREACH(cached_resolve_pqueue, cached_resolve_t *, res, - { - if (res->state == CACHE_STATE_DONE) - free_cached_resolve_(res); - }); - } - for (ptr = HT_START(cache_map, &cache_root); ptr != NULL; ptr = next) { - item = *ptr; - next = HT_NEXT_RMV(cache_map, &cache_root, ptr); - free_cached_resolve_(item); - } - HT_CLEAR(cache_map, &cache_root); - smartlist_free(cached_resolve_pqueue); - cached_resolve_pqueue = NULL; - tor_free(resolv_conf_fname); -} - -/** Remove every cached_resolve whose expire time is before or - * equal to now from the cache. */ -static void -purge_expired_resolves(time_t now) -{ - cached_resolve_t *resolve, *removed; - pending_connection_t *pend; - edge_connection_t *pendconn; - - assert_cache_ok(); - if (!cached_resolve_pqueue) - return; - - while (smartlist_len(cached_resolve_pqueue)) { - resolve = smartlist_get(cached_resolve_pqueue, 0); - if (resolve->expire > now) - break; - smartlist_pqueue_pop(cached_resolve_pqueue, - compare_cached_resolves_by_expiry_, - STRUCT_OFFSET(cached_resolve_t, minheap_idx)); - - if (resolve->state == CACHE_STATE_PENDING) { - log_debug(LD_EXIT, - "Expiring a dns resolve %s that's still pending. Forgot to " - "cull it? DNS resolve didn't tell us about the timeout?", - escaped_safe_str(resolve->address)); - } else if (resolve->state == CACHE_STATE_CACHED) { - log_debug(LD_EXIT, - "Forgetting old cached resolve (address %s, expires %lu)", - escaped_safe_str(resolve->address), - (unsigned long)resolve->expire); - tor_assert(!resolve->pending_connections); - } else { - tor_assert(resolve->state == CACHE_STATE_DONE); - tor_assert(!resolve->pending_connections); - } - - if (resolve->pending_connections) { - log_debug(LD_EXIT, - "Closing pending connections on timed-out DNS resolve!"); - 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; - 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); - } - } - - if (resolve->state == CACHE_STATE_CACHED || - resolve->state == CACHE_STATE_PENDING) { - removed = HT_REMOVE(cache_map, &cache_root, resolve); - if (removed != resolve) { - log_err(LD_BUG, "The expired resolve we purged didn't match any in" - " the cache. Tried to purge %s (%p); instead got %s (%p).", - resolve->address, (void*)resolve, - removed ? removed->address : "NULL", (void*)removed); - } - tor_assert(removed == resolve); - } else { - /* This should be in state DONE. Make sure it's not in the cache. */ - cached_resolve_t *tmp = HT_FIND(cache_map, &cache_root, resolve); - tor_assert(tmp != resolve); - } - if (resolve->res_status_hostname == RES_STATUS_DONE_OK) - tor_free(resolve->result_ptr.hostname); - resolve->magic = 0xF0BBF0BB; - tor_free(resolve); - } - - assert_cache_ok(); -} - -/* argument for send_resolved_cell only, meaning "let the answer type be ipv4 - * or ipv6 depending on the connection's address". */ -#define RESOLVED_TYPE_AUTO 0xff - -/** Send a response to the RESOLVE request of a connection. - * answer_type must be one of - * RESOLVED_TYPE_(AUTO|ERROR|ERROR_TRANSIENT|). - * - * If circ is provided, and we have a cached answer, send the - * answer back along circ; otherwise, send the answer back along - * conn's attached circuit. - */ -static void -send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolved) -{ - char buf[RELAY_PAYLOAD_SIZE], *cp = buf; - size_t buflen = 0; - uint32_t ttl; - - buf[0] = answer_type; - ttl = dns_clip_ttl(conn->address_ttl); - - switch (answer_type) - { - case RESOLVED_TYPE_AUTO: - if (resolved && resolved->res_status_ipv4 == RES_STATUS_DONE_OK) { - cp[0] = RESOLVED_TYPE_IPV4; - cp[1] = 4; - set_uint32(cp+2, htonl(resolved->result_ipv4.addr_ipv4)); - set_uint32(cp+6, htonl(ttl)); - cp += 10; - } - if (resolved && resolved->res_status_ipv6 == RES_STATUS_DONE_OK) { - const uint8_t *bytes = resolved->result_ipv6.addr_ipv6.s6_addr; - cp[0] = RESOLVED_TYPE_IPV6; - cp[1] = 16; - memcpy(cp+2, bytes, 16); - set_uint32(cp+18, htonl(ttl)); - cp += 22; - } - if (cp != buf) { - buflen = cp - buf; - break; - } else { - answer_type = RESOLVED_TYPE_ERROR; - /* fall through. */ - } - case RESOLVED_TYPE_ERROR_TRANSIENT: - case RESOLVED_TYPE_ERROR: - { - const char *errmsg = "Error resolving hostname"; - size_t msglen = strlen(errmsg); - - buf[0] = answer_type; - buf[1] = msglen; - strlcpy(buf+2, errmsg, sizeof(buf)-2); - set_uint32(buf+2+msglen, htonl(ttl)); - buflen = 6+msglen; - break; - } - default: - tor_assert(0); - return; - } - // log_notice(LD_EXIT, "Sending a regular RESOLVED reply: "); - - connection_edge_send_command(conn, RELAY_COMMAND_RESOLVED, buf, buflen); -} - -/** Send a response to the RESOLVE request of a connection for an in-addr.arpa - * address on connection conn which yielded the result hostname. - * The answer type will be RESOLVED_HOSTNAME. - * - * If circ is provided, and we have a cached answer, send the - * answer back along circ; otherwise, send the answer back along - * conn's attached circuit. - */ -static void -send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname) -{ - char buf[RELAY_PAYLOAD_SIZE]; - size_t buflen; - uint32_t ttl; - size_t namelen = strlen(hostname); - tor_assert(hostname); - - tor_assert(namelen < 256); - ttl = dns_clip_ttl(conn->address_ttl); - - buf[0] = RESOLVED_TYPE_HOSTNAME; - buf[1] = (uint8_t)namelen; - memcpy(buf+2, hostname, namelen); - set_uint32(buf+2+namelen, htonl(ttl)); - buflen = 2+namelen+4; - - // log_notice(LD_EXIT, "Sending a reply RESOLVED reply: %s", hostname); - connection_edge_send_command(conn, RELAY_COMMAND_RESOLVED, buf, buflen); - // log_notice(LD_EXIT, "Sent"); -} - -/** See if we have a cache entry for exitconn-\>address. If so, - * if resolve valid, put it into exitconn-\>addr and return 1. - * If resolve failed, free exitconn and return -1. - * - * (For EXIT_PURPOSE_RESOLVE connections, send back a RESOLVED error cell - * on returning -1. For EXIT_PURPOSE_CONNECT connections, there's no - * need to send back an END cell, since connection_exit_begin_conn will - * do that for us.) - * - * If we have a cached answer, send the answer back along exitconn's - * circuit. - * - * Else, if seen before and pending, add conn to the pending list, - * and return 0. - * - * Else, if not seen before, add conn to pending list, hand to - * dns farm, and return 0. - * - * Exitconn's on_circuit field must be set, but exitconn should not - * yet be linked onto the n_streams/resolving_streams list of that circuit. - * On success, link the connection to n_streams if it's an exit connection. - * On "pending", link the connection to resolving streams. Otherwise, - * clear its on_circuit field. - */ -int -dns_resolve(edge_connection_t *exitconn) -{ - or_circuit_t *oncirc = TO_OR_CIRCUIT(exitconn->on_circuit); - int is_resolve, r; - int made_connection_pending = 0; - char *hostname = NULL; - cached_resolve_t *resolve = NULL; - is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE; - - r = dns_resolve_impl(exitconn, is_resolve, oncirc, &hostname, - &made_connection_pending, &resolve); - - switch (r) { - case 1: - /* We got an answer without a lookup -- either the answer was - * cached, or it was obvious (like an IP address). */ - if (is_resolve) { - /* Send the answer back right now, and detach. */ - if (hostname) - send_resolved_hostname_cell(exitconn, hostname); - else - send_resolved_cell(exitconn, RESOLVED_TYPE_AUTO, resolve); - exitconn->on_circuit = NULL; - } else { - /* Add to the n_streams list; the calling function will send back a - * connected cell. */ - exitconn->next_stream = oncirc->n_streams; - oncirc->n_streams = exitconn; - } - break; - case 0: - /* The request is pending: add the connection into the linked list of - * resolving_streams on this circuit. */ - exitconn->base_.state = EXIT_CONN_STATE_RESOLVING; - exitconn->next_stream = oncirc->resolving_streams; - oncirc->resolving_streams = exitconn; - break; - case -2: - case -1: - /* The request failed before it could start: cancel this connection, - * and stop everybody waiting for the same connection. */ - if (is_resolve) { - send_resolved_cell(exitconn, - (r == -1) ? RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT, - NULL); - } - - exitconn->on_circuit = NULL; - - dns_cancel_pending_resolve(exitconn->base_.address); - - if (!made_connection_pending && !exitconn->base_.marked_for_close) { - /* If we made the connection pending, then we freed it already in - * dns_cancel_pending_resolve(). If we marked it for close, it'll - * get freed from the main loop. Otherwise, can free it now. */ - connection_free(TO_CONN(exitconn)); - } - break; - default: - tor_assert(0); - } - - tor_free(hostname); - return r; -} - -/** Helper function for dns_resolve: same functionality, but does not handle: - * - marking connections on error and clearing their on_circuit - * - linking connections to n_streams/resolving_streams, - * - sending resolved cells if we have an answer/error right away, - * - * Return -2 on a transient error. If it's a reverse resolve and it's - * successful, sets *hostname_out to a newly allocated string - * holding the cached reverse DNS value. - * - * Set *made_connection_pending_out to true if we have placed - * exitconn on the list of pending connections for some resolve; set it - * to false otherwise. - * - * Set *resolve_out to a cached resolve, if we found one. - */ -static int -dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, - or_circuit_t *oncirc, char **hostname_out, - int *made_connection_pending_out, - cached_resolve_t **resolve_out) -{ - cached_resolve_t *resolve; - cached_resolve_t search; - pending_connection_t *pending_connection; - int is_reverse = 0; - tor_addr_t addr; - time_t now = time(NULL); - int r; - assert_connection_ok(TO_CONN(exitconn), 0); - tor_assert(!SOCKET_OK(exitconn->base_.s)); - assert_cache_ok(); - tor_assert(oncirc); - *made_connection_pending_out = 0; - - /* first check if exitconn->base_.address is an IP. If so, we already - * know the answer. */ - if (tor_addr_parse(&addr, exitconn->base_.address) >= 0) { - if (tor_addr_family(&addr) == AF_INET || - tor_addr_family(&addr) == AF_INET6) { - tor_addr_copy(&exitconn->base_.addr, &addr); - exitconn->address_ttl = DEFAULT_DNS_TTL; - return 1; - } else { - /* XXXX unspec? Bogus? */ - return -1; - } - } - - /* If we're a non-exit, don't even do DNS lookups. */ - if (router_my_exit_policy_is_reject_star()) - return -1; - - if (address_is_invalid_destination(exitconn->base_.address, 0)) { - tor_log(LOG_PROTOCOL_WARN, LD_EXIT, - "Rejecting invalid destination address %s", - escaped_safe_str(exitconn->base_.address)); - return -1; - } - - /* then take this opportunity to see if there are any expired - * resolves in the hash table. */ - purge_expired_resolves(now); - - /* lower-case exitconn->base_.address, so it's in canonical form */ - tor_strlower(exitconn->base_.address); - - /* Check whether this is a reverse lookup. If it's malformed, or it's a - * .in-addr.arpa address but this isn't a resolve request, kill the - * connection. - */ - if ((r = tor_addr_parse_PTR_name(&addr, exitconn->base_.address, - AF_UNSPEC, 0)) != 0) { - if (r == 1) { - is_reverse = 1; - if (tor_addr_is_internal(&addr, 0)) /* internal address? */ - return -1; - } - - if (!is_reverse || !is_resolve) { - if (!is_reverse) - log_info(LD_EXIT, "Bad .in-addr.arpa address \"%s\"; sending error.", - escaped_safe_str(exitconn->base_.address)); - else if (!is_resolve) - log_info(LD_EXIT, - "Attempt to connect to a .in-addr.arpa address \"%s\"; " - "sending error.", - escaped_safe_str(exitconn->base_.address)); - - return -1; - } - //log_notice(LD_EXIT, "Looks like an address %s", - //exitconn->base_.address); - } - exitconn->is_reverse_dns_lookup = is_reverse; - - /* now check the hash table to see if 'address' is already there. */ - strlcpy(search.address, exitconn->base_.address, sizeof(search.address)); - resolve = HT_FIND(cache_map, &cache_root, &search); - if (resolve && resolve->expire > now) { /* already there */ - switch (resolve->state) { - case CACHE_STATE_PENDING: - /* add us to the pending list */ - pending_connection = tor_malloc_zero( - sizeof(pending_connection_t)); - pending_connection->conn = exitconn; - pending_connection->next = resolve->pending_connections; - resolve->pending_connections = pending_connection; - *made_connection_pending_out = 1; - log_debug(LD_EXIT,"Connection (fd "TOR_SOCKET_T_FORMAT") waiting " - "for pending DNS resolve of %s", exitconn->base_.s, - escaped_safe_str(exitconn->base_.address)); - return 0; - case CACHE_STATE_CACHED: - log_debug(LD_EXIT,"Connection (fd "TOR_SOCKET_T_FORMAT") found " - "cached answer for %s", - exitconn->base_.s, - escaped_safe_str(resolve->address)); - - *resolve_out = resolve; - - return set_exitconn_info_from_resolve(exitconn, resolve, hostname_out); - case CACHE_STATE_DONE: - log_err(LD_BUG, "Found a 'DONE' dns resolve still in the cache."); - tor_fragile_assert(); - } - tor_assert(0); - } - tor_assert(!resolve); - /* not there, need to add it */ - resolve = tor_malloc_zero(sizeof(cached_resolve_t)); - resolve->magic = CACHED_RESOLVE_MAGIC; - resolve->state = CACHE_STATE_PENDING; - resolve->minheap_idx = -1; - strlcpy(resolve->address, exitconn->base_.address, sizeof(resolve->address)); - - /* add this connection to the pending list */ - pending_connection = tor_malloc_zero(sizeof(pending_connection_t)); - pending_connection->conn = exitconn; - resolve->pending_connections = pending_connection; - *made_connection_pending_out = 1; - - /* Add this resolve to the cache and priority queue. */ - HT_INSERT(cache_map, &cache_root, resolve); - set_expiry(resolve, now + RESOLVE_MAX_TIMEOUT); - - log_debug(LD_EXIT,"Launching %s.", - escaped_safe_str(exitconn->base_.address)); - assert_cache_ok(); - - return launch_resolve(resolve); -} - -/** Given an exit connection exitconn, and a cached_resolve_t - * resolve whose DNS lookups have all succeeded or failed, update the - * appropriate fields (address_ttl and addr) of exitconn. - * - * If this is a reverse lookup, set *hostname_out to a newly allocated - * copy of the name resulting hostname. - * - * Return -2 on a transient error, -1 on a permenent error, and 1 on - * a successful lookup. - */ -static int -set_exitconn_info_from_resolve(edge_connection_t *exitconn, - const cached_resolve_t *resolve, - char **hostname_out) -{ - int ipv4_ok, ipv6_ok, answer_with_ipv4, r; - uint32_t begincell_flags; - const int is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE; - tor_assert(exitconn); - tor_assert(resolve); - - if (exitconn->is_reverse_dns_lookup) { - exitconn->address_ttl = resolve->ttl_hostname; - if (resolve->res_status_hostname == RES_STATUS_DONE_OK) { - *hostname_out = tor_strdup(resolve->result_ptr.hostname); - return 1; - } else { - return -1; - } - } - - /* If we're here then the connection wants one or either of ipv4, ipv6, and - * we can give it one or both. */ - if (is_resolve) { - begincell_flags = BEGIN_FLAG_IPV6_OK; - } else { - begincell_flags = exitconn->begincell_flags; - } - - ipv4_ok = (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) && - ! (begincell_flags & BEGIN_FLAG_IPV4_NOT_OK); - ipv6_ok = (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) && - (begincell_flags & BEGIN_FLAG_IPV6_OK) && - get_options()->IPv6Exit; - - /* Now decide which one to actually give. */ - if (ipv4_ok && ipv6_ok && is_resolve) { - answer_with_ipv4 = 1; - } else if (ipv4_ok && ipv6_ok) { - /* If we have both, see if our exit policy has an opinion. */ - const uint16_t port = exitconn->base_.port; - int ipv4_allowed, ipv6_allowed; - tor_addr_t a4, a6; - tor_addr_from_ipv4h(&a4, resolve->result_ipv4.addr_ipv4); - tor_addr_from_in6(&a6, &resolve->result_ipv6.addr_ipv6); - ipv4_allowed = !router_compare_to_my_exit_policy(&a4, port); - ipv6_allowed = !router_compare_to_my_exit_policy(&a6, port); - if (ipv4_allowed && !ipv6_allowed) { - answer_with_ipv4 = 1; - } else if (ipv6_allowed && !ipv4_allowed) { - answer_with_ipv4 = 0; - } else { - /* Our exit policy would permit both. Answer with whichever the user - * prefers */ - answer_with_ipv4 = !(begincell_flags & - BEGIN_FLAG_IPV6_PREFERRED); - } - } else { - /* Otherwise if one is okay, send it back. */ - if (ipv4_ok) { - answer_with_ipv4 = 1; - } else if (ipv6_ok) { - answer_with_ipv4 = 0; - } else { - /* Neither one was okay. Choose based on user preference. */ - answer_with_ipv4 = !(begincell_flags & - BEGIN_FLAG_IPV6_PREFERRED); - } - } - - /* Finally, we write the answer back. */ - r = 1; - if (answer_with_ipv4) { - if (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) { - tor_addr_from_ipv4h(&exitconn->base_.addr, - resolve->result_ipv4.addr_ipv4); - } else { - r = evdns_err_is_transient(resolve->result_ipv4.err_ipv4) ? -2 : -1; - } - - exitconn->address_ttl = resolve->ttl_ipv4; - } else { - if (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) { - tor_addr_from_in6(&exitconn->base_.addr, - &resolve->result_ipv6.addr_ipv6); - } else { - r = evdns_err_is_transient(resolve->result_ipv6.err_ipv6) ? -2 : -1; - } - - exitconn->address_ttl = resolve->ttl_ipv6; - } - - return r; -} - -/** Log an error and abort if conn is waiting for a DNS resolve. - */ -void -assert_connection_edge_not_dns_pending(edge_connection_t *conn) -{ - pending_connection_t *pend; - cached_resolve_t search; - -#if 1 - cached_resolve_t *resolve; - strlcpy(search.address, conn->base_.address, sizeof(search.address)); - resolve = HT_FIND(cache_map, &cache_root, &search); - if (!resolve) - return; - for (pend = resolve->pending_connections; pend; pend = pend->next) { - tor_assert(pend->conn != conn); - } -#else - cached_resolve_t **resolve; - HT_FOREACH(resolve, cache_map, &cache_root) { - for (pend = (*resolve)->pending_connections; pend; pend = pend->next) { - tor_assert(pend->conn != conn); - } - } -#endif -} - -/** Log an error and abort if any connection waiting for a DNS resolve is - * corrupted. */ -void -assert_all_pending_dns_resolves_ok(void) -{ - pending_connection_t *pend; - cached_resolve_t **resolve; - - HT_FOREACH(resolve, cache_map, &cache_root) { - for (pend = (*resolve)->pending_connections; - pend; - pend = pend->next) { - assert_connection_ok(TO_CONN(pend->conn), 0); - tor_assert(!SOCKET_OK(pend->conn->base_.s)); - tor_assert(!connection_in_array(TO_CONN(pend->conn))); - } - } -} - -/** Remove conn from the list of connections waiting for conn-\>address. - */ -void -connection_dns_remove(edge_connection_t *conn) -{ - pending_connection_t *pend, *victim; - cached_resolve_t search; - cached_resolve_t *resolve; - - tor_assert(conn->base_.type == CONN_TYPE_EXIT); - tor_assert(conn->base_.state == EXIT_CONN_STATE_RESOLVING); - - strlcpy(search.address, conn->base_.address, sizeof(search.address)); - - resolve = HT_FIND(cache_map, &cache_root, &search); - if (!resolve) { - log_notice(LD_BUG, "Address %s is not pending. Dropping.", - escaped_safe_str(conn->base_.address)); - return; - } - - tor_assert(resolve->pending_connections); - assert_connection_ok(TO_CONN(conn),0); - - pend = resolve->pending_connections; - - if (pend->conn == conn) { - resolve->pending_connections = pend->next; - tor_free(pend); - log_debug(LD_EXIT, "First connection (fd "TOR_SOCKET_T_FORMAT") no " - "longer waiting for resolve of %s", - conn->base_.s, - escaped_safe_str(conn->base_.address)); - return; - } else { - for ( ; pend->next; pend = pend->next) { - if (pend->next->conn == conn) { - victim = pend->next; - pend->next = victim->next; - tor_free(victim); - log_debug(LD_EXIT, - "Connection (fd "TOR_SOCKET_T_FORMAT") no longer waiting " - "for resolve of %s", - conn->base_.s, escaped_safe_str(conn->base_.address)); - return; /* more are pending */ - } - } - tor_assert(0); /* not reachable unless onlyconn not in pending list */ - } -} - -/** Mark all connections waiting for address for close. Then cancel - * the resolve for address itself, and remove any cached results for - * address from the cache. - */ -void -dns_cancel_pending_resolve(const char *address) -{ - pending_connection_t *pend; - cached_resolve_t search; - cached_resolve_t *resolve, *tmp; - edge_connection_t *pendconn; - circuit_t *circ; - - strlcpy(search.address, address, sizeof(search.address)); - - resolve = HT_FIND(cache_map, &cache_root, &search); - if (!resolve) - return; - - if (resolve->state != CACHE_STATE_PENDING) { - /* We can get into this state if we never actually created the pending - * resolve, due to finding an earlier cached error or something. Just - * ignore it. */ - if (resolve->pending_connections) { - log_warn(LD_BUG, - "Address %s is not pending but has pending connections!", - escaped_safe_str(address)); - tor_fragile_assert(); - } - return; - } - - if (!resolve->pending_connections) { - log_warn(LD_BUG, - "Address %s is pending but has no pending connections!", - escaped_safe_str(address)); - tor_fragile_assert(); - return; - } - tor_assert(resolve->pending_connections); - - /* mark all pending connections to fail */ - log_debug(LD_EXIT, - "Failing all connections waiting on DNS resolve of %s", - escaped_safe_str(address)); - while (resolve->pending_connections) { - pend = resolve->pending_connections; - pend->conn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; - pendconn = pend->conn; - assert_connection_ok(TO_CONN(pendconn), 0); - tor_assert(!SOCKET_OK(pendconn->base_.s)); - if (!pendconn->base_.marked_for_close) { - connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED); - } - circ = circuit_get_by_edge_conn(pendconn); - if (circ) - circuit_detach_stream(circ, pendconn); - if (!pendconn->base_.marked_for_close) - connection_free(TO_CONN(pendconn)); - resolve->pending_connections = pend->next; - tor_free(pend); - } - - tmp = HT_REMOVE(cache_map, &cache_root, resolve); - if (tmp != resolve) { - log_err(LD_BUG, "The cancelled resolve we purged didn't match any in" - " the cache. Tried to purge %s (%p); instead got %s (%p).", - resolve->address, (void*)resolve, - tmp ? tmp->address : "NULL", (void*)tmp); - } - tor_assert(tmp == resolve); - - resolve->state = CACHE_STATE_DONE; -} - -/** Return true iff address is one of the addresses we use to verify - * that well-known sites aren't being hijacked by our DNS servers. */ -static INLINE int -is_test_address(const char *address) -{ - const or_options_t *options = get_options(); - return options->ServerDNSTestAddresses && - smartlist_contains_string_case(options->ServerDNSTestAddresses, address); -} - -/** Called on the OR side when the eventdns library tells us the outcome of a - * single DNS resolve: remember the answer, and tell all pending connections - * about the result of the lookup if the lookup is now done. (address - * is a NUL-terminated string containing the address to look up; - * query_type is one of DNS_{IPv4_A,IPv6_AAAA,PTR}; dns_answer - * is DNS_OK or one of DNS_ERR_*, addr is an IPv4 or IPv6 address if we - * got one; hostname is a hostname fora PTR request if we got one, and - * ttl is the time-to-live of this answer, in seconds.) - */ -static void -dns_found_answer(const char *address, uint8_t query_type, - int dns_answer, - const tor_addr_t *addr, - const char *hostname, uint32_t ttl) -{ - cached_resolve_t search; - cached_resolve_t *resolve; - - assert_cache_ok(); - - strlcpy(search.address, address, sizeof(search.address)); - - resolve = HT_FIND(cache_map, &cache_root, &search); - if (!resolve) { - int is_test_addr = is_test_address(address); - if (!is_test_addr) - log_info(LD_EXIT,"Resolved unasked address %s; ignoring.", - escaped_safe_str(address)); - return; - } - assert_resolve_ok(resolve); - - if (resolve->state != CACHE_STATE_PENDING) { - /* XXXX Maybe update addr? or check addr for consistency? Or let - * VALID replace FAILED? */ - int is_test_addr = is_test_address(address); - if (!is_test_addr) - log_notice(LD_EXIT, - "Resolved %s which was already resolved; ignoring", - escaped_safe_str(address)); - tor_assert(resolve->pending_connections == NULL); - return; - } - - cached_resolve_add_answer(resolve, query_type, dns_answer, - addr, hostname, ttl); - - if (cached_resolve_have_all_answers(resolve)) { - inform_pending_connections(resolve); - - make_pending_resolve_cached(resolve); - } -} - -/** Given a pending cached_resolve_t that we just finished resolving, - * inform every connection that was waiting for the outcome of that - * resolution. */ -static void -inform_pending_connections(cached_resolve_t *resolve) -{ - pending_connection_t *pend; - edge_connection_t *pendconn; - int r; - - while (resolve->pending_connections) { - char *hostname = NULL; - pend = resolve->pending_connections; - 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; - } - - r = set_exitconn_info_from_resolve(pendconn, - resolve, - &hostname); - - if (r < 0) { - /* prevent double-remove. */ - pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; - if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) { - connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED); - /* This detach must happen after we send the end cell. */ - circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - } else { - send_resolved_cell(pendconn, r == -1 ? - RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT, - NULL); - /* This detach must happen after we send the resolved cell. */ - circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - } - connection_free(TO_CONN(pendconn)); - } else { - circuit_t *circ; - if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) { - /* prevent double-remove. */ - pend->conn->base_.state = EXIT_CONN_STATE_CONNECTING; - - circ = circuit_get_by_edge_conn(pend->conn); - tor_assert(circ); - tor_assert(!CIRCUIT_IS_ORIGIN(circ)); - /* unlink pend->conn from resolving_streams, */ - circuit_detach_stream(circ, pend->conn); - /* and link it to n_streams */ - pend->conn->next_stream = TO_OR_CIRCUIT(circ)->n_streams; - pend->conn->on_circuit = circ; - TO_OR_CIRCUIT(circ)->n_streams = pend->conn; - - connection_exit_connect(pend->conn); - } else { - /* prevent double-remove. This isn't really an accurate state, - * but it does the right thing. */ - pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; - if (pendconn->is_reverse_dns_lookup) - send_resolved_hostname_cell(pendconn, hostname); - else - send_resolved_cell(pendconn, RESOLVED_TYPE_AUTO, resolve); - circ = circuit_get_by_edge_conn(pendconn); - tor_assert(circ); - circuit_detach_stream(circ, pendconn); - connection_free(TO_CONN(pendconn)); - } - } - resolve->pending_connections = pend->next; - tor_free(pend); - } -} - -/** Remove a pending cached_resolve_t from the hashtable, and add a - * corresponding cached cached_resolve_t. - * - * This function is only necessary because of the perversity of our - * cache timeout code; see inline comment for ideas on eliminating it. - **/ -static void -make_pending_resolve_cached(cached_resolve_t *resolve) -{ - cached_resolve_t *removed; - - resolve->state = CACHE_STATE_DONE; - removed = HT_REMOVE(cache_map, &cache_root, resolve); - if (removed != resolve) { - log_err(LD_BUG, "The pending resolve we found wasn't removable from" - " the cache. Tried to purge %s (%p); instead got %s (%p).", - resolve->address, (void*)resolve, - removed ? removed->address : "NULL", (void*)removed); - } - assert_resolve_ok(resolve); - assert_cache_ok(); - /* The resolve will eventually just hit the time-out in the expiry queue and - * expire. See fd0bafb0dedc7e2 for a brief explanation of how this got that - * way. XXXXX we could do better!*/ - - { - cached_resolve_t *new_resolve = tor_memdup(resolve, - sizeof(cached_resolve_t)); - uint32_t ttl = UINT32_MAX; - new_resolve->expire = 0; /* So that set_expiry won't croak. */ - if (resolve->res_status_hostname == RES_STATUS_DONE_OK) - new_resolve->result_ptr.hostname = - tor_strdup(resolve->result_ptr.hostname); - - new_resolve->state = CACHE_STATE_CACHED; - - assert_resolve_ok(new_resolve); - HT_INSERT(cache_map, &cache_root, new_resolve); - - if ((resolve->res_status_ipv4 == RES_STATUS_DONE_OK || - resolve->res_status_ipv4 == RES_STATUS_DONE_ERR) && - resolve->ttl_ipv4 < ttl) - ttl = resolve->ttl_ipv4; - - if ((resolve->res_status_ipv6 == RES_STATUS_DONE_OK || - resolve->res_status_ipv6 == RES_STATUS_DONE_ERR) && - resolve->ttl_ipv6 < ttl) - ttl = resolve->ttl_ipv6; - - if ((resolve->res_status_hostname == RES_STATUS_DONE_OK || - resolve->res_status_hostname == RES_STATUS_DONE_ERR) && - resolve->ttl_hostname < ttl) - ttl = resolve->ttl_hostname; - - set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl)); - } - - assert_cache_ok(); -} - -/** Eventdns helper: return true iff the eventdns result err is - * a transient failure. */ -static int -evdns_err_is_transient(int err) -{ - switch (err) - { - case DNS_ERR_SERVERFAILED: - case DNS_ERR_TRUNCATED: - case DNS_ERR_TIMEOUT: - return 1; - default: - return 0; - } -} - -/** Configure eventdns nameservers if force is true, or if the configuration - * has changed since the last time we called this function, or if we failed on - * our last attempt. On Unix, this reads from /etc/resolv.conf or - * options->ServerDNSResolvConfFile; on Windows, this reads from - * options->ServerDNSResolvConfFile or the registry. Return 0 on success or - * -1 on failure. */ -static int -configure_nameservers(int force) -{ - const or_options_t *options; - const char *conf_fname; - struct stat st; - int r, flags; - options = get_options(); - conf_fname = options->ServerDNSResolvConfFile; -#ifndef _WIN32 - if (!conf_fname) - conf_fname = "/etc/resolv.conf"; -#endif - flags = DNS_OPTIONS_ALL; - - if (!the_evdns_base) { - if (!(the_evdns_base = evdns_base_new(tor_libevent_get_base(), 0))) { - log_err(LD_BUG, "Couldn't create an evdns_base"); - return -1; - } - } - -#ifdef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS - if (! tor_addr_is_null(&options->OutboundBindAddressIPv4_)) { - int socklen; - struct sockaddr_storage ss; - socklen = tor_addr_to_sockaddr(&options->OutboundBindAddressIPv4_, 0, - (struct sockaddr *)&ss, sizeof(ss)); - if (socklen <= 0) { - log_warn(LD_BUG, "Couldn't convert outbound bind address to sockaddr." - " Ignoring."); - } else { - evdns_base_set_default_outgoing_bind_address(the_evdns_base, - (struct sockaddr *)&ss, - socklen); - } - } -#endif - - evdns_set_log_fn(evdns_log_cb); - if (conf_fname) { - if (stat(sandbox_intern_string(conf_fname), &st)) { - log_warn(LD_EXIT, "Unable to stat resolver configuration in '%s': %s", - conf_fname, strerror(errno)); - goto err; - } - if (!force && resolv_conf_fname && !strcmp(conf_fname,resolv_conf_fname) - && st.st_mtime == resolv_conf_mtime) { - log_info(LD_EXIT, "No change to '%s'", conf_fname); - return 0; - } - if (nameservers_configured) { - evdns_base_search_clear(the_evdns_base); - evdns_base_clear_nameservers_and_suspend(the_evdns_base); - } -#if defined(DNS_OPTION_HOSTSFILE) && defined(USE_LIBSECCOMP) - if (flags & DNS_OPTION_HOSTSFILE) { - flags ^= DNS_OPTION_HOSTSFILE; - evdns_base_load_hosts(the_evdns_base, - sandbox_intern_string("/etc/hosts")); - } -#endif - log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname); - if ((r = evdns_base_resolv_conf_parse(the_evdns_base, flags, - sandbox_intern_string(conf_fname)))) { - log_warn(LD_EXIT, "Unable to parse '%s', or no nameservers in '%s' (%d)", - conf_fname, conf_fname, r); - goto err; - } - if (evdns_base_count_nameservers(the_evdns_base) == 0) { - log_warn(LD_EXIT, "Unable to find any nameservers in '%s'.", conf_fname); - goto err; - } - tor_free(resolv_conf_fname); - resolv_conf_fname = tor_strdup(conf_fname); - resolv_conf_mtime = st.st_mtime; - if (nameservers_configured) - evdns_base_resume(the_evdns_base); - } -#ifdef _WIN32 - else { - if (nameservers_configured) { - evdns_base_search_clear(the_evdns_base); - evdns_base_clear_nameservers_and_suspend(the_evdns_base); - } - if (evdns_base_config_windows_nameservers(the_evdns_base)) { - log_warn(LD_EXIT,"Could not config nameservers."); - goto err; - } - if (evdns_base_count_nameservers(the_evdns_base) == 0) { - log_warn(LD_EXIT, "Unable to find any platform nameservers in " - "your Windows configuration."); - goto err; - } - if (nameservers_configured) - evdns_base_resume(the_evdns_base); - tor_free(resolv_conf_fname); - resolv_conf_mtime = 0; - } -#endif - -#define SET(k,v) evdns_base_set_option_(the_evdns_base, (k), (v)) - - if (evdns_base_count_nameservers(the_evdns_base) == 1) { - SET("max-timeouts:", "16"); - SET("timeout:", "10"); - } else { - SET("max-timeouts:", "3"); - SET("timeout:", "5"); - } - - if (options->ServerDNSRandomizeCase) - SET("randomize-case:", "1"); - else - SET("randomize-case:", "0"); - -#undef SET - - dns_servers_relaunch_checks(); - - nameservers_configured = 1; - if (nameserver_config_failed) { - nameserver_config_failed = 0; - /* XXX the three calls to republish the descriptor might be producing - * descriptors that are only cosmetically different, especially on - * non-exit relays! -RD */ - mark_my_descriptor_dirty("dns resolvers back"); - } - return 0; - err: - nameservers_configured = 0; - if (! nameserver_config_failed) { - nameserver_config_failed = 1; - mark_my_descriptor_dirty("dns resolvers failed"); - } - return -1; -} - -/** For eventdns: Called when we get an answer for a request we launched. - * See eventdns.h for arguments; 'arg' holds the address we tried to resolve. - */ -static void -evdns_callback(int result, char type, int count, int ttl, void *addresses, - void *arg) -{ - char *arg_ = arg; - uint8_t orig_query_type = arg_[0]; - char *string_address = arg_ + 1; - tor_addr_t addr; - const char *hostname = NULL; - int was_wildcarded = 0; - - tor_addr_make_unspec(&addr); - - /* Keep track of whether IPv6 is working */ - if (type == DNS_IPv6_AAAA) { - if (result == DNS_ERR_TIMEOUT) { - ++n_ipv6_timeouts; - } - - if (n_ipv6_timeouts > 10 && - n_ipv6_timeouts > n_ipv6_requests_made / 2) { - if (! dns_is_broken_for_ipv6) { - log_notice(LD_EXIT, "More than half of our IPv6 requests seem to " - "have timed out. I'm going to assume I can't get AAAA " - "responses."); - dns_is_broken_for_ipv6 = 1; - } - } - } - - if (result == DNS_ERR_NONE) { - if (type == DNS_IPv4_A && count) { - char answer_buf[INET_NTOA_BUF_LEN+1]; - char *escaped_address; - uint32_t *addrs = addresses; - tor_addr_from_ipv4n(&addr, addrs[0]); - - tor_addr_to_str(answer_buf, &addr, sizeof(answer_buf), 0); - escaped_address = esc_for_log(string_address); - - if (answer_is_wildcarded(answer_buf)) { - log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked " - "address %s; treating as a failure.", - safe_str(escaped_address), - escaped_safe_str(answer_buf)); - was_wildcarded = 1; - tor_addr_make_unspec(&addr); - result = DNS_ERR_NOTEXIST; - } else { - log_debug(LD_EXIT, "eventdns said that %s resolves to %s", - safe_str(escaped_address), - escaped_safe_str(answer_buf)); - } - tor_free(escaped_address); - } else if (type == DNS_IPv6_AAAA && count) { - char answer_buf[TOR_ADDR_BUF_LEN]; - char *escaped_address; - struct in6_addr *addrs = addresses; - tor_addr_from_in6(&addr, &addrs[0]); - tor_inet_ntop(AF_INET6, &addrs[0], answer_buf, sizeof(answer_buf)); - escaped_address = esc_for_log(string_address); - - if (answer_is_wildcarded(answer_buf)) { - log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked " - "address %s; treating as a failure.", - safe_str(escaped_address), - escaped_safe_str(answer_buf)); - was_wildcarded = 1; - tor_addr_make_unspec(&addr); - result = DNS_ERR_NOTEXIST; - } else { - log_debug(LD_EXIT, "eventdns said that %s resolves to %s", - safe_str(escaped_address), - escaped_safe_str(answer_buf)); - } - tor_free(escaped_address); - } else if (type == DNS_PTR && count) { - char *escaped_address; - hostname = ((char**)addresses)[0]; - escaped_address = esc_for_log(string_address); - log_debug(LD_EXIT, "eventdns said that %s resolves to %s", - safe_str(escaped_address), - escaped_safe_str(hostname)); - tor_free(escaped_address); - } else if (count) { - log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.", - escaped_safe_str(string_address)); - } else { - log_warn(LD_BUG, "eventdns returned no addresses or error for %s!", - escaped_safe_str(string_address)); - } - } - if (was_wildcarded) { - if (is_test_address(string_address)) { - /* Ick. We're getting redirected on known-good addresses. Our DNS - * server must really hate us. */ - add_wildcarded_test_address(string_address); - } - } - - if (orig_query_type && type && orig_query_type != type) { - log_warn(LD_BUG, "Weird; orig_query_type == %d but type == %d", - (int)orig_query_type, (int)type); - } - if (result != DNS_ERR_SHUTDOWN) - dns_found_answer(string_address, orig_query_type, - result, &addr, hostname, ttl); - - tor_free(arg_); -} - -/** Start a single DNS resolve for address (if query_type is - * DNS_IPv4_A or DNS_IPv6_AAAA) ptr_address (if query_type is - * DNS_PTR). Return 0 if we launched the request, -1 otherwise. */ -static int -launch_one_resolve(const char *address, uint8_t query_type, - const tor_addr_t *ptr_address) -{ - const int options = get_options()->ServerDNSSearchDomains ? 0 - : DNS_QUERY_NO_SEARCH; - const size_t addr_len = strlen(address); - struct evdns_request *req = 0; - char *addr = tor_malloc(addr_len + 2); - addr[0] = (char) query_type; - memcpy(addr+1, address, addr_len + 1); - - switch (query_type) { - case DNS_IPv4_A: - req = evdns_base_resolve_ipv4(the_evdns_base, - address, options, evdns_callback, addr); - break; - case DNS_IPv6_AAAA: - req = evdns_base_resolve_ipv6(the_evdns_base, - address, options, evdns_callback, addr); - ++n_ipv6_requests_made; - break; - case DNS_PTR: - if (tor_addr_family(ptr_address) == AF_INET) - req = evdns_base_resolve_reverse(the_evdns_base, - tor_addr_to_in(ptr_address), - DNS_QUERY_NO_SEARCH, - evdns_callback, addr); - else if (tor_addr_family(ptr_address) == AF_INET6) - req = evdns_base_resolve_reverse_ipv6(the_evdns_base, - tor_addr_to_in6(ptr_address), - DNS_QUERY_NO_SEARCH, - evdns_callback, addr); - else - log_warn(LD_BUG, "Called with PTR query and unexpected address family"); - break; - default: - log_warn(LD_BUG, "Called with unexpectd query type %d", (int)query_type); - break; - } - - if (req) { - return 0; - } else { - tor_free(addr); - return -1; - } -} - -/** For eventdns: start resolving as necessary to find the target for - * exitconn. Returns -1 on error, -2 on transient error, - * 0 on "resolve launched." */ -static int -launch_resolve(cached_resolve_t *resolve) -{ - tor_addr_t a; - int r; - - if (get_options()->DisableNetwork) - return -1; - - /* What? Nameservers not configured? Sounds like a bug. */ - if (!nameservers_configured) { - log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve " - "launched. Configuring."); - if (configure_nameservers(1) < 0) { - return -1; - } - } - - r = tor_addr_parse_PTR_name( - &a, resolve->address, AF_UNSPEC, 0); - - tor_assert(the_evdns_base); - if (r == 0) { - log_info(LD_EXIT, "Launching eventdns request for %s", - escaped_safe_str(resolve->address)); - resolve->res_status_ipv4 = RES_STATUS_INFLIGHT; - if (get_options()->IPv6Exit) - resolve->res_status_ipv6 = RES_STATUS_INFLIGHT; - - if (launch_one_resolve(resolve->address, DNS_IPv4_A, NULL) < 0) { - resolve->res_status_ipv4 = 0; - r = -1; - } - - if (r==0 && get_options()->IPv6Exit) { - /* We ask for an IPv6 address for *everything*. */ - if (launch_one_resolve(resolve->address, DNS_IPv6_AAAA, NULL) < 0) { - resolve->res_status_ipv6 = 0; - r = -1; - } - } - } else if (r == 1) { - r = 0; - log_info(LD_EXIT, "Launching eventdns reverse request for %s", - escaped_safe_str(resolve->address)); - resolve->res_status_hostname = RES_STATUS_INFLIGHT; - if (launch_one_resolve(resolve->address, DNS_PTR, &a) < 0) { - resolve->res_status_hostname = 0; - r = -1; - } - } else if (r == -1) { - log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here."); - } - - if (r < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_EXIT, "eventdns rejected address %s.", - escaped_safe_str(resolve->address)); - } - return r; -} - -/** How many requests for bogus addresses have we launched so far? */ -static int n_wildcard_requests = 0; - -/** Map from dotted-quad IP address in response to an int holding how many - * times we've seen it for a randomly generated (hopefully bogus) address. It - * would be easier to use definitely-invalid addresses (as specified by - * RFC2606), but see comment in dns_launch_wildcard_checks(). */ -static strmap_t *dns_wildcard_response_count = NULL; - -/** If present, a list of dotted-quad IP addresses that we are pretty sure our - * nameserver wants to return in response to requests for nonexistent domains. - */ -static smartlist_t *dns_wildcard_list = NULL; -/** True iff we've logged about a single address getting wildcarded. - * Subsequent warnings will be less severe. */ -static int dns_wildcard_one_notice_given = 0; -/** True iff we've warned that our DNS server is wildcarding too many failures. - */ -static int dns_wildcard_notice_given = 0; - -/** List of supposedly good addresses that are getting wildcarded to the - * same addresses as nonexistent addresses. */ -static smartlist_t *dns_wildcarded_test_address_list = NULL; -/** True iff we've warned about a test address getting wildcarded */ -static int dns_wildcarded_test_address_notice_given = 0; -/** True iff all addresses seem to be getting wildcarded. */ -static int dns_is_completely_invalid = 0; - -/** Called when we see id (a dotted quad or IPv6 address) in response - * to a request for a hopefully bogus address. */ -static void -wildcard_increment_answer(const char *id) -{ - int *ip; - if (!dns_wildcard_response_count) - dns_wildcard_response_count = strmap_new(); - - ip = strmap_get(dns_wildcard_response_count, id); // may be null (0) - if (!ip) { - ip = tor_malloc_zero(sizeof(int)); - strmap_set(dns_wildcard_response_count, id, ip); - } - ++*ip; - - if (*ip > 5 && n_wildcard_requests > 10) { - if (!dns_wildcard_list) dns_wildcard_list = smartlist_new(); - if (!smartlist_contains_string(dns_wildcard_list, id)) { - tor_log(dns_wildcard_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT, - "Your DNS provider has given \"%s\" as an answer for %d different " - "invalid addresses. Apparently they are hijacking DNS failures. " - "I'll try to correct for this by treating future occurrences of " - "\"%s\" as 'not found'.", id, *ip, id); - smartlist_add(dns_wildcard_list, tor_strdup(id)); - } - if (!dns_wildcard_notice_given) - control_event_server_status(LOG_NOTICE, "DNS_HIJACKED"); - dns_wildcard_notice_given = 1; - } -} - -/** Note that a single test address (one believed to be good) seems to be - * getting redirected to the same IP as failures are. */ -static void -add_wildcarded_test_address(const char *address) -{ - int n, n_test_addrs; - if (!dns_wildcarded_test_address_list) - dns_wildcarded_test_address_list = smartlist_new(); - - if (smartlist_contains_string_case(dns_wildcarded_test_address_list, - address)) - return; - - n_test_addrs = get_options()->ServerDNSTestAddresses ? - smartlist_len(get_options()->ServerDNSTestAddresses) : 0; - - smartlist_add(dns_wildcarded_test_address_list, tor_strdup(address)); - n = smartlist_len(dns_wildcarded_test_address_list); - if (n > n_test_addrs/2) { - tor_log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE, - LD_EXIT, "Your DNS provider tried to redirect \"%s\" to a junk " - "address. It has done this with %d test addresses so far. I'm " - "going to stop being an exit node for now, since our DNS seems so " - "broken.", address, n); - if (!dns_is_completely_invalid) { - dns_is_completely_invalid = 1; - mark_my_descriptor_dirty("dns hijacking confirmed"); - } - if (!dns_wildcarded_test_address_notice_given) - control_event_server_status(LOG_WARN, "DNS_USELESS"); - dns_wildcarded_test_address_notice_given = 1; - } -} - -/** Callback function when we get an answer (possibly failing) for a request - * for a (hopefully) nonexistent domain. */ -static void -evdns_wildcard_check_callback(int result, char type, int count, int ttl, - void *addresses, void *arg) -{ - (void)ttl; - ++n_wildcard_requests; - if (result == DNS_ERR_NONE && count) { - char *string_address = arg; - int i; - if (type == DNS_IPv4_A) { - const uint32_t *addrs = addresses; - for (i = 0; i < count; ++i) { - char answer_buf[INET_NTOA_BUF_LEN+1]; - struct in_addr in; - in.s_addr = addrs[i]; - tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf)); - wildcard_increment_answer(answer_buf); - } - } else if (type == DNS_IPv6_AAAA) { - const struct in6_addr *addrs = addresses; - for (i = 0; i < count; ++i) { - char answer_buf[TOR_ADDR_BUF_LEN+1]; - tor_inet_ntop(AF_INET6, &addrs[i], answer_buf, sizeof(answer_buf)); - wildcard_increment_answer(answer_buf); - } - } - - tor_log(dns_wildcard_one_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT, - "Your DNS provider gave an answer for \"%s\", which " - "is not supposed to exist. Apparently they are hijacking " - "DNS failures. Trying to correct for this. We've noticed %d " - "possibly bad address%s so far.", - string_address, strmap_size(dns_wildcard_response_count), - (strmap_size(dns_wildcard_response_count) == 1) ? "" : "es"); - dns_wildcard_one_notice_given = 1; - } - tor_free(arg); -} - -/** Launch a single request for a nonexistent hostname consisting of between - * min_len and max_len random (plausible) characters followed by - * suffix */ -static void -launch_wildcard_check(int min_len, int max_len, int is_ipv6, - const char *suffix) -{ - char *addr; - struct evdns_request *req; - - addr = crypto_random_hostname(min_len, max_len, "", suffix); - log_info(LD_EXIT, "Testing whether our DNS server is hijacking nonexistent " - "domains with request for bogus hostname \"%s\"", addr); - - tor_assert(the_evdns_base); - if (is_ipv6) - req = evdns_base_resolve_ipv6( - the_evdns_base, - /* This "addr" tells us which address to resolve */ - addr, - DNS_QUERY_NO_SEARCH, evdns_wildcard_check_callback, - /* This "addr" is an argument to the callback*/ addr); - else - req = evdns_base_resolve_ipv4( - the_evdns_base, - /* This "addr" tells us which address to resolve */ - addr, - DNS_QUERY_NO_SEARCH, evdns_wildcard_check_callback, - /* This "addr" is an argument to the callback*/ addr); - if (!req) { - /* There is no evdns request in progress; stop addr from getting leaked */ - tor_free(addr); - } -} - -/** Launch attempts to resolve a bunch of known-good addresses (configured in - * ServerDNSTestAddresses). [Callback for a libevent timer] */ -static void -launch_test_addresses(evutil_socket_t fd, short event, void *args) -{ - const or_options_t *options = get_options(); - (void)fd; - (void)event; - (void)args; - - if (options->DisableNetwork) - return; - - log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " - "hijack *everything*."); - /* This situation is worse than the failure-hijacking situation. When this - * happens, we're no good for DNS requests at all, and we shouldn't really - * be an exit server.*/ - if (options->ServerDNSTestAddresses) { - - tor_assert(the_evdns_base); - SMARTLIST_FOREACH_BEGIN(options->ServerDNSTestAddresses, - const char *, address) { - if (launch_one_resolve(address, DNS_IPv4_A, NULL) < 0) { - log_info(LD_EXIT, "eventdns rejected test address %s", - escaped_safe_str(address)); - } - - if (launch_one_resolve(address, DNS_IPv6_AAAA, NULL) < 0) { - log_info(LD_EXIT, "eventdns rejected test address %s", - escaped_safe_str(address)); - } - } SMARTLIST_FOREACH_END(address); - } -} - -#define N_WILDCARD_CHECKS 2 - -/** Launch DNS requests for a few nonexistent hostnames and a few well-known - * hostnames, and see if we can catch our nameserver trying to hijack them and - * map them to a stupid "I couldn't find ggoogle.com but maybe you'd like to - * buy these lovely encyclopedias" page. */ -static void -dns_launch_wildcard_checks(void) -{ - int i, ipv6; - log_info(LD_EXIT, "Launching checks to see whether our nameservers like " - "to hijack DNS failures."); - for (ipv6 = 0; ipv6 <= 1; ++ipv6) { - for (i = 0; i < N_WILDCARD_CHECKS; ++i) { - /* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly - * attempt to 'comply' with rfc2606, refrain from giving A records for - * these. This is the standards-compliance equivalent of making sure - * that your crackhouse's elevator inspection certificate is up to date. - */ - launch_wildcard_check(2, 16, ipv6, ".invalid"); - launch_wildcard_check(2, 16, ipv6, ".test"); - - /* These will break specs if there are ever any number of - * 8+-character top-level domains. */ - launch_wildcard_check(8, 16, ipv6, ""); - - /* Try some random .com/org/net domains. This will work fine so long as - * not too many resolve to the same place. */ - launch_wildcard_check(8, 16, ipv6, ".com"); - launch_wildcard_check(8, 16, ipv6, ".org"); - launch_wildcard_check(8, 16, ipv6, ".net"); - } - } -} - -/** If appropriate, start testing whether our DNS servers tend to lie to - * us. */ -void -dns_launch_correctness_checks(void) -{ - static struct event *launch_event = NULL; - struct timeval timeout; - if (!get_options()->ServerDNSDetectHijacking) - return; - dns_launch_wildcard_checks(); - - /* Wait a while before launching requests for test addresses, so we can - * get the results from checking for wildcarding. */ - if (! launch_event) - launch_event = tor_evtimer_new(tor_libevent_get_base(), - launch_test_addresses, NULL); - timeout.tv_sec = 30; - timeout.tv_usec = 0; - if (evtimer_add(launch_event, &timeout)<0) { - log_warn(LD_BUG, "Couldn't add timer for checking for dns hijacking"); - } -} - -/** Return true iff our DNS servers lie to us too much to be trusted. */ -int -dns_seems_to_be_broken(void) -{ - return dns_is_completely_invalid; -} - -/** Return true iff we think that IPv6 hostname lookup is broken */ -int -dns_seems_to_be_broken_for_ipv6(void) -{ - return dns_is_broken_for_ipv6; -} - -/** Forget what we've previously learned about our DNS servers' correctness. */ -void -dns_reset_correctness_checks(void) -{ - strmap_free(dns_wildcard_response_count, tor_free_); - dns_wildcard_response_count = NULL; - - n_wildcard_requests = 0; - - n_ipv6_requests_made = n_ipv6_timeouts = 0; - - if (dns_wildcard_list) { - SMARTLIST_FOREACH(dns_wildcard_list, char *, cp, tor_free(cp)); - smartlist_clear(dns_wildcard_list); - } - if (dns_wildcarded_test_address_list) { - SMARTLIST_FOREACH(dns_wildcarded_test_address_list, char *, cp, - tor_free(cp)); - smartlist_clear(dns_wildcarded_test_address_list); - } - dns_wildcard_one_notice_given = dns_wildcard_notice_given = - dns_wildcarded_test_address_notice_given = dns_is_completely_invalid = - dns_is_broken_for_ipv6 = 0; -} - -/** Return true iff we have noticed that the dotted-quad ip has been - * returned in response to requests for nonexistent hostnames. */ -static int -answer_is_wildcarded(const char *ip) -{ - return dns_wildcard_list && smartlist_contains_string(dns_wildcard_list, ip); -} - -/** Exit with an assertion if resolve is corrupt. */ -static void -assert_resolve_ok(cached_resolve_t *resolve) -{ - tor_assert(resolve); - tor_assert(resolve->magic == CACHED_RESOLVE_MAGIC); - tor_assert(strlen(resolve->address) < MAX_ADDRESSLEN); - tor_assert(tor_strisnonupper(resolve->address)); - if (resolve->state != CACHE_STATE_PENDING) { - tor_assert(!resolve->pending_connections); - } - if (resolve->state == CACHE_STATE_PENDING || - resolve->state == CACHE_STATE_DONE) { -#if 0 - tor_assert(!resolve->ttl); - if (resolve->is_reverse) - tor_assert(!resolve->hostname); - else - tor_assert(!resolve->result_ipv4.addr_ipv4); -#endif - /*XXXXX ADD MORE */ - } -} - -/** Return the number of DNS cache entries as an int */ -static int -dns_cache_entry_count(void) -{ - return HT_SIZE(&cache_root); -} - -/** Log memory information about our internal DNS cache at level 'severity'. */ -void -dump_dns_mem_usage(int severity) -{ - /* This should never be larger than INT_MAX. */ - int hash_count = dns_cache_entry_count(); - size_t hash_mem = sizeof(struct cached_resolve_t) * hash_count; - hash_mem += HT_MEM_USAGE(&cache_root); - - /* Print out the count and estimated size of our &cache_root. It undercounts - hostnames in cached reverse resolves. - */ - tor_log(severity, LD_MM, "Our DNS cache has %d entries.", hash_count); - tor_log(severity, LD_MM, "Our DNS cache size is approximately %u bytes.", - (unsigned)hash_mem); -} - -#ifdef DEBUG_DNS_CACHE -/** Exit with an assertion if the DNS cache is corrupt. */ -static void -assert_cache_ok_(void) -{ - cached_resolve_t **resolve; - int bad_rep = _cache_map_HT_REP_IS_BAD(&cache_root); - if (bad_rep) { - log_err(LD_BUG, "Bad rep type %d on dns cache hash table", bad_rep); - tor_assert(!bad_rep); - } - - HT_FOREACH(resolve, cache_map, &cache_root) { - assert_resolve_ok(*resolve); - tor_assert((*resolve)->state != CACHE_STATE_DONE); - } - if (!cached_resolve_pqueue) - return; - - smartlist_pqueue_assert_ok(cached_resolve_pqueue, - compare_cached_resolves_by_expiry_, - STRUCT_OFFSET(cached_resolve_t, minheap_idx)); - - SMARTLIST_FOREACH(cached_resolve_pqueue, cached_resolve_t *, res, - { - if (res->state == CACHE_STATE_DONE) { - cached_resolve_t *found = HT_FIND(cache_map, &cache_root, res); - tor_assert(!found || found != res); - } else { - cached_resolve_t *found = HT_FIND(cache_map, &cache_root, res); - tor_assert(found); - } - }); -} -#endif - diff --git a/src/tor/dns.h b/src/tor/dns.h deleted file mode 100644 index 022cd4a..0000000 --- a/src/tor/dns.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dns.h - * \brief Header file for dns.c. - **/ - -#ifndef TOR_DNS_H -#define TOR_DNS_H - -int dns_init(void); -int has_dns_init_failed(void); -void dns_free_all(void); -uint32_t dns_clip_ttl(uint32_t ttl); -int dns_reset(void); -void connection_dns_remove(edge_connection_t *conn); -void assert_connection_edge_not_dns_pending(edge_connection_t *conn); -void assert_all_pending_dns_resolves_ok(void); -void dns_cancel_pending_resolve(const char *question); -int dns_resolve(edge_connection_t *exitconn); -void dns_launch_correctness_checks(void); -int dns_seems_to_be_broken(void); -int dns_seems_to_be_broken_for_ipv6(void); -void dns_reset_correctness_checks(void); -void dump_dns_mem_usage(int severity); - -#endif - diff --git a/src/tor/dnsserv.c b/src/tor/dnsserv.c deleted file mode 100644 index 9ac42e4..0000000 --- a/src/tor/dnsserv.c +++ /dev/null @@ -1,374 +0,0 @@ -/* Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dnsserv.c \brief Implements client-side DNS proxy server code. Note: - * this is the DNS Server code, not the Server DNS code. Confused? This code - * runs on client-side, and acts as a DNS server. The code in dns.c, on the - * other hand, runs on Tor servers, and acts as a DNS client. - **/ - -#include "or.h" -#include "dnsserv.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "control.h" -#include "onion_main.h" -#include "policies.h" -#ifdef HAVE_EVENT2_DNS_H -#include -#include -/* XXXX this implies we want an improved evdns */ -#include -#else -#include "eventdns.h" -#endif - -/** Helper function: called by evdns whenever the client sends a request to our - * DNSPort. We need to eventually answer the request req. - */ -static void -evdns_server_callback(struct evdns_server_request *req, void *data_) -{ - const listener_connection_t *listener = data_; - entry_connection_t *entry_conn; - edge_connection_t *conn; - int i = 0; - struct evdns_server_question *q = NULL; - struct sockaddr_storage addr; - struct sockaddr *sa; - int addrlen; - tor_addr_t tor_addr; - uint16_t port; - int err = DNS_ERR_NONE; - char *q_name; - - tor_assert(req); - - log_info(LD_APP, "Got a new DNS request!"); - - req->flags |= 0x80; /* set RA */ - - /* First, check whether the requesting address matches our SOCKSPolicy. */ - if ((addrlen = evdns_server_request_get_requesting_addr(req, - (struct sockaddr*)&addr, (socklen_t)sizeof(addr))) < 0) { - log_warn(LD_APP, "Couldn't get requesting address."); - evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - return; - } - (void) addrlen; - sa = (struct sockaddr*) &addr; - if (tor_addr_from_sockaddr(&tor_addr, sa, &port)<0) { - log_warn(LD_APP, "Requesting address wasn't recognized."); - evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - return; - } - - if (!socks_policy_permits_address(&tor_addr)) { - log_warn(LD_APP, "Rejecting DNS request from disallowed IP."); - evdns_server_request_respond(req, DNS_ERR_REFUSED); - return; - } - - /* Now, let's find the first actual question of a type we can answer in this - * DNS request. It makes us a little noncompliant to act like this; we - * should fix that eventually if it turns out to make a difference for - * anybody. */ - if (req->nquestions == 0) { - log_info(LD_APP, "No questions in DNS request; sending back nil reply."); - evdns_server_request_respond(req, 0); - return; - } - if (req->nquestions > 1) { - log_info(LD_APP, "Got a DNS request with more than one question; I only " - "handle one question at a time for now. Skipping the extras."); - } - for (i = 0; i < req->nquestions; ++i) { - if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET) - continue; - switch (req->questions[i]->type) { - case EVDNS_TYPE_A: - case EVDNS_TYPE_AAAA: - case EVDNS_TYPE_PTR: - q = req->questions[i]; - default: - break; - } - } - if (!q) { - log_info(LD_APP, "None of the questions we got were ones we're willing " - "to support. Sending NOTIMPL."); - evdns_server_request_respond(req, DNS_ERR_NOTIMPL); - return; - } - if (q->type != EVDNS_TYPE_A && q->type != EVDNS_TYPE_AAAA) { - tor_assert(q->type == EVDNS_TYPE_PTR); - } - - /* Make sure the name isn't too long: This should be impossible, I think. */ - if (err == DNS_ERR_NONE && strlen(q->name) > MAX_SOCKS_ADDR_LEN-1) - err = DNS_ERR_FORMAT; - - if (err != DNS_ERR_NONE) { - /* We got an error? Then send back an answer immediately; we're done. */ - evdns_server_request_respond(req, err); - return; - } - - /* Make a new dummy AP connection, and attach the request to it. */ - entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); - conn = ENTRY_TO_EDGE_CONN(entry_conn); - TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT; - conn->is_dns_request = 1; - - tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr); - TO_CONN(conn)->port = port; - TO_CONN(conn)->address = tor_dup_addr(&tor_addr); - - if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA) - entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE; - else - entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; - - strlcpy(entry_conn->socks_request->address, q->name, - sizeof(entry_conn->socks_request->address)); - - entry_conn->socks_request->listener_type = listener->base_.type; - entry_conn->dns_server_request = req; - entry_conn->isolation_flags = listener->isolation_flags; - entry_conn->session_group = listener->session_group; - entry_conn->nym_epoch = get_signewnym_epoch(); - - if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) { - log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); - evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - connection_free(ENTRY_TO_CONN(entry_conn)); - return; - } - - control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0); - - /* Now, unless a controller asked us to leave streams unattached, - * throw the connection over to get rewritten (which will - * answer it immediately if it's in the cache, or completely bogus, or - * automapped), and then attached to a circuit. */ - log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", - escaped_safe_str_client(q->name)); - q_name = tor_strdup(q->name); /* q could be freed in rewrite_and_attach */ - connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL); - /* Now, the connection is marked if it was bad. */ - - log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.", - escaped_safe_str_client(q_name)); - tor_free(q_name); -} - -/** Helper function: called whenever the client sends a resolve request to our - * controller. We need to eventually answer the request req. - * Returns 0 if the controller will be getting (or has gotten) an event in - * response; -1 if we couldn't launch the request. - */ -int -dnsserv_launch_request(const char *name, int reverse, - control_connection_t *control_conn) -{ - entry_connection_t *entry_conn; - edge_connection_t *conn; - char *q_name; - - /* Make a new dummy AP connection, and attach the request to it. */ - entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); - conn = ENTRY_TO_EDGE_CONN(entry_conn); - conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT; - - tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr); -#ifdef AF_UNIX - /* - * The control connection can be AF_UNIX and if so tor_dup_addr will - * unhelpfully say ""; say "(Tor_internal)" - * instead. - */ - if (control_conn->base_.socket_family == AF_UNIX) { - TO_CONN(conn)->port = 0; - TO_CONN(conn)->address = tor_strdup("(Tor_internal)"); - } else { - TO_CONN(conn)->port = control_conn->base_.port; - TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr); - } -#else - TO_CONN(conn)->port = control_conn->base_.port; - TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr); -#endif - - if (reverse) - entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; - else - entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE; - - conn->is_dns_request = 1; - - strlcpy(entry_conn->socks_request->address, name, - sizeof(entry_conn->socks_request->address)); - - entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER; - entry_conn->original_dest_address = tor_strdup(name); - entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE; - entry_conn->nym_epoch = get_signewnym_epoch(); - entry_conn->isolation_flags = ISO_DEFAULT; - - if (connection_add(TO_CONN(conn))<0) { - log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); - connection_free(TO_CONN(conn)); - return -1; - } - - control_event_stream_status(entry_conn, STREAM_EVENT_NEW_RESOLVE, 0); - - /* Now, unless a controller asked us to leave streams unattached, - * throw the connection over to get rewritten (which will - * answer it immediately if it's in the cache, or completely bogus, or - * automapped), and then attached to a circuit. */ - log_info(LD_APP, "Passing request for %s to rewrite_and_attach.", - escaped_safe_str_client(name)); - q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */ - connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL); - /* Now, the connection is marked if it was bad. */ - - log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.", - escaped_safe_str_client(q_name)); - tor_free(q_name); - return 0; -} - -/** If there is a pending request on conn that's waiting for an answer, - * send back an error and free the request. */ -void -dnsserv_reject_request(entry_connection_t *conn) -{ - if (conn->dns_server_request) { - evdns_server_request_respond(conn->dns_server_request, - DNS_ERR_SERVERFAILED); - conn->dns_server_request = NULL; - } -} - -/** Look up the original name that corresponds to 'addr' in req. We use this - * to preserve case in order to facilitate people using 0x20-hacks to avoid - * DNS poisoning. */ -static const char * -evdns_get_orig_address(const struct evdns_server_request *req, - int rtype, const char *addr) -{ - int i, type; - - switch (rtype) { - case RESOLVED_TYPE_IPV4: - type = EVDNS_TYPE_A; - break; - case RESOLVED_TYPE_HOSTNAME: - type = EVDNS_TYPE_PTR; - break; - case RESOLVED_TYPE_IPV6: - type = EVDNS_TYPE_AAAA; - break; - default: - tor_fragile_assert(); - return addr; - } - - for (i = 0; i < req->nquestions; ++i) { - const struct evdns_server_question *q = req->questions[i]; - if (q->type == type && !strcasecmp(q->name, addr)) - return q->name; - } - return addr; -} - -/** Tell the dns request waiting for an answer on conn that we have an - * answer of type answer_type (RESOLVE_TYPE_IPV4/IPV6/ERR), of length - * answer_len, in answer, with TTL ttl. Doesn't do - * any caching; that's handled elsewhere. */ -void -dnsserv_resolved(entry_connection_t *conn, - int answer_type, - size_t answer_len, - const char *answer, - int ttl) -{ - struct evdns_server_request *req = conn->dns_server_request; - const char *name; - int err = DNS_ERR_NONE; - if (!req) - return; - name = evdns_get_orig_address(req, answer_type, - conn->socks_request->address); - - /* XXXX Re-do; this is dumb. */ - if (ttl < 60) - ttl = 60; - - /* The evdns interface is: add a bunch of reply items (corresponding to one - * or more of the questions in the request); then, call - * evdns_server_request_respond. */ - if (answer_type == RESOLVED_TYPE_IPV6) { - evdns_server_request_add_aaaa_reply(req, - name, - 1, answer, ttl); - } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4 && - conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { - evdns_server_request_add_a_reply(req, - name, - 1, answer, ttl); - } else if (answer_type == RESOLVED_TYPE_HOSTNAME && - answer_len < 256 && - conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) { - char *ans = tor_strndup(answer, answer_len); - evdns_server_request_add_ptr_reply(req, NULL, - name, - ans, ttl); - tor_free(ans); - } else if (answer_type == RESOLVED_TYPE_ERROR) { - err = DNS_ERR_NOTEXIST; - } else { /* answer_type == RESOLVED_TYPE_ERROR_TRANSIENT */ - err = DNS_ERR_SERVERFAILED; - } - - evdns_server_request_respond(req, err); - - conn->dns_server_request = NULL; -} - -/** Set up the evdns server port for the UDP socket on conn, which - * must be an AP_DNS_LISTENER */ -void -dnsserv_configure_listener(connection_t *conn) -{ - listener_connection_t *listener_conn; - tor_assert(conn); - tor_assert(SOCKET_OK(conn->s)); - tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER); - - listener_conn = TO_LISTENER_CONN(conn); - listener_conn->dns_server_port = - tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, - listener_conn); -} - -/** Free the evdns server port for conn, which must be an - * AP_DNS_LISTENER. */ -void -dnsserv_close_listener(connection_t *conn) -{ - listener_connection_t *listener_conn; - tor_assert(conn); - tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER); - - listener_conn = TO_LISTENER_CONN(conn); - - if (listener_conn->dns_server_port) { - evdns_close_server_port(listener_conn->dns_server_port); - listener_conn->dns_server_port = NULL; - } -} - diff --git a/src/tor/dnsserv.h b/src/tor/dnsserv.h deleted file mode 100644 index 687a77e..0000000 --- a/src/tor/dnsserv.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file dnsserv.h - * \brief Header file for dnsserv.c. - **/ - -#ifndef TOR_DNSSERV_H -#define TOR_DNSSERV_H - -void dnsserv_configure_listener(connection_t *conn); -void dnsserv_close_listener(connection_t *conn); -void dnsserv_resolved(entry_connection_t *conn, - int answer_type, - size_t answer_len, - const char *answer, - int ttl); -void dnsserv_reject_request(entry_connection_t *conn); -int dnsserv_launch_request(const char *name, int is_reverse, - control_connection_t *control_conn); - -#endif - diff --git a/src/tor/entrynodes.c b/src/tor/entrynodes.c deleted file mode 100644 index 566896e..0000000 --- a/src/tor/entrynodes.c +++ /dev/null @@ -1,2334 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file entrynodes.c - * \brief Code to manage our fixed first nodes for various functions. - * - * Entry nodes can be guards (for general use) or bridges (for censorship - * circumvention). - **/ - -#include "or.h" -#include "circpathbias.h" -#include "circuitbuild.h" -#include "circuitstats.h" -#include "config.h" -#include "confparse.h" -#include "connection.h" -#include "connection_or.h" -#include "control.h" -#include "directory.h" -#include "entrynodes.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" -#include "routerset.h" -#include "transports.h" -#include "statefile.h" - -/** Information about a configured bridge. Currently this just matches the - * ones in the torrc file, but one day we may be able to learn about new - * bridges on our own, and remember them in the state file. */ -typedef struct { - /** Address of the bridge. */ - tor_addr_t addr; - /** TLS port for the bridge. */ - uint16_t port; - /** Boolean: We are re-parsing our bridge list, and we are going to remove - * this one if we don't find it in the list of configured bridges. */ - unsigned marked_for_removal : 1; - /** Expected identity digest, or all zero bytes if we don't know what the - * digest should be. */ - char identity[DIGEST_LEN]; - - /** Name of pluggable transport protocol taken from its config line. */ - char *transport_name; - - /** When should we next try to fetch a descriptor for this bridge? */ - download_status_t fetch_status; - - /** A smartlist of k=v values to be passed to the SOCKS proxy, if - transports are used for this bridge. */ - smartlist_t *socks_args; -} bridge_info_t; - -/** A list of our chosen entry guards. */ -static smartlist_t *entry_guards = NULL; -/** A value of 1 means that the entry_guards list has changed - * and those changes need to be flushed to disk. */ -static int entry_guards_dirty = 0; - -static void bridge_free(bridge_info_t *bridge); -static const node_t *choose_random_entry_impl(cpath_build_state_t *state, - int for_directory, - dirinfo_type_t dirtype); - -/** Return the list of entry guards, creating it if necessary. */ -const smartlist_t * -get_entry_guards(void) -{ - if (! entry_guards) - entry_guards = smartlist_new(); - return entry_guards; -} - -/** Check whether the entry guard e is usable, given the directory - * authorities' opinion about the router (stored in ri) and the user's - * configuration (in options). Set e->bad_since - * accordingly. Return true iff the entry guard's status changes. - * - * If it's not usable, set *reason to a static string explaining why. - */ -static int -entry_guard_set_status(entry_guard_t *e, const node_t *node, - time_t now, const or_options_t *options, - const char **reason) -{ - char buf[HEX_DIGEST_LEN+1]; - int changed = 0; - - *reason = NULL; - - /* Do we want to mark this guard as bad? */ - if (!node) - *reason = "unlisted"; - else if (!node->is_running) - *reason = "down"; - else if (options->UseBridges && (!node->ri || - node->ri->purpose != ROUTER_PURPOSE_BRIDGE)) - *reason = "not a bridge"; - else if (options->UseBridges && !node_is_a_configured_bridge(node)) - *reason = "not a configured bridge"; - else if (!options->UseBridges && !node->is_possible_guard && - !routerset_contains_node(options->EntryNodes,node)) - *reason = "not recommended as a guard"; - else if (routerset_contains_node(options->ExcludeNodes, node)) - *reason = "excluded"; - else if (e->path_bias_disabled) - *reason = "path-biased"; - - if (*reason && ! e->bad_since) { - /* Router is newly bad. */ - base16_encode(buf, sizeof(buf), e->identity, DIGEST_LEN); - log_info(LD_CIRC, "Entry guard %s (%s) is %s: marking as unusable.", - e->nickname, buf, *reason); - - e->bad_since = now; - control_event_guard(e->nickname, e->identity, "BAD"); - changed = 1; - } else if (!*reason && e->bad_since) { - /* There's nothing wrong with the router any more. */ - base16_encode(buf, sizeof(buf), e->identity, DIGEST_LEN); - log_info(LD_CIRC, "Entry guard %s (%s) is no longer unusable: " - "marking as ok.", e->nickname, buf); - - e->bad_since = 0; - control_event_guard(e->nickname, e->identity, "GOOD"); - changed = 1; - } - - if (node) { - int is_dir = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; - if (options->UseBridges && node_is_a_configured_bridge(node)) - is_dir = 1; - if (e->is_dir_cache != is_dir) { - e->is_dir_cache = is_dir; - changed = 1; - } - } - - return changed; -} - -/** Return true iff enough time has passed since we last tried to connect - * to the unreachable guard e that we're willing to try again. */ -static int -entry_is_time_to_retry(entry_guard_t *e, time_t now) -{ - long diff; - if (e->last_attempted < e->unreachable_since) - return 1; - diff = now - e->unreachable_since; - if (diff < 6*60*60) - return now > (e->last_attempted + 60*60); - else if (diff < 3*24*60*60) - return now > (e->last_attempted + 4*60*60); - else if (diff < 7*24*60*60) - return now > (e->last_attempted + 18*60*60); - else - return now > (e->last_attempted + 36*60*60); -} - -/** Return the node corresponding to e, if e is - * working well enough that we are willing to use it as an entry - * right now. (Else return NULL.) In particular, it must be - * - Listed as either up or never yet contacted; - * - Present in the routerlist; - * - Listed as 'stable' or 'fast' by the current dirserver consensus, - * if demanded by need_uptime or need_capacity - * (unless it's a configured EntryNode); - * - Allowed by our current ReachableORAddresses config option; and - * - Currently thought to be reachable by us (unless assume_reachable - * is true). - * - * If the answer is no, set *msg to an explanation of why. - * - * If need_descriptor is true, only return the node if we currently have - * a descriptor (routerinfo or microdesc) for it. - */ -static INLINE const node_t * -entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity, - int assume_reachable, int need_descriptor, const char **msg) -{ - const node_t *node; - const or_options_t *options = get_options(); - tor_assert(msg); - - if (e->path_bias_disabled) { - *msg = "path-biased"; - return NULL; - } - if (e->bad_since) { - *msg = "bad"; - return NULL; - } - /* no good if it's unreachable, unless assume_unreachable or can_retry. */ - if (!assume_reachable && !e->can_retry && - e->unreachable_since && !entry_is_time_to_retry(e, time(NULL))) { - *msg = "unreachable"; - return NULL; - } - node = node_get_by_id(e->identity); - if (!node) { - *msg = "no node info"; - return NULL; - } - if (need_descriptor && !node_has_descriptor(node)) { - *msg = "no descriptor"; - return NULL; - } - if (get_options()->UseBridges) { - if (node_get_purpose(node) != ROUTER_PURPOSE_BRIDGE) { - *msg = "not a bridge"; - return NULL; - } - if (!node_is_a_configured_bridge(node)) { - *msg = "not a configured bridge"; - return NULL; - } - } else { /* !get_options()->UseBridges */ - if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) { - *msg = "not general-purpose"; - return NULL; - } - } - if (routerset_contains_node(options->EntryNodes, node)) { - /* they asked for it, they get it */ - need_uptime = need_capacity = 0; - } - if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { - *msg = "not fast/stable"; - return NULL; - } - if (!fascist_firewall_allows_node(node)) { - *msg = "unreachable by config"; - return NULL; - } - return node; -} - -/** Return the number of entry guards that we think are usable. */ -int -num_live_entry_guards(int for_directory) -{ - int n = 0; - const char *msg; - if (! entry_guards) - return 0; - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - if (for_directory && !entry->is_dir_cache) - continue; - if (entry_is_live(entry, 0, 1, 0, !for_directory, &msg)) - ++n; - } SMARTLIST_FOREACH_END(entry); - return n; -} - -/** If digest matches the identity of any node in the - * entry_guards list, return that node. Else return NULL. */ -entry_guard_t * -entry_guard_get_by_id_digest(const char *digest) -{ - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry, - if (tor_memeq(digest, entry->identity, DIGEST_LEN)) - return entry; - ); - return NULL; -} - -/** Dump a description of our list of entry guards to the log at level - * severity. */ -static void -log_entry_guards(int severity) -{ - smartlist_t *elements = smartlist_new(); - char *s; - - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) - { - const char *msg = NULL; - if (entry_is_live(e, 0, 1, 0, 0, &msg)) - smartlist_add_asprintf(elements, "%s [%s] (up %s)", - e->nickname, - hex_str(e->identity, DIGEST_LEN), - e->made_contact ? "made-contact" : "never-contacted"); - else - smartlist_add_asprintf(elements, "%s [%s] (%s, %s)", - e->nickname, - hex_str(e->identity, DIGEST_LEN), - msg, - e->made_contact ? "made-contact" : "never-contacted"); - } - SMARTLIST_FOREACH_END(e); - - s = smartlist_join_strings(elements, ",", 0, NULL); - SMARTLIST_FOREACH(elements, char*, cp, tor_free(cp)); - smartlist_free(elements); - log_fn(severity,LD_CIRC,"%s",s); - tor_free(s); -} - -/** Called when one or more guards that we would previously have used for some - * purpose are no longer in use because a higher-priority guard has become - * usable again. */ -static void -control_event_guard_deferred(void) -{ - /* XXXX We don't actually have a good way to figure out _how many_ entries - * are live for some purpose. We need an entry_is_even_slightly_live() - * function for this to work right. NumEntryGuards isn't reliable: if we - * need guards with weird properties, we can have more than that number - * live. - **/ -#if 0 - int n = 0; - const char *msg; - const or_options_t *options = get_options(); - if (!entry_guards) - return; - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry, - { - if (entry_is_live(entry, 0, 1, 0, &msg)) { - if (n++ == options->NumEntryGuards) { - control_event_guard(entry->nickname, entry->identity, "DEFERRED"); - return; - } - } - }); -#endif -} - -/** Largest amount that we'll backdate chosen_on_date */ -#define CHOSEN_ON_DATE_SLOP (30*86400) - -/** Add a new (preferably stable and fast) router to our - * entry_guards list. Return a pointer to the router if we succeed, - * or NULL if we can't find any more suitable entries. - * - * If chosen is defined, use that one, and if it's not - * already in our entry_guards list, put it at the *beginning*. - * Else, put the one we pick at the end of the list. */ -static const node_t * -add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, - int for_discovery, int for_directory) -{ - const node_t *node; - entry_guard_t *entry; - - if (chosen) { - node = chosen; - entry = entry_guard_get_by_id_digest(node->identity); - if (entry) { - if (reset_status) { - entry->bad_since = 0; - entry->can_retry = 1; - } - entry->is_dir_cache = node->rs && - node->rs->version_supports_microdesc_cache; - if (get_options()->UseBridges && node_is_a_configured_bridge(node)) - entry->is_dir_cache = 1; - return NULL; - } - } else if (!for_directory) { - node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL); - if (!node) - return NULL; - } else { - const routerstatus_t *rs; - rs = router_pick_directory_server(MICRODESC_DIRINFO|V3_DIRINFO, - PDS_PREFER_TUNNELED_DIR_CONNS_|PDS_FOR_GUARD); - if (!rs) - return NULL; - node = node_get_by_id(rs->identity_digest); - if (!node) - return NULL; - } - if (node->using_as_guard) - return NULL; - if (entry_guard_get_by_id_digest(node->identity) != NULL) { - log_info(LD_CIRC, "I was about to add a duplicate entry guard."); - /* This can happen if we choose a guard, then the node goes away, then - * comes back. */ - ((node_t*) node)->using_as_guard = 1; - return NULL; - } - entry = tor_malloc_zero(sizeof(entry_guard_t)); - log_info(LD_CIRC, "Chose %s as new entry guard.", - node_describe(node)); - strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname)); - memcpy(entry->identity, node->identity, DIGEST_LEN); - entry->is_dir_cache = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; - if (get_options()->UseBridges && node_is_a_configured_bridge(node)) - entry->is_dir_cache = 1; - - /* Choose expiry time smudged over the past month. The goal here - * is to a) spread out when Tor clients rotate their guards, so they - * don't all select them on the same day, and b) avoid leaving a - * precise timestamp in the state file about when we first picked - * this guard. For details, see the Jan 2010 or-dev thread. */ - entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); - entry->chosen_by_version = tor_strdup(VERSION); - - /* Are we picking this guard because all of our current guards are - * down so we need another one (for_discovery is 1), or because we - * decided we need more variety in our guard list (for_discovery is 0)? - * - * Currently we hack this behavior into place by setting "made_contact" - * for guards of the latter variety, so we'll be willing to use any of - * them right off the bat. - */ - if (!for_discovery) - entry->made_contact = 1; - - ((node_t*)node)->using_as_guard = 1; - if (prepend) - smartlist_insert(entry_guards, 0, entry); - else - smartlist_add(entry_guards, entry); - control_event_guard(entry->nickname, entry->identity, "NEW"); - control_event_guard_deferred(); - log_entry_guards(LOG_INFO); - return node; -} - -/** Choose how many entry guards or directory guards we'll use. If - * for_directory is true, we return how many directory guards to - * use; else we return how many entry guards to use. */ -static int -decide_num_guards(const or_options_t *options, int for_directory) -{ - if (for_directory && options->NumDirectoryGuards != 0) - return options->NumDirectoryGuards; - return options->NumEntryGuards; -} - -/** If the use of entry guards is configured, choose more entry guards - * until we have enough in the list. */ -static void -pick_entry_guards(const or_options_t *options, int for_directory) -{ - int changed = 0; - const int num_needed = decide_num_guards(options, for_directory); - - tor_assert(entry_guards); - - while (num_live_entry_guards(for_directory) < num_needed) { - if (!add_an_entry_guard(NULL, 0, 0, 0, for_directory)) - break; - changed = 1; - } - if (changed) - entry_guards_changed(); -} - -/** How long (in seconds) do we allow an entry guard to be nonfunctional, - * unlisted, excluded, or otherwise nonusable before we give up on it? */ -#define ENTRY_GUARD_REMOVE_AFTER (30*24*60*60) - -/** Release all storage held by e. */ -static void -entry_guard_free(entry_guard_t *e) -{ - if (!e) - return; - tor_free(e->chosen_by_version); - tor_free(e); -} - -/** - * Return the minimum lifetime of working entry guard, in seconds, - * as given in the consensus networkstatus. (Plus CHOSEN_ON_DATE_SLOP, - * so that we can do the chosen_on_date randomization while achieving the - * desired minimum lifetime.) - */ -static int32_t -guards_get_lifetime(void) -{ - const or_options_t *options = get_options(); -#define DFLT_GUARD_LIFETIME (86400 * 60) /* Two months. */ -#define MIN_GUARD_LIFETIME (86400 * 30) /* One months. */ -#define MAX_GUARD_LIFETIME (86400 * 1826) /* Five years. */ - - if (options->GuardLifetime >= 1) { - return CLAMP(MIN_GUARD_LIFETIME, - options->GuardLifetime, - MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; - } - - return networkstatus_get_param(NULL, "GuardLifetime", - DFLT_GUARD_LIFETIME, - MIN_GUARD_LIFETIME, - MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; -} - -/** Remove any entry guard which was selected by an unknown version of Tor, - * or which was selected by a version of Tor that's known to select - * entry guards badly, or which was selected more 2 months ago. */ -/* XXXX The "obsolete guards" and "chosen long ago guards" things should - * probably be different functions. */ -static int -remove_obsolete_entry_guards(time_t now) -{ - int changed = 0, i; - int32_t guard_lifetime = guards_get_lifetime(); - - for (i = 0; i < smartlist_len(entry_guards); ++i) { - entry_guard_t *entry = smartlist_get(entry_guards, i); - const char *ver = entry->chosen_by_version; - const char *msg = NULL; - tor_version_t v; - int version_is_bad = 0, date_is_bad = 0; - if (!ver) { - msg = "does not say what version of Tor it was selected by"; - version_is_bad = 1; - } else if (tor_version_parse(ver, &v)) { - msg = "does not seem to be from any recognized version of Tor"; - version_is_bad = 1; - } else { - char *tor_ver = NULL; - tor_asprintf(&tor_ver, "Tor %s", ver); - if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") && - !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) || - (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.0.6-alpha")) || - /* above are bug 440; below are bug 1217 */ - (tor_version_as_new_as(tor_ver, "0.2.1.3-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.1.23")) || - (tor_version_as_new_as(tor_ver, "0.2.2.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.2.7-alpha"))) { - msg = "was selected without regard for guard bandwidth"; - version_is_bad = 1; - } - tor_free(tor_ver); - } - if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) { - /* It's been too long since the date listed in our state file. */ - msg = "was selected several months ago"; - date_is_bad = 1; - } - - if (version_is_bad || date_is_bad) { /* we need to drop it */ - char dbuf[HEX_DIGEST_LEN+1]; - tor_assert(msg); - base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); - log_fn(version_is_bad ? LOG_NOTICE : LOG_INFO, LD_CIRC, - "Entry guard '%s' (%s) %s. (Version=%s.) Replacing it.", - entry->nickname, dbuf, msg, ver?escaped(ver):"none"); - control_event_guard(entry->nickname, entry->identity, "DROPPED"); - entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, i--); - log_entry_guards(LOG_INFO); - changed = 1; - } - } - - return changed ? 1 : 0; -} - -/** Remove all entry guards that have been down or unlisted for so - * long that we don't think they'll come up again. Return 1 if we - * removed any, or 0 if we did nothing. */ -static int -remove_dead_entry_guards(time_t now) -{ - char dbuf[HEX_DIGEST_LEN+1]; - char tbuf[ISO_TIME_LEN+1]; - int i; - int changed = 0; - - for (i = 0; i < smartlist_len(entry_guards); ) { - entry_guard_t *entry = smartlist_get(entry_guards, i); - if (entry->bad_since && - ! entry->path_bias_disabled && - entry->bad_since + ENTRY_GUARD_REMOVE_AFTER < now) { - - base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); - format_local_iso_time(tbuf, entry->bad_since); - log_info(LD_CIRC, "Entry guard '%s' (%s) has been down or unlisted " - "since %s local time; removing.", - entry->nickname, dbuf, tbuf); - control_event_guard(entry->nickname, entry->identity, "DROPPED"); - entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, i); - log_entry_guards(LOG_INFO); - changed = 1; - } else - ++i; - } - return changed ? 1 : 0; -} - -/** Remove all currently listed entry guards. So new ones will be chosen. */ -void -remove_all_entry_guards(void) -{ - char dbuf[HEX_DIGEST_LEN+1]; - - while (smartlist_len(entry_guards)) { - entry_guard_t *entry = smartlist_get(entry_guards, 0); - base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); - log_info(LD_CIRC, "Entry guard '%s' (%s) has been dropped.", - entry->nickname, dbuf); - control_event_guard(entry->nickname, entry->identity, "DROPPED"); - entry_guard_free(entry); - smartlist_del(entry_guards, 0); - } - log_entry_guards(LOG_INFO); - entry_guards_changed(); -} - -/** A new directory or router-status has arrived; update the down/listed - * status of the entry guards. - * - * An entry is 'down' if the directory lists it as nonrunning. - * An entry is 'unlisted' if the directory doesn't include it. - * - * Don't call this on startup; only on a fresh download. Otherwise we'll - * think that things are unlisted. - */ -void -entry_guards_compute_status(const or_options_t *options, time_t now) -{ - int changed = 0; - digestmap_t *reasons; - - if (! entry_guards) - return; - - if (options->EntryNodes) /* reshuffle the entry guard list if needed */ - entry_nodes_should_be_added(); - - reasons = digestmap_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) - { - const node_t *r = node_get_by_id(entry->identity); - const char *reason = NULL; - if (entry_guard_set_status(entry, r, now, options, &reason)) - changed = 1; - - if (entry->bad_since) - tor_assert(reason); - if (reason) - digestmap_set(reasons, entry->identity, (char*)reason); - } - SMARTLIST_FOREACH_END(entry); - - if (remove_dead_entry_guards(now)) - changed = 1; - if (remove_obsolete_entry_guards(now)) - changed = 1; - - if (changed) { - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - const char *reason = digestmap_get(reasons, entry->identity); - const char *live_msg = ""; - const node_t *r = entry_is_live(entry, 0, 1, 0, 0, &live_msg); - log_info(LD_CIRC, "Summary: Entry %s [%s] is %s, %s%s%s, and %s%s.", - entry->nickname, - hex_str(entry->identity, DIGEST_LEN), - entry->unreachable_since ? "unreachable" : "reachable", - entry->bad_since ? "unusable" : "usable", - reason ? ", ": "", - reason ? reason : "", - r ? "live" : "not live / ", - r ? "" : live_msg); - } SMARTLIST_FOREACH_END(entry); - log_info(LD_CIRC, " (%d/%d entry guards are usable/new)", - num_live_entry_guards(0), smartlist_len(entry_guards)); - log_entry_guards(LOG_INFO); - entry_guards_changed(); - } - - digestmap_free(reasons, NULL); -} - -/** Called when a connection to an OR with the identity digest digest - * is established (succeeded==1) or has failed (succeeded==0). - * If the OR is an entry, change that entry's up/down status. - * Return 0 normally, or -1 if we want to tear down the new connection. - * - * If mark_relay_status, also call router_set_status() on this - * relay. - * - * XXX024 change succeeded and mark_relay_status into 'int flags'. - */ -int -entry_guard_register_connect_status(const char *digest, int succeeded, - int mark_relay_status, time_t now) -{ - int changed = 0; - int refuse_conn = 0; - int first_contact = 0; - entry_guard_t *entry = NULL; - int idx = -1; - char buf[HEX_DIGEST_LEN+1]; - - if (! entry_guards) - return 0; - - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - tor_assert(e); - if (tor_memeq(e->identity, digest, DIGEST_LEN)) { - entry = e; - idx = e_sl_idx; - break; - } - } SMARTLIST_FOREACH_END(e); - - if (!entry) - return 0; - - base16_encode(buf, sizeof(buf), entry->identity, DIGEST_LEN); - - if (succeeded) { - if (entry->unreachable_since) { - log_info(LD_CIRC, "Entry guard '%s' (%s) is now reachable again. Good.", - entry->nickname, buf); - entry->can_retry = 0; - entry->unreachable_since = 0; - entry->last_attempted = now; - control_event_guard(entry->nickname, entry->identity, "UP"); - changed = 1; - } - if (!entry->made_contact) { - entry->made_contact = 1; - first_contact = changed = 1; - } - } else { /* ! succeeded */ - if (!entry->made_contact) { - /* We've never connected to this one. */ - log_info(LD_CIRC, - "Connection to never-contacted entry guard '%s' (%s) failed. " - "Removing from the list. %d/%d entry guards usable/new.", - entry->nickname, buf, - num_live_entry_guards(0)-1, smartlist_len(entry_guards)-1); - control_event_guard(entry->nickname, entry->identity, "DROPPED"); - entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, idx); - log_entry_guards(LOG_INFO); - changed = 1; - } else if (!entry->unreachable_since) { - log_info(LD_CIRC, "Unable to connect to entry guard '%s' (%s). " - "Marking as unreachable.", entry->nickname, buf); - entry->unreachable_since = entry->last_attempted = now; - control_event_guard(entry->nickname, entry->identity, "DOWN"); - changed = 1; - entry->can_retry = 0; /* We gave it an early chance; no good. */ - } else { - char tbuf[ISO_TIME_LEN+1]; - format_iso_time(tbuf, entry->unreachable_since); - log_debug(LD_CIRC, "Failed to connect to unreachable entry guard " - "'%s' (%s). It has been unreachable since %s.", - entry->nickname, buf, tbuf); - entry->last_attempted = now; - entry->can_retry = 0; /* We gave it an early chance; no good. */ - } - } - - /* if the caller asked us to, also update the is_running flags for this - * relay */ - if (mark_relay_status) - router_set_status(digest, succeeded); - - if (first_contact) { - /* We've just added a new long-term entry guard. Perhaps the network just - * came back? We should give our earlier entries another try too, - * and close this connection so we don't use it before we've given - * the others a shot. */ - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - if (e == entry) - break; - if (e->made_contact) { - const char *msg; - const node_t *r = entry_is_live(e, 0, 1, 1, 0, &msg); - if (r && e->unreachable_since) { - refuse_conn = 1; - e->can_retry = 1; - } - } - } SMARTLIST_FOREACH_END(e); - if (refuse_conn) { - log_info(LD_CIRC, - "Connected to new entry guard '%s' (%s). Marking earlier " - "entry guards up. %d/%d entry guards usable/new.", - entry->nickname, buf, - num_live_entry_guards(0), smartlist_len(entry_guards)); - log_entry_guards(LOG_INFO); - changed = 1; - } - } - - if (changed) - entry_guards_changed(); - return refuse_conn ? -1 : 0; -} - -/** When we try to choose an entry guard, should we parse and add - * config's EntryNodes first? */ -static int should_add_entry_nodes = 0; - -/** Called when the value of EntryNodes changes in our configuration. */ -void -entry_nodes_should_be_added(void) -{ - log_info(LD_CIRC, "EntryNodes config option set. Putting configured " - "relays at the front of the entry guard list."); - should_add_entry_nodes = 1; -} - -/** Update the using_as_guard fields of all the nodes. We do this after we - * remove entry guards from the list: This is the only function that clears - * the using_as_guard field. */ -static void -update_node_guard_status(void) -{ - smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, node_t *, node, node->using_as_guard = 0); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - node_t *node = node_get_mutable_by_id(entry->identity); - if (node) - node->using_as_guard = 1; - } SMARTLIST_FOREACH_END(entry); -} - -/** Adjust the entry guards list so that it only contains entries from - * EntryNodes, adding new entries from EntryNodes to the list as needed. */ -static void -entry_guards_set_from_config(const or_options_t *options) -{ - smartlist_t *entry_nodes, *worse_entry_nodes, *entry_fps; - smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list; - tor_assert(entry_guards); - - should_add_entry_nodes = 0; - - if (!options->EntryNodes) { - /* It's possible that a controller set EntryNodes, thus making - * should_add_entry_nodes set, then cleared it again, all before the - * call to choose_random_entry() that triggered us. If so, just return. - */ - return; - } - - { - char *string = routerset_to_string(options->EntryNodes); - log_info(LD_CIRC,"Adding configured EntryNodes '%s'.", string); - tor_free(string); - } - - entry_nodes = smartlist_new(); - worse_entry_nodes = smartlist_new(); - entry_fps = smartlist_new(); - old_entry_guards_on_list = smartlist_new(); - old_entry_guards_not_on_list = smartlist_new(); - - /* Split entry guards into those on the list and those not. */ - - routerset_get_all_nodes(entry_nodes, options->EntryNodes, - options->ExcludeNodes, 0); - SMARTLIST_FOREACH(entry_nodes, const node_t *,node, - smartlist_add(entry_fps, (void*)node->identity)); - - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { - if (smartlist_contains_digest(entry_fps, e->identity)) - smartlist_add(old_entry_guards_on_list, e); - else - smartlist_add(old_entry_guards_not_on_list, e); - }); - - /* Remove all currently configured guard nodes, excluded nodes, unreachable - * nodes, or non-Guard nodes from entry_nodes. */ - SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { - if (entry_guard_get_by_id_digest(node->identity)) { - SMARTLIST_DEL_CURRENT(entry_nodes, node); - continue; - } else if (routerset_contains_node(options->ExcludeNodes, node)) { - SMARTLIST_DEL_CURRENT(entry_nodes, node); - continue; - } else if (!fascist_firewall_allows_node(node)) { - SMARTLIST_DEL_CURRENT(entry_nodes, node); - continue; - } else if (! node->is_possible_guard) { - smartlist_add(worse_entry_nodes, (node_t*)node); - SMARTLIST_DEL_CURRENT(entry_nodes, node); - } - } SMARTLIST_FOREACH_END(node); - - /* Now build the new entry_guards list. */ - smartlist_clear(entry_guards); - /* First, the previously configured guards that are in EntryNodes. */ - smartlist_add_all(entry_guards, old_entry_guards_on_list); - /* Next, scramble the rest of EntryNodes, putting the guards first. */ - smartlist_shuffle(entry_nodes); - smartlist_shuffle(worse_entry_nodes); - smartlist_add_all(entry_nodes, worse_entry_nodes); - - /* Next, the rest of EntryNodes */ - SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { - add_an_entry_guard(node, 0, 0, 1, 0); - if (smartlist_len(entry_guards) > options->NumEntryGuards * 10) - break; - } SMARTLIST_FOREACH_END(node); - log_notice(LD_GENERAL, "%d entries in guards", smartlist_len(entry_guards)); - /* Finally, free the remaining previously configured guards that are not in - * EntryNodes. */ - SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e, - entry_guard_free(e)); - - update_node_guard_status(); - - smartlist_free(entry_nodes); - smartlist_free(worse_entry_nodes); - smartlist_free(entry_fps); - smartlist_free(old_entry_guards_on_list); - smartlist_free(old_entry_guards_not_on_list); - entry_guards_changed(); -} - -/** Return 0 if we're fine adding arbitrary routers out of the - * directory to our entry guard list, or return 1 if we have a - * list already and we must stick to it. - */ -int -entry_list_is_constrained(const or_options_t *options) -{ - if (options->EntryNodes) - return 1; - if (options->UseBridges) - return 1; - return 0; -} - -/** Return true iff this node can answer directory questions about - * microdescriptors. */ -static int -node_understands_microdescriptors(const node_t *node) -{ - tor_assert(node); - if (node->rs && node->rs->version_supports_microdesc_cache) - return 1; - if (node->ri && tor_version_supports_microdescriptors(node->ri->platform)) - return 1; - return 0; -} - -/** Return true iff node is able to answer directory questions - * of type dirinfo. */ -static int -node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo) -{ - /* Checking dirinfo for any type other than microdescriptors isn't required - yet, since we only choose directory guards that can support microdescs, - routerinfos, and networkstatuses, AND we don't use directory guards if - we're configured to do direct downloads of anything else. The only case - where we might have a guard that doesn't know about a type of directory - information is when we're retrieving directory information from a - bridge. */ - - if ((dirinfo & MICRODESC_DIRINFO) && - !node_understands_microdescriptors(node)) - return 0; - return 1; -} - -/** Pick a live (up and listed) entry guard from entry_guards. If - * state is non-NULL, this is for a specific circuit -- - * make sure not to pick this circuit's exit or any node in the - * exit's family. If state is NULL, we're looking for a random - * guard (likely a bridge). If dirinfo is not NO_DIRINFO, then - * only select from nodes that know how to answer directory questions - * of that type. */ -const node_t * -choose_random_entry(cpath_build_state_t *state) -{ - return choose_random_entry_impl(state, 0, 0); -} - -/** Pick a live (up and listed) directory guard from entry_guards for - * downloading information of type type. */ -const node_t * -choose_random_dirguard(dirinfo_type_t type) -{ - return choose_random_entry_impl(NULL, 1, type); -} - -/** Helper for choose_random{entry,dirguard}. */ -static const node_t * -choose_random_entry_impl(cpath_build_state_t *state, int for_directory, - dirinfo_type_t dirinfo_type) -{ - const or_options_t *options = get_options(); - smartlist_t *live_entry_guards = smartlist_new(); - smartlist_t *exit_family = smartlist_new(); - const node_t *chosen_exit = - state?build_state_get_exit_node(state) : NULL; - const node_t *node = NULL; - int need_uptime = state ? state->need_uptime : 0; - int need_capacity = state ? state->need_capacity : 0; - int preferred_min, consider_exit_family = 0; - int need_descriptor = !for_directory; - const int num_needed = decide_num_guards(options, for_directory); - - if (chosen_exit) { - nodelist_add_node_and_family(exit_family, chosen_exit); - consider_exit_family = 1; - } - - if (!entry_guards) - entry_guards = smartlist_new(); - - if (should_add_entry_nodes) - entry_guards_set_from_config(options); - - if (!entry_list_is_constrained(options) && - smartlist_len(entry_guards) < num_needed) - pick_entry_guards(options, for_directory); - - retry: - smartlist_clear(live_entry_guards); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - const char *msg; - node = entry_is_live(entry, need_uptime, need_capacity, 0, - need_descriptor, &msg); - if (!node) - continue; /* down, no point */ - if (for_directory) { - if (!entry->is_dir_cache) - continue; /* We need a directory and didn't get one. */ - } - if (node == chosen_exit) - continue; /* don't pick the same node for entry and exit */ - if (consider_exit_family && smartlist_contains(exit_family, node)) - continue; /* avoid relays that are family members of our exit */ - if (dirinfo_type != NO_DIRINFO && - !node_can_handle_dirinfo(node, dirinfo_type)) - continue; /* this node won't be able to answer our dir questions */ -#if 0 /* since EntryNodes is always strict now, this clause is moot */ - if (options->EntryNodes && - !routerset_contains_node(options->EntryNodes, node)) { - /* We've come to the end of our preferred entry nodes. */ - if (smartlist_len(live_entry_guards)) - goto choose_and_finish; /* only choose from the ones we like */ - if (options->StrictNodes) { - /* in theory this case should never happen, since - * entry_guards_set_from_config() drops unwanted relays */ - tor_fragile_assert(); - } else { - log_info(LD_CIRC, - "No relays from EntryNodes available. Using others."); - } - } -#endif - smartlist_add(live_entry_guards, (void*)node); - if (!entry->made_contact) { - /* Always start with the first not-yet-contacted entry - * guard. Otherwise we might add several new ones, pick - * the second new one, and now we've expanded our entry - * guard list without needing to. */ - goto choose_and_finish; - } - if (smartlist_len(live_entry_guards) >= num_needed) - goto choose_and_finish; /* we have enough */ - } SMARTLIST_FOREACH_END(entry); - - if (entry_list_is_constrained(options)) { - /* If we prefer the entry nodes we've got, and we have at least - * one choice, that's great. Use it. */ - preferred_min = 1; - } else { - /* Try to have at least 2 choices available. This way we don't - * get stuck with a single live-but-crummy entry and just keep - * using him. - * (We might get 2 live-but-crummy entry guards, but so be it.) */ - preferred_min = 2; - } - - if (smartlist_len(live_entry_guards) < preferred_min) { - if (!entry_list_is_constrained(options)) { - /* still no? try adding a new entry then */ - /* XXX if guard doesn't imply fast and stable, then we need - * to tell add_an_entry_guard below what we want, or it might - * be a long time til we get it. -RD */ - node = add_an_entry_guard(NULL, 0, 0, 1, for_directory); - if (node) { - entry_guards_changed(); - /* XXX we start over here in case the new node we added shares - * a family with our exit node. There's a chance that we'll just - * load up on entry guards here, if the network we're using is - * one big family. Perhaps we should teach add_an_entry_guard() - * to understand nodes-to-avoid-if-possible? -RD */ - goto retry; - } - } - if (!node && need_uptime) { - need_uptime = 0; /* try without that requirement */ - goto retry; - } - if (!node && need_capacity) { - /* still no? last attempt, try without requiring capacity */ - need_capacity = 0; - goto retry; - } -#if 0 - /* Removing this retry logic: if we only allow one exit, and it is in the - same family as all our entries, then we are just plain not going to win - here. */ - if (!node && entry_list_is_constrained(options) && consider_exit_family) { - /* still no? if we're using bridges or have strictentrynodes - * set, and our chosen exit is in the same family as all our - * bridges/entry guards, then be flexible about families. */ - consider_exit_family = 0; - goto retry; - } -#endif - /* live_entry_guards may be empty below. Oh well, we tried. */ - } - - choose_and_finish: - if (entry_list_is_constrained(options)) { - /* We need to weight by bandwidth, because our bridges or entryguards - * were not already selected proportional to their bandwidth. */ - node = node_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD); - } else { - /* We choose uniformly at random here, because choose_good_entry_server() - * already weights its choices by bandwidth, so we don't want to - * *double*-weight our guard selection. */ - node = smartlist_choose(live_entry_guards); - } - smartlist_free(live_entry_guards); - smartlist_free(exit_family); - return node; -} - -/** Parse state and learn about the entry guards it describes. - * If set is true, and there are no errors, replace the global - * entry_list with what we find. - * On success, return 0. On failure, alloc into *msg a string - * describing the error, and return -1. - */ -int -entry_guards_parse_state(or_state_t *state, int set, char **msg) -{ - entry_guard_t *node = NULL; - smartlist_t *new_entry_guards = smartlist_new(); - config_line_t *line; - time_t now = time(NULL); - const char *state_version = state->TorVersion; - digestmap_t *added_by = digestmap_new(); - - *msg = NULL; - for (line = state->EntryGuards; line; line = line->next) { - if (!strcasecmp(line->key, "EntryGuard")) { - smartlist_t *args = smartlist_new(); - node = tor_malloc_zero(sizeof(entry_guard_t)); - /* all entry guards on disk have been contacted */ - node->made_contact = 1; - smartlist_add(new_entry_guards, node); - smartlist_split_string(args, line->value, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (smartlist_len(args)<2) { - *msg = tor_strdup("Unable to parse entry nodes: " - "Too few arguments to EntryGuard"); - } else if (!is_legal_nickname(smartlist_get(args,0))) { - *msg = tor_strdup("Unable to parse entry nodes: " - "Bad nickname for EntryGuard"); - } else { - strlcpy(node->nickname, smartlist_get(args,0), MAX_NICKNAME_LEN+1); - if (base16_decode(node->identity, DIGEST_LEN, smartlist_get(args,1), - strlen(smartlist_get(args,1)))<0) { - *msg = tor_strdup("Unable to parse entry nodes: " - "Bad hex digest for EntryGuard"); - } - } - if (smartlist_len(args) >= 3) { - const char *is_cache = smartlist_get(args, 2); - if (!strcasecmp(is_cache, "DirCache")) { - node->is_dir_cache = 1; - } else if (!strcasecmp(is_cache, "NoDirCache")) { - node->is_dir_cache = 0; - } else { - log_warn(LD_CONFIG, "Bogus third argument to EntryGuard line: %s", - escaped(is_cache)); - } - } - SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); - smartlist_free(args); - if (*msg) - break; - } else if (!strcasecmp(line->key, "EntryGuardDownSince") || - !strcasecmp(line->key, "EntryGuardUnlistedSince")) { - time_t when; - time_t last_try = 0; - if (!node) { - *msg = tor_strdup("Unable to parse entry nodes: " - "EntryGuardDownSince/UnlistedSince without EntryGuard"); - break; - } - if (parse_iso_time(line->value, &when)<0) { - *msg = tor_strdup("Unable to parse entry nodes: " - "Bad time in EntryGuardDownSince/UnlistedSince"); - break; - } - if (when > now) { - /* It's a bad idea to believe info in the future: you can wind - * up with timeouts that aren't allowed to happen for years. */ - continue; - } - if (strlen(line->value) >= ISO_TIME_LEN+ISO_TIME_LEN+1) { - /* ignore failure */ - (void) parse_iso_time(line->value+ISO_TIME_LEN+1, &last_try); - } - if (!strcasecmp(line->key, "EntryGuardDownSince")) { - node->unreachable_since = when; - node->last_attempted = last_try; - } else { - node->bad_since = when; - } - } else if (!strcasecmp(line->key, "EntryGuardAddedBy")) { - char d[DIGEST_LEN]; - /* format is digest version date */ - if (strlen(line->value) < HEX_DIGEST_LEN+1+1+1+ISO_TIME_LEN) { - log_warn(LD_BUG, "EntryGuardAddedBy line is not long enough."); - continue; - } - if (base16_decode(d, sizeof(d), line->value, HEX_DIGEST_LEN)<0 || - line->value[HEX_DIGEST_LEN] != ' ') { - log_warn(LD_BUG, "EntryGuardAddedBy line %s does not begin with " - "hex digest", escaped(line->value)); - continue; - } - digestmap_set(added_by, d, tor_strdup(line->value+HEX_DIGEST_LEN+1)); - } else if (!strcasecmp(line->key, "EntryGuardPathUseBias")) { - const or_options_t *options = get_options(); - double use_cnt, success_cnt; - - if (!node) { - *msg = tor_strdup("Unable to parse entry nodes: " - "EntryGuardPathUseBias without EntryGuard"); - break; - } - - if (tor_sscanf(line->value, "%lf %lf", - &use_cnt, &success_cnt) != 2) { - log_info(LD_GENERAL, "Malformed path use bias line for node %s", - node->nickname); - continue; - } - - if (use_cnt < success_cnt) { - int severity = LOG_INFO; - /* If this state file was written by a Tor that would have - * already fixed it, then the overcounting bug is still there.. */ - if (tor_version_as_new_as(state_version, "0.2.4.13-alpha")) { - severity = LOG_NOTICE; - } - log_fn(severity, LD_BUG, - "State file contains unexpectedly high usage success " - "counts %lf/%lf for Guard %s ($%s)", - success_cnt, use_cnt, - node->nickname, hex_str(node->identity, DIGEST_LEN)); - success_cnt = use_cnt; - } - - node->use_attempts = use_cnt; - node->use_successes = success_cnt; - - log_info(LD_GENERAL, "Read %f/%f path use bias for node %s", - node->use_successes, node->use_attempts, node->nickname); - - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_use_success_count(node)/node->use_attempts - < pathbias_get_extreme_use_rate(options) && - pathbias_get_dropguards(options)) { - node->path_bias_disabled = 1; - log_info(LD_GENERAL, - "Path use bias is too high (%f/%f); disabling node %s", - node->circ_successes, node->circ_attempts, node->nickname); - } - } else if (!strcasecmp(line->key, "EntryGuardPathBias")) { - const or_options_t *options = get_options(); - double hop_cnt, success_cnt, timeouts, collapsed, successful_closed, - unusable; - - if (!node) { - *msg = tor_strdup("Unable to parse entry nodes: " - "EntryGuardPathBias without EntryGuard"); - break; - } - - /* First try 3 params, then 2. */ - /* In the long run: circuit_success ~= successful_circuit_close + - * collapsed_circuits + - * unusable_circuits */ - if (tor_sscanf(line->value, "%lf %lf %lf %lf %lf %lf", - &hop_cnt, &success_cnt, &successful_closed, - &collapsed, &unusable, &timeouts) != 6) { - int old_success, old_hops; - if (tor_sscanf(line->value, "%u %u", &old_success, &old_hops) != 2) { - continue; - } - log_info(LD_GENERAL, "Reading old-style EntryGuardPathBias %s", - escaped(line->value)); - - success_cnt = old_success; - successful_closed = old_success; - hop_cnt = old_hops; - timeouts = 0; - collapsed = 0; - unusable = 0; - } - - if (hop_cnt < success_cnt) { - int severity = LOG_INFO; - /* If this state file was written by a Tor that would have - * already fixed it, then the overcounting bug is still there.. */ - if (tor_version_as_new_as(state_version, "0.2.4.13-alpha")) { - severity = LOG_NOTICE; - } - log_fn(severity, LD_BUG, - "State file contains unexpectedly high success counts " - "%lf/%lf for Guard %s ($%s)", - success_cnt, hop_cnt, - node->nickname, hex_str(node->identity, DIGEST_LEN)); - success_cnt = hop_cnt; - } - - node->circ_attempts = hop_cnt; - node->circ_successes = success_cnt; - - node->successful_circuits_closed = successful_closed; - node->timeouts = timeouts; - node->collapsed_circuits = collapsed; - node->unusable_circuits = unusable; - - log_info(LD_GENERAL, "Read %f/%f path bias for node %s", - node->circ_successes, node->circ_attempts, node->nickname); - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_close_success_count(node)/node->circ_attempts - < pathbias_get_extreme_rate(options) && - pathbias_get_dropguards(options)) { - node->path_bias_disabled = 1; - log_info(LD_GENERAL, - "Path bias is too high (%f/%f); disabling node %s", - node->circ_successes, node->circ_attempts, node->nickname); - } - - } else { - log_warn(LD_BUG, "Unexpected key %s", line->key); - } - } - - SMARTLIST_FOREACH_BEGIN(new_entry_guards, entry_guard_t *, e) { - char *sp; - char *val = digestmap_get(added_by, e->identity); - if (val && (sp = strchr(val, ' '))) { - time_t when; - *sp++ = '\0'; - if (parse_iso_time(sp, &when)<0) { - log_warn(LD_BUG, "Can't read time %s in EntryGuardAddedBy", sp); - } else { - e->chosen_by_version = tor_strdup(val); - e->chosen_on_date = when; - } - } else { - if (state_version) { - e->chosen_by_version = tor_strdup(state_version); - e->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); - } - } - if (e->path_bias_disabled && !e->bad_since) - e->bad_since = time(NULL); - } - SMARTLIST_FOREACH_END(e); - - if (*msg || !set) { - SMARTLIST_FOREACH(new_entry_guards, entry_guard_t *, e, - entry_guard_free(e)); - smartlist_free(new_entry_guards); - } else { /* !err && set */ - if (entry_guards) { - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, - entry_guard_free(e)); - smartlist_free(entry_guards); - } - entry_guards = new_entry_guards; - entry_guards_dirty = 0; - /* XXX024 hand new_entry_guards to this func, and move it up a - * few lines, so we don't have to re-dirty it */ - if (remove_obsolete_entry_guards(now)) - entry_guards_dirty = 1; - - update_node_guard_status(); - } - digestmap_free(added_by, tor_free_); - return *msg ? -1 : 0; -} - -/** Our list of entry guards has changed, or some element of one - * of our entry guards has changed. Write the changes to disk within - * the next few minutes. - */ -void -entry_guards_changed(void) -{ - time_t when; - entry_guards_dirty = 1; - - /* or_state_save() will call entry_guards_update_state(). */ - when = get_options()->AvoidDiskWrites ? time(NULL) + 3600 : time(NULL)+600; - or_state_mark_dirty(get_or_state(), when); -} - -/** If the entry guard info has not changed, do nothing and return. - * Otherwise, free the EntryGuards piece of state and create - * a new one out of the global entry_guards list, and then mark - * state dirty so it will get saved to disk. - */ -void -entry_guards_update_state(or_state_t *state) -{ - config_line_t **next, *line; - if (! entry_guards_dirty) - return; - - config_free_lines(state->EntryGuards); - next = &state->EntryGuards; - *next = NULL; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - char dbuf[HEX_DIGEST_LEN+1]; - if (!e->made_contact) - continue; /* don't write this one to disk */ - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuard"); - base16_encode(dbuf, sizeof(dbuf), e->identity, DIGEST_LEN); - tor_asprintf(&line->value, "%s %s %sDirCache", e->nickname, dbuf, - e->is_dir_cache ? "" : "No"); - next = &(line->next); - if (e->unreachable_since) { - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuardDownSince"); - line->value = tor_malloc(ISO_TIME_LEN+1+ISO_TIME_LEN+1); - format_iso_time(line->value, e->unreachable_since); - if (e->last_attempted) { - line->value[ISO_TIME_LEN] = ' '; - format_iso_time(line->value+ISO_TIME_LEN+1, e->last_attempted); - } - next = &(line->next); - } - if (e->bad_since) { - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuardUnlistedSince"); - line->value = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(line->value, e->bad_since); - next = &(line->next); - } - if (e->chosen_on_date && e->chosen_by_version && - !strchr(e->chosen_by_version, ' ')) { - char d[HEX_DIGEST_LEN+1]; - char t[ISO_TIME_LEN+1]; - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuardAddedBy"); - base16_encode(d, sizeof(d), e->identity, DIGEST_LEN); - format_iso_time(t, e->chosen_on_date); - tor_asprintf(&line->value, "%s %s %s", - d, e->chosen_by_version, t); - next = &(line->next); - } - if (e->circ_attempts > 0) { - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuardPathBias"); - /* In the long run: circuit_success ~= successful_circuit_close + - * collapsed_circuits + - * unusable_circuits */ - tor_asprintf(&line->value, "%f %f %f %f %f %f", - e->circ_attempts, e->circ_successes, - pathbias_get_close_success_count(e), - e->collapsed_circuits, - e->unusable_circuits, e->timeouts); - next = &(line->next); - } - if (e->use_attempts > 0) { - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuardPathUseBias"); - - tor_asprintf(&line->value, "%f %f", - e->use_attempts, - pathbias_get_use_success_count(e)); - next = &(line->next); - } - - } SMARTLIST_FOREACH_END(e); - if (!get_options()->AvoidDiskWrites) - or_state_mark_dirty(get_or_state(), 0); - entry_guards_dirty = 0; -} - -/** If question is the string "entry-guards", then dump - * to *answer a newly allocated string describing all of - * the nodes in the global entry_guards list. See control-spec.txt - * for details. - * For backward compatibility, we also handle the string "helper-nodes". - * */ -int -getinfo_helper_entry_guards(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg) -{ - (void) conn; - (void) errmsg; - - if (!strcmp(question,"entry-guards") || - !strcmp(question,"helper-nodes")) { - smartlist_t *sl = smartlist_new(); - char tbuf[ISO_TIME_LEN+1]; - char nbuf[MAX_VERBOSE_NICKNAME_LEN+1]; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - const char *status = NULL; - time_t when = 0; - const node_t *node; - - if (!e->made_contact) { - status = "never-connected"; - } else if (e->bad_since) { - when = e->bad_since; - status = "unusable"; - } else { - status = "up"; - } - - node = node_get_by_id(e->identity); - if (node) { - node_get_verbose_nickname(node, nbuf); - } else { - nbuf[0] = '$'; - base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN); - /* e->nickname field is not very reliable if we don't know about - * this router any longer; don't include it. */ - } - - if (when) { - format_iso_time(tbuf, when); - smartlist_add_asprintf(sl, "%s %s %s\n", nbuf, status, tbuf); - } else { - smartlist_add_asprintf(sl, "%s %s\n", nbuf, status); - } - } SMARTLIST_FOREACH_END(e); - *answer = smartlist_join_strings(sl, "", 0, NULL); - SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); - smartlist_free(sl); - } - return 0; -} - -/** A list of configured bridges. Whenever we actually get a descriptor - * for one, we add it as an entry guard. Note that the order of bridges - * in this list does not necessarily correspond to the order of bridges - * in the torrc. */ -static smartlist_t *bridge_list = NULL; - -/** Mark every entry of the bridge list to be removed on our next call to - * sweep_bridge_list unless it has first been un-marked. */ -void -mark_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, - b->marked_for_removal = 1); -} - -/** Remove every entry of the bridge list that was marked with - * mark_bridge_list if it has not subsequently been un-marked. */ -void -sweep_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { - if (b->marked_for_removal) { - SMARTLIST_DEL_CURRENT(bridge_list, b); - bridge_free(b); - } - } SMARTLIST_FOREACH_END(b); -} - -/** Initialize the bridge list to empty, creating it if needed. */ -static void -clear_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b)); - smartlist_clear(bridge_list); -} - -/** Free the bridge bridge. */ -static void -bridge_free(bridge_info_t *bridge) -{ - if (!bridge) - return; - - tor_free(bridge->transport_name); - if (bridge->socks_args) { - SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s)); - smartlist_free(bridge->socks_args); - } - - tor_free(bridge); -} - -/** If we have a bridge configured whose digest matches digest, or a - * bridge with no known digest whose address matches any of the - * tor_addr_port_t's in orports, return that bridge. Else return - * NULL. */ -static bridge_info_t * -get_configured_bridge_by_orports_digest(const char *digest, - const smartlist_t *orports) -{ - if (!bridge_list) - return NULL; - 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; - } - SMARTLIST_FOREACH_END(bridge); - return NULL; -} - -/** If we have a bridge configured whose digest matches digest, or a - * bridge with no known digest whose address matches addr:/port, - * return that bridge. Else return NULL. If digest is NULL, check for - * address/port matches only. */ -static bridge_info_t * -get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, - uint16_t port, - const char *digest) -{ - if (!bridge_list) - return NULL; - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) - { - if ((tor_digest_is_zero(bridge->identity) || digest == NULL) && - !tor_addr_compare(&bridge->addr, addr, CMP_EXACT) && - bridge->port == port) - return bridge; - if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) - return bridge; - } - SMARTLIST_FOREACH_END(bridge); - return NULL; -} - -/** Wrapper around get_configured_bridge_by_addr_port_digest() to look - * it up via router descriptor ri. */ -static bridge_info_t * -get_configured_bridge_by_routerinfo(const routerinfo_t *ri) -{ - 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 ri is one of our known bridges, else 0. */ -int -routerinfo_is_a_configured_bridge(const routerinfo_t *ri) -{ - return get_configured_bridge_by_routerinfo(ri) ? 1 : 0; -} - -/** Return 1 if node is one of our configured bridges, else 0. */ -int -node_is_a_configured_bridge(const node_t *node) -{ - 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; -} - -/** We made a connection to a router at addr:port - * without knowing its digest. Its digest turned out to be digest. - * If it was a bridge, and we still don't know its digest, record it. - */ -void -learned_router_identity(const tor_addr_t *addr, uint16_t port, - const char *digest) -{ - bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(addr, port, digest); - if (bridge && tor_digest_is_zero(bridge->identity)) { - char *transport_info = NULL; - const char *transport_name = - find_transport_name_by_bridge_addrport(addr, port); - if (transport_name) - tor_asprintf(&transport_info, " (with transport '%s')", transport_name); - - memcpy(bridge->identity, digest, DIGEST_LEN); - log_notice(LD_DIR, "Learned fingerprint %s for bridge %s%s.", - hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port), - transport_info ? transport_info : ""); - tor_free(transport_info); - } -} - -/** Return true if bridge has the same identity digest as - * digest. If digest is NULL, it matches - * bridges with unspecified identity digests. */ -static int -bridge_has_digest(const bridge_info_t *bridge, const char *digest) -{ - if (digest) - return tor_memeq(digest, bridge->identity, DIGEST_LEN); - else - return tor_digest_is_zero(bridge->identity); -} - -/** We are about to add a new bridge at addr:port, with optional - * digest and transport_name. Mark for removal any previously - * existing bridge with the same address and port, and warn the user as - * appropriate. - */ -static void -bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, - const char *digest, const char *transport_name) -{ - /* Iterate the already-registered bridge list: - - If you find a bridge with the same adress and port, mark it for - removal. It doesn't make sense to have two active bridges with - the same IP:PORT. If the bridge in question has a different - digest or transport than digest/transport_name, - it's probably a misconfiguration and we should warn the user. - */ - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { - if (bridge->marked_for_removal) - continue; - - if (tor_addr_eq(&bridge->addr, addr) && (bridge->port == port)) { - - bridge->marked_for_removal = 1; - - if (!bridge_has_digest(bridge, digest) || - strcmp_opt(bridge->transport_name, transport_name)) { - /* warn the user */ - char *bridge_description_new, *bridge_description_old; - tor_asprintf(&bridge_description_new, "%s:%s:%s", - fmt_addrport(addr, port), - digest ? hex_str(digest, DIGEST_LEN) : "", - transport_name ? transport_name : ""); - tor_asprintf(&bridge_description_old, "%s:%s:%s", - fmt_addrport(&bridge->addr, bridge->port), - tor_digest_is_zero(bridge->identity) ? - "" : hex_str(bridge->identity,DIGEST_LEN), - bridge->transport_name ? bridge->transport_name : ""); - - log_warn(LD_GENERAL,"Tried to add bridge '%s', but we found a conflict" - " with the already registered bridge '%s'. We will discard" - " the old bridge and keep '%s'. If this is not what you" - " wanted, please change your configuration file accordingly.", - bridge_description_new, bridge_description_old, - bridge_description_new); - - tor_free(bridge_description_new); - tor_free(bridge_description_old); - } - } - } SMARTLIST_FOREACH_END(bridge); -} - -/** Return True if we have a bridge that uses a transport with name - * transport_name. */ -int -transport_is_needed(const char *transport_name) -{ - if (!bridge_list) - return 0; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (bridge->transport_name && - !strcmp(bridge->transport_name, transport_name)) - return 1; - } SMARTLIST_FOREACH_END(bridge); - - return 0; -} - -/** Register the bridge information in bridge_line to the - * bridge subsystem. Steals reference of bridge_line. */ -void -bridge_add_from_config(bridge_line_t *bridge_line) -{ - bridge_info_t *b; - - { /* Log the bridge we are about to register: */ - log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)", - fmt_addrport(&bridge_line->addr, bridge_line->port), - bridge_line->transport_name ? - bridge_line->transport_name : "no transport", - tor_digest_is_zero(bridge_line->digest) ? - "no key listed" : hex_str(bridge_line->digest, DIGEST_LEN)); - - if (bridge_line->socks_args) { /* print socks arguments */ - int i = 0; - - tor_assert(smartlist_len(bridge_line->socks_args) > 0); - - log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:", - smartlist_len(bridge_line->socks_args)); - SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg, - log_debug(LD_CONFIG, "%d: %s", ++i, arg)); - } - } - - bridge_resolve_conflicts(&bridge_line->addr, - bridge_line->port, - bridge_line->digest, - bridge_line->transport_name); - - b = tor_malloc_zero(sizeof(bridge_info_t)); - tor_addr_copy(&b->addr, &bridge_line->addr); - b->port = bridge_line->port; - memcpy(b->identity, bridge_line->digest, DIGEST_LEN); - if (bridge_line->transport_name) - b->transport_name = bridge_line->transport_name; - b->fetch_status.schedule = DL_SCHED_BRIDGE; - b->socks_args = bridge_line->socks_args; - if (!bridge_list) - bridge_list = smartlist_new(); - - tor_free(bridge_line); /* Deallocate bridge_line now. */ - - smartlist_add(bridge_list, b); -} - -/** Return true iff routerset contains the bridge bridge. */ -static int -routerset_contains_bridge(const routerset_t *routerset, - const bridge_info_t *bridge) -{ - int result; - extend_info_t *extinfo; - tor_assert(bridge); - if (!routerset) - return 0; - - extinfo = extend_info_new( - NULL, bridge->identity, NULL, NULL, &bridge->addr, bridge->port); - result = routerset_contains_extendinfo(routerset, extinfo); - extend_info_free(extinfo); - return result; -} - -/** If digest is one of our known bridges, return it. */ -static bridge_info_t * -find_bridge_by_digest(const char *digest) -{ - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, bridge, - { - if (tor_memeq(bridge->identity, digest, DIGEST_LEN)) - return bridge; - }); - return NULL; -} - -/** Given the addr and port of a bridge, if that bridge - * supports a pluggable transport, return its name. Otherwise, return - * NULL. */ -const char * -find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) -{ - if (!bridge_list) - return NULL; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (tor_addr_eq(&bridge->addr, addr) && - (bridge->port == port)) - return bridge->transport_name; - } SMARTLIST_FOREACH_END(bridge); - - return NULL; -} - -/** If addr and port match the address and port of a - * bridge of ours that uses pluggable transports, place its transport - * in transport. - * - * Return 0 on success (found a transport, or found a bridge with no - * transport, or found no bridge); return -1 if we should be using a - * transport, but the transport could not be found. - */ -int -get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const transport_t **transport) -{ - *transport = NULL; - if (!bridge_list) - return 0; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (tor_addr_eq(&bridge->addr, addr) && - (bridge->port == port)) { /* bridge matched */ - if (bridge->transport_name) { /* it also uses pluggable transports */ - *transport = transport_get_by_name(bridge->transport_name); - if (*transport == NULL) { /* it uses pluggable transports, but - the transport could not be found! */ - return -1; - } - return 0; - } else { /* bridge matched, but it doesn't use transports. */ - break; - } - } - } SMARTLIST_FOREACH_END(bridge); - - *transport = NULL; - return 0; -} - -/** Return a smartlist containing all the SOCKS arguments that we - * should pass to the SOCKS proxy. */ -const smartlist_t * -get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) -{ - bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr, - port, - NULL); - return bridge ? bridge->socks_args : NULL; -} - -/** We need to ask bridge for its server descriptor. */ -static void -launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) -{ - char *address; - const or_options_t *options = get_options(); - - if (connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &bridge->addr, bridge->port, - DIR_PURPOSE_FETCH_SERVERDESC)) - return; /* it's already on the way */ - - if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { - download_status_mark_impossible(&bridge->fetch_status); - log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", - safe_str_client(fmt_and_decorate_addr(&bridge->addr))); - return; - } - - address = tor_dup_addr(&bridge->addr); - - directory_initiate_command(address, &bridge->addr, - bridge->port, 0/*no dirport*/, - bridge->identity, - DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_BRIDGE, - DIRIND_ONEHOP, "authority.z", NULL, 0, 0); - tor_free(address); -} - -/** Fetching the bridge descriptor from the bridge authority returned a - * "not found". Fall back to trying a direct fetch. */ -void -retry_bridge_descriptor_fetch_directly(const char *digest) -{ - bridge_info_t *bridge = find_bridge_by_digest(digest); - if (!bridge) - return; /* not found? oh well. */ - - launch_direct_bridge_descriptor_fetch(bridge); -} - -/** For each bridge in our list for which we don't currently have a - * descriptor, fetch a new copy of its descriptor -- either directly - * from the bridge or via a bridge authority. */ -void -fetch_bridge_descriptors(const or_options_t *options, time_t now) -{ - int num_bridge_auths = get_n_authorities(BRIDGE_DIRINFO); - int ask_bridge_directly; - int can_use_bridge_authority; - - if (!bridge_list) - return; - - /* If we still have unconfigured managed proxies, don't go and - connect to a bridge. */ - if (pt_proxies_configuration_pending()) - return; - - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) - { - if (!download_status_is_ready(&bridge->fetch_status, now, - IMPOSSIBLE_TO_DOWNLOAD)) - continue; /* don't bother, no need to retry yet */ - if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { - download_status_mark_impossible(&bridge->fetch_status); - log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", - safe_str_client(fmt_and_decorate_addr(&bridge->addr))); - continue; - } - - /* schedule another fetch as if this one will fail, in case it does */ - download_status_failed(&bridge->fetch_status, 0); - - can_use_bridge_authority = !tor_digest_is_zero(bridge->identity) && - num_bridge_auths; - ask_bridge_directly = !can_use_bridge_authority || - !options->UpdateBridgesFromAuthority; - log_debug(LD_DIR, "ask_bridge_directly=%d (%d, %d, %d)", - ask_bridge_directly, tor_digest_is_zero(bridge->identity), - !options->UpdateBridgesFromAuthority, !num_bridge_auths); - - if (ask_bridge_directly && - !fascist_firewall_allows_address_or(&bridge->addr, bridge->port)) { - log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " - "firewall policy. %s.", - fmt_addrport(&bridge->addr, bridge->port), - can_use_bridge_authority ? - "Asking bridge authority instead" : "Skipping"); - if (can_use_bridge_authority) - ask_bridge_directly = 0; - else - continue; - } - - if (ask_bridge_directly) { - /* we need to ask the bridge itself for its descriptor. */ - launch_direct_bridge_descriptor_fetch(bridge); - } else { - /* We have a digest and we want to ask an authority. We could - * combine all the requests into one, but that may give more - * hints to the bridge authority than we want to give. */ - char resource[10 + HEX_DIGEST_LEN]; - memcpy(resource, "fp/", 3); - base16_encode(resource+3, HEX_DIGEST_LEN+1, - bridge->identity, DIGEST_LEN); - memcpy(resource+3+HEX_DIGEST_LEN, ".z", 3); - log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.", - resource); - directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_BRIDGE, resource, 0); - } - } - SMARTLIST_FOREACH_END(bridge); -} - -/** If our bridge is configured to be a different address than - * the bridge gives in node, rewrite the routerinfo - * we received to use the address we meant to use. Now we handle - * multihomed bridges better. - */ -static void -rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) -{ - /* XXXX move this function. */ - /* XXXX overridden addresses should really live in the node_t, so that the - * routerinfo_t and the microdesc_t can be immutable. But we can only - * do that safely if we know that no function that connects to an OR - * does so through an address from any source other than node_get_addr(). - */ - tor_addr_t addr; - - if (node->ri) { - routerinfo_t *ri = node->ri; - tor_addr_from_ipv4h(&addr, ri->addr); - - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) || - (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && - bridge->port == ri->ipv6_orport)) { - /* they match, so no need to do anything */ - } else { - if (tor_addr_family(&bridge->addr) == AF_INET) { - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - tor_free(ri->address); - ri->address = tor_dup_ip(ri->addr); - ri->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s:%d.", - ri->nickname, ri->address, ri->or_port); - } else if (tor_addr_family(&bridge->addr) == AF_INET6) { - tor_addr_copy(&ri->ipv6_addr, &bridge->addr); - ri->ipv6_orport = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s.", - ri->nickname, fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); - } else { - log_err(LD_BUG, "Address family not supported: %d.", - tor_addr_family(&bridge->addr)); - return; - } - } - - /* Mark which address to use based on which bridge_t we got. */ - node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && - !tor_addr_is_null(&node->ri->ipv6_addr)); - - /* 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; - 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).", - ri->nickname, - tor_addr_family(&ap.addr) == AF_INET6 ? "IPv6" : "IPv4", - fmt_addrport(&ap.addr, ap.port)); - } - } - if (node->rs) { - routerstatus_t *rs = node->rs; - tor_addr_from_ipv4h(&addr, rs->addr); - - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == rs->or_port) { - /* they match, so no need to do anything */ - } else { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerstatus for '%s' to match " - "configured address %s.", - rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); - } - } -} - -/** We just learned a descriptor for a bridge. See if that - * digest is in our entry guard list, and add it if not. */ -void -learned_bridge_descriptor(routerinfo_t *ri, int from_cache) -{ - tor_assert(ri); - tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE); - if (get_options()->UseBridges) { - int first = !any_bridge_descriptors_known(); - bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri); - time_t now = time(NULL); - router_set_status(ri->cache_info.identity_digest, 1); - - if (bridge) { /* if we actually want to use this one */ - node_t *node; - /* it's here; schedule its re-fetch for a long time from now. */ - if (!from_cache) - download_status_reset(&bridge->fetch_status); - - node = node_get_mutable_by_id(ri->cache_info.identity_digest); - tor_assert(node); - rewrite_node_address_for_bridge(bridge, node); - add_an_entry_guard(node, 1, 1, 0, 0); - - log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, - from_cache ? "cached" : "fresh", router_describe(ri)); - /* set entry->made_contact so if it goes down we don't drop it from - * our entry node list */ - entry_guard_register_connect_status(ri->cache_info.identity_digest, - 1, 0, now); - if (first) - routerlist_retry_directory_downloads(now); - } - } -} - -/** Return 1 if any of our entry guards have descriptors that - * are marked with purpose 'bridge' and are running. Else return 0. - * - * We use this function to decide if we're ready to start building - * circuits through our bridges, or if we need to wait until the - * directory "server/authority" requests finish. */ -int -any_bridge_descriptors_known(void) -{ - tor_assert(get_options()->UseBridges); - return choose_random_entry(NULL) != NULL; -} - -/** Return 1 if there are any directory conns fetching bridge descriptors - * that aren't marked for close. We use this to guess if we should tell - * the controller that we have a problem. */ -int -any_pending_bridge_descriptor_fetches(void) -{ - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->type == CONN_TYPE_DIR && - conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC && - TO_DIR_CONN(conn)->router_purpose == ROUTER_PURPOSE_BRIDGE && - !conn->marked_for_close && - conn->linked && - conn->linked_conn && !conn->linked_conn->marked_for_close) { - log_debug(LD_DIR, "found one: %s", conn->address); - return 1; - } - } SMARTLIST_FOREACH_END(conn); - return 0; -} - -/** Return 1 if we have at least one descriptor for an entry guard - * (bridge or member of EntryNodes) and all descriptors we know are - * down. Else return 0. If act is 1, then mark the down guards - * up; else just observe and report. */ -static int -entries_retry_helper(const or_options_t *options, int act) -{ - const node_t *node; - int any_known = 0; - int any_running = 0; - int need_bridges = options->UseBridges != 0; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - node = node_get_by_id(e->identity); - if (node && node_has_descriptor(node) && - node_is_bridge(node) == need_bridges) { - any_known = 1; - if (node->is_running) - any_running = 1; /* some entry is both known and running */ - else if (act) { - /* Mark all current connections to this OR as unhealthy, since - * otherwise there could be one that started 30 seconds - * ago, and in 30 seconds it will time out, causing us to mark - * the node down and undermine the retry attempt. We mark even - * the established conns, since if the network just came back - * we'll want to attach circuits to fresh conns. */ - connection_or_set_bad_connections(node->identity, 1); - - /* mark this entry node for retry */ - router_set_status(node->identity, 1); - e->can_retry = 1; - e->bad_since = 0; - } - } - } SMARTLIST_FOREACH_END(e); - log_debug(LD_DIR, "%d: any_known %d, any_running %d", - act, any_known, any_running); - return any_known && !any_running; -} - -/** Do we know any descriptors for our bridges / entrynodes, and are - * all the ones we have descriptors for down? */ -int -entries_known_but_down(const or_options_t *options) -{ - tor_assert(entry_list_is_constrained(options)); - return entries_retry_helper(options, 0); -} - -/** Mark all down known bridges / entrynodes up. */ -void -entries_retry_all(const or_options_t *options) -{ - tor_assert(entry_list_is_constrained(options)); - entries_retry_helper(options, 1); -} - -/** Return true if at least one of our bridges runs a Tor version that can - * provide microdescriptors to us. If not, we'll fall back to asking for - * full descriptors. */ -int -any_bridge_supports_microdescriptors(void) -{ - const node_t *node; - if (!get_options()->UseBridges || !entry_guards) - return 0; - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - node = node_get_by_id(e->identity); - if (node && node->is_running && - node_is_bridge(node) && node_is_a_configured_bridge(node) && - node_understands_microdescriptors(node)) { - /* This is one of our current bridges, and we know enough about - * it to know that it will be able to answer our microdescriptor - * questions. */ - return 1; - } - } SMARTLIST_FOREACH_END(e); - return 0; -} - -/** Release all storage held by the list of entry guards and related - * memory structs. */ -void -entry_guards_free_all(void) -{ - if (entry_guards) { - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, - entry_guard_free(e)); - smartlist_free(entry_guards); - entry_guards = NULL; - } - clear_bridge_list(); - smartlist_free(bridge_list); - bridge_list = NULL; - circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); -} - diff --git a/src/tor/entrynodes.h b/src/tor/entrynodes.h deleted file mode 100644 index 772c666..0000000 --- a/src/tor/entrynodes.h +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file guardnodes.h - * \brief Header file for circuitbuild.c. - **/ - -#ifndef TOR_ENTRYNODES_H -#define TOR_ENTRYNODES_H - -#if 1 -/* XXXX NM I would prefer that all of this stuff be private to - * entrynodes.c. */ - -/** An entry_guard_t represents our information about a chosen long-term - * first hop, known as a "helper" node in the literature. We can't just - * use a node_t, since we want to remember these even when we - * don't have any directory info. */ -typedef struct entry_guard_t { - char nickname[MAX_NICKNAME_LEN+1]; - char identity[DIGEST_LEN]; - time_t chosen_on_date; /**< Approximately when was this guard added? - * "0" if we don't know. */ - char *chosen_by_version; /**< What tor version added this guard? NULL - * if we don't know. */ - unsigned int made_contact : 1; /**< 0 if we have never connected to this - * router, 1 if we have. */ - unsigned int can_retry : 1; /**< Should we retry connecting to this entry, - * in spite of having it marked as unreachable?*/ - unsigned int path_bias_noticed : 1; /**< Did we alert the user about path - * bias for this node already? */ - unsigned int path_bias_warned : 1; /**< Did we alert the user about path bias - * for this node already? */ - unsigned int path_bias_extreme : 1; /**< Did we alert the user about path - * bias for this node already? */ - unsigned int path_bias_disabled : 1; /**< Have we disabled this node because - * of path bias issues? */ - unsigned int path_bias_use_noticed : 1; /**< Did we alert the user about path - * use bias for this node already? */ - unsigned int path_bias_use_extreme : 1; /**< Did we alert the user about path - * use bias for this node already? */ - unsigned int is_dir_cache : 1; /**< Is this node a directory cache? */ - time_t bad_since; /**< 0 if this guard is currently usable, or the time at - * which it was observed to become (according to the - * directory or the user configuration) unusable. */ - time_t unreachable_since; /**< 0 if we can connect to this guard, or the - * time at which we first noticed we couldn't - * connect to it. */ - time_t last_attempted; /**< 0 if we can connect to this guard, or the time - * at which we last failed to connect to it. */ - - double circ_attempts; /**< Number of circuits this guard has "attempted" */ - double circ_successes; /**< Number of successfully built circuits using - * this guard as first hop. */ - double successful_circuits_closed; /**< Number of circuits that carried - * streams successfully. */ - double collapsed_circuits; /**< Number of fully built circuits that were - * remotely closed before any streams were - * attempted. */ - double unusable_circuits; /**< Number of circuits for which streams were - * attempted, but none succeeded. */ - double timeouts; /**< Number of 'right-censored' circuit timeouts for this - * guard. */ - double use_attempts; /**< Number of circuits we tried to use with streams */ - double use_successes; /**< Number of successfully used circuits using - * this guard as first hop. */ -} entry_guard_t; - -entry_guard_t *entry_guard_get_by_id_digest(const char *digest); -void entry_guards_changed(void); -const smartlist_t *get_entry_guards(void); -int num_live_entry_guards(int for_directory); - -#endif - -void remove_all_entry_guards(void); - -void entry_guards_compute_status(const or_options_t *options, time_t now); -int entry_guard_register_connect_status(const char *digest, int succeeded, - int mark_relay_status, time_t now); -void entry_nodes_should_be_added(void); -int entry_list_is_constrained(const or_options_t *options); -const node_t *choose_random_entry(cpath_build_state_t *state); -const node_t *choose_random_dirguard(dirinfo_type_t t); -int entry_guards_parse_state(or_state_t *state, int set, char **msg); -void entry_guards_update_state(or_state_t *state); -int getinfo_helper_entry_guards(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg); - -void mark_bridge_list(void); -void sweep_bridge_list(void); - -int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); -int node_is_a_configured_bridge(const node_t *node); -void learned_router_identity(const tor_addr_t *addr, uint16_t port, - const char *digest); -struct bridge_line_t; -void bridge_add_from_config(struct bridge_line_t *bridge_line); -void retry_bridge_descriptor_fetch_directly(const char *digest); -void fetch_bridge_descriptors(const or_options_t *options, time_t now); -void learned_bridge_descriptor(routerinfo_t *ri, int from_cache); -int any_bridge_descriptors_known(void); -int any_pending_bridge_descriptor_fetches(void); -int entries_known_but_down(const or_options_t *options); -void entries_retry_all(const or_options_t *options); - -int any_bridge_supports_microdescriptors(void); -const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr, - uint16_t port); - -int any_bridges_dont_support_microdescriptors(void); - -void entry_guards_free_all(void); - -const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, - uint16_t port); -struct transport_t; -int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const struct transport_t **transport); - -int transport_is_needed(const char *transport_name); -int validate_pluggable_transports_config(void); - -double pathbias_get_close_success_count(entry_guard_t *guard); -double pathbias_get_use_success_count(entry_guard_t *guard); - -#endif - diff --git a/src/tor/eventdns.h b/src/tor/eventdns.h deleted file mode 100644 index ad8c100..0000000 --- a/src/tor/eventdns.h +++ /dev/null @@ -1,337 +0,0 @@ - -/* - * The original DNS code is due to Adam Langley with heavy - * modifications by Nick Mathewson. Adam put his DNS software in the - * public domain. You can find his original copyright below. Please, - * aware that the code as part of libevent is governed by the 3-clause - * BSD license above. - * - * This software is Public Domain. To view a copy of the public domain dedication, - * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to - * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. - * - * I ask and expect, but do not require, that all derivative works contain an - * attribution similar to: - * Parts developed by Adam Langley - * - * You may wish to replace the word "Parts" with something else depending on - * the amount of original code. - * - * (Derivative works does not include programs which link against, run or include - * the source verbatim in their source distributions) - */ - -/* - * Welcome, gentle reader - * - * Async DNS lookups are really a whole lot harder than they should be, - * mostly stemming from the fact that the libc resolver has never been - * very good at them. Before you use this library you should see if libc - * can do the job for you with the modern async call getaddrinfo_a - * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, - * please continue. - * - * This code is based on libevent and you must call event_init before - * any of the APIs in this file. You must also seed the OpenSSL random - * source if you are using OpenSSL for ids (see below). - * - * This library is designed to be included and shipped with your source - * code. You statically link with it. You should also test for the - * existence of strtok_r and define HAVE_STRTOK_R if you have it. - * - * The DNS protocol requires a good source of id numbers and these - * numbers should be unpredictable for spoofing reasons. There are - * three methods for generating them here and you must define exactly - * one of them. In increasing order of preference: - * - * DNS_USE_GETTIMEOFDAY_FOR_ID: - * Using the bottom 16 bits of the usec result from gettimeofday. This - * is a pretty poor solution but should work anywhere. - * DNS_USE_CPU_CLOCK_FOR_ID: - * Using the bottom 16 bits of the nsec result from the CPU's time - * counter. This is better, but may not work everywhere. Requires - * POSIX realtime support and you'll need to link against -lrt on - * glibc systems at least. - * DNS_USE_OPENSSL_FOR_ID: - * Uses the OpenSSL RAND_bytes call to generate the data. You must - * have seeded the pool before making any calls to this library. - * - * The library keeps track of the state of nameservers and will avoid - * them when they go down. Otherwise it will round robin between them. - * - * Quick start guide: - * #include "evdns.h" - * void callback(int result, char type, int count, int ttl, - * void *addresses, void *arg); - * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); - * evdns_resolve("www.hostname.com", 0, callback, NULL); - * - * When the lookup is complete the callback function is called. The - * first argument will be one of the DNS_ERR_* defines in evdns.h. - * Hopefully it will be DNS_ERR_NONE, in which case type will be - * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time - * which the data can be cached for (in seconds), addresses will point - * to an array of uint32_t's and arg will be whatever you passed to - * evdns_resolve. - * - * Searching: - * - * In order for this library to be a good replacement for glibc's resolver it - * supports searching. This involves setting a list of default domains, in - * which names will be queried for. The number of dots in the query name - * determines the order in which this list is used. - * - * Searching appears to be a single lookup from the point of view of the API, - * although many DNS queries may be generated from a single call to - * evdns_resolve. Searching can also drastically slow down the resolution - * of names. - * - * To disable searching: - * 1. Never set it up. If you never call evdns_resolv_conf_parse or - * evdns_search_add then no searching will occur. - * - * 2. If you do call evdns_resolv_conf_parse then don't pass - * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). - * - * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. - * - * The order of searches depends on the number of dots in the name. If the - * number is greater than the ndots setting then the names is first tried - * globally. Otherwise each search domain is appended in turn. - * - * The ndots setting can either be set from a resolv.conf, or by calling - * evdns_search_ndots_set. - * - * For example, with ndots set to 1 (the default) and a search domain list of - * ["myhome.net"]: - * Query: www - * Order: www.myhome.net, www. - * - * Query: www.abc - * Order: www.abc., www.abc.myhome.net - * - * API reference: - * - * int evdns_nameserver_add(uint32_t address) - * Add a nameserver. The address should be an IP address in - * network byte order. The type of address is chosen so that - * it matches in_addr.s_addr. - * Returns non-zero on error. - * - * int evdns_nameserver_ip_add(const char *ip_as_string) - * This wraps the above function by parsing a string as an IP - * address and adds it as a nameserver. - * Returns non-zero on error - * - * int evdns_resolve(const char *name, int flags, - * evdns_callback_type callback, - * void *ptr) - * Resolve a name. The name parameter should be a DNS name. - * The flags parameter should be 0, or DNS_QUERY_NO_SEARCH - * which disables searching for this query. (see defn of - * searching above). - * - * The callback argument is a function which is called when - * this query completes and ptr is an argument which is passed - * to that callback function. - * - * Returns non-zero on error - * - * void evdns_search_clear() - * Clears the list of search domains - * - * void evdns_search_add(const char *domain) - * Add a domain to the list of search domains - * - * void evdns_search_ndots_set(int ndots) - * Set the number of dots which, when found in a name, causes - * the first query to be without any search domain. - * - * int evdns_count_nameservers(void) - * Return the number of configured nameservers (not necessarily the - * number of running nameservers). This is useful for double-checking - * whether our calls to the various nameserver configuration functions - * have been successful. - * - * int evdns_clear_nameservers_and_suspend(void) - * Remove all currently configured nameservers, and suspend all pending - * resolves. Resolves will not necessarily be re-attempted until - * evdns_resume() is called. - * - * int evdns_resume(void) - * Re-attempt resolves left in limbo after an earlier call to - * evdns_clear_nameservers_and_suspend(). - * - * int evdns_config_windows_nameservers(void) - * Attempt to configure a set of nameservers based on platform settings on - * a win32 host. Preferentially tries to use GetNetworkParams; if that fails, - * looks in the registry. Returns 0 on success, nonzero on failure. - * - * int evdns_resolv_conf_parse(int flags, const char *filename) - * Parse a resolv.conf like file from the given filename. - * - * See the man page for resolv.conf for the format of this file. - * The flags argument determines what information is parsed from - * this file: - * DNS_OPTION_SEARCH - domain, search and ndots options - * DNS_OPTION_NAMESERVERS - nameserver lines - * DNS_OPTION_MISC - timeout and attempts options - * DNS_OPTIONS_ALL - all of the above - * The following directives are not parsed from the file: - * sortlist, rotate, no-check-names, inet6, debug - * - * Returns non-zero on error: - * 0 no errors - * 1 failed to open file - * 2 failed to stat file - * 3 file too large - * 4 out of memory - * 5 short read from file - * 6 no nameservers in file - * - * Internals: - * - * Requests are kept in two queues. The first is the inflight queue. In - * this queue requests have an allocated transaction id and nameserver. - * They will soon be transmitted if they haven't already been. - * - * The second is the waiting queue. The size of the inflight ring is - * limited and all other requests wait in waiting queue for space. This - * bounds the number of concurrent requests so that we don't flood the - * nameserver. Several algorithms require a full walk of the inflight - * queue and so bounding its size keeps thing going nicely under huge - * (many thousands of requests) loads. - * - * If a nameserver loses too many requests it is considered down and we - * try not to use it. After a while we send a probe to that nameserver - * (a lookup for google.com) and, if it replies, we consider it working - * again. If the nameserver fails a probe we wait longer to try again - * with the next probe. - */ - -#ifndef TOR_EVENTDNS_H -#define TOR_EVENTDNS_H - -/* Error codes 0-5 are as described in RFC 1035. */ -#define DNS_ERR_NONE 0 -/* The name server was unable to interpret the query */ -#define DNS_ERR_FORMAT 1 -/* The name server was unable to process this query due to a problem with the - * name server */ -#define DNS_ERR_SERVERFAILED 2 -/* The domain name does not exist */ -#define DNS_ERR_NOTEXIST 3 -/* The name server does not support the requested kind of query */ -#define DNS_ERR_NOTIMPL 4 -/* The name server refuses to reform the specified operation for policy - * reasons */ -#define DNS_ERR_REFUSED 5 -/* The reply was truncated or ill-formated */ -#define DNS_ERR_TRUNCATED 65 -/* An unknown error occurred */ -#define DNS_ERR_UNKNOWN 66 -/* Communication with the server timed out */ -#define DNS_ERR_TIMEOUT 67 -/* The request was canceled because the DNS subsystem was shut down. */ -#define DNS_ERR_SHUTDOWN 68 - -#define DNS_IPv4_A 1 -#define DNS_PTR 2 -#define DNS_IPv6_AAAA 3 - -#define DNS_QUERY_NO_SEARCH 1 - -#define DNS_OPTION_SEARCH 1 -#define DNS_OPTION_NAMESERVERS 2 -#define DNS_OPTION_MISC 4 -#define DNS_OPTIONS_ALL 7 - -/* - * The callback that contains the results from a lookup. - * - type is either DNS_IPv4_A or DNS_IPv6_AAAA or DNS_PTR - * - count contains the number of addresses of form type - * - ttl is the number of seconds the resolution may be cached for. - * - addresses needs to be cast according to type - */ -typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg); - -int evdns_init(void); -void evdns_shutdown(int fail_requests); -const char *evdns_err_to_string(int err); -int evdns_nameserver_add(uint32_t address); -int evdns_count_nameservers(void); -int evdns_clear_nameservers_and_suspend(void); -int evdns_resume(void); -int evdns_nameserver_ip_add(const char *ip_as_string); -int evdns_nameserver_sockaddr_add(const struct sockaddr *sa, socklen_t len); -void evdns_set_default_outgoing_bind_address(const struct sockaddr *addr, socklen_t addrlen); -int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr); -int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); -struct in_addr; -struct in6_addr; -int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); -int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); -int evdns_set_option(const char *option, const char *val, int flags); -int evdns_resolv_conf_parse(int flags, const char *); -#ifdef _WIN32 -int evdns_config_windows_nameservers(void); -#endif -void evdns_search_clear(void); -void evdns_search_add(const char *domain); -void evdns_search_ndots_set(const int ndots); - -typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); -void evdns_set_log_fn(evdns_debug_log_fn_type fn); - -void evdns_set_transaction_id_fn(uint16_t (*fn)(void)); -void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); - -#define DNS_NO_SEARCH 1 - -/* Structures and functions used to implement a DNS server. */ - -struct evdns_server_request { - int flags; - int nquestions; - struct evdns_server_question **questions; -}; -struct evdns_server_question { - int type; - int dns_question_class; - char name[1]; -}; -typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); -#define EVDNS_ANSWER_SECTION 0 -#define EVDNS_AUTHORITY_SECTION 1 -#define EVDNS_ADDITIONAL_SECTION 2 - -#define EVDNS_TYPE_A 1 -#define EVDNS_TYPE_NS 2 -#define EVDNS_TYPE_CNAME 5 -#define EVDNS_TYPE_SOA 6 -#define EVDNS_TYPE_PTR 12 -#define EVDNS_TYPE_MX 15 -#define EVDNS_TYPE_TXT 16 -#define EVDNS_TYPE_AAAA 28 - -#define EVDNS_QTYPE_AXFR 252 -#define EVDNS_QTYPE_ALL 255 - -#define EVDNS_CLASS_INET 1 - -struct evdns_server_port *evdns_add_server_port(tor_socket_t socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data); -void evdns_close_server_port(struct evdns_server_port *port); - -int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data); -int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); -int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); -int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); -int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); - -struct sockaddr; -int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len); - -int evdns_server_request_respond(struct evdns_server_request *req, int err); -int evdns_server_request_drop(struct evdns_server_request *req); - -#endif // !EVENTDNS_H diff --git a/src/tor/eventdns_tor.h b/src/tor/eventdns_tor.h deleted file mode 100644 index 4dc3c2f..0000000 --- a/src/tor/eventdns_tor.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_EVENTDNS_TOR_H -#define TOR_EVENTDNS_TOR_H - -#include "orconfig.h" -#define DNS_USE_OPENSSL_FOR_ID -#ifndef HAVE_UINT -typedef unsigned int uint; -#endif -#ifndef HAVE_U_CHAR -typedef unsigned char u_char; -#endif -#ifdef _WIN32 -#define inline __inline -#endif -#include "torint.h" - -/* These are for debugging possible memory leaks. */ -#include "tor_util.h" -#include "tor_compat.h" - -#endif - diff --git a/src/tor/ext_orport.c b/src/tor/ext_orport.c deleted file mode 100644 index fcfe15c..0000000 --- a/src/tor/ext_orport.c +++ /dev/null @@ -1,648 +0,0 @@ -/* Copyright (c) 2012, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file ext_orport.c - * \brief Code implementing the Extended ORPort. -*/ - -#define EXT_ORPORT_PRIVATE -#include "or.h" -#include "connection.h" -#include "connection_or.h" -#include "ext_orport.h" -#include "control.h" -#include "config.h" -#include "tor_util.h" -#include "onion_main.h" - -/** Allocate and return a structure capable of holding an Extended - * ORPort message of body length len. */ -ext_or_cmd_t * -ext_or_cmd_new(uint16_t len) -{ - size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len; - ext_or_cmd_t *cmd = tor_malloc(size); - cmd->len = len; - return cmd; -} - -/** Deallocate the Extended ORPort message in cmd. */ -void -ext_or_cmd_free(ext_or_cmd_t *cmd) -{ - tor_free(cmd); -} - -/** Get an Extended ORPort message from conn, and place it in - * out. Return -1 on fail, 0 if we need more data, and 1 if we - * successfully extracted an Extended ORPort command from the - * buffer. */ -static int -connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out) -{ - IF_HAS_BUFFEREVENT(conn, { - struct evbuffer *input = bufferevent_get_input(conn->bufev); - return fetch_ext_or_command_from_evbuffer(input, out); - }) ELSE_IF_NO_BUFFEREVENT { - return fetch_ext_or_command_from_buf(conn->inbuf, out); - } -} - -/** Write an Extended ORPort message to conn. Use - * command as the command type, bodylen as the body - * length, and body, if it's present, as the body of the - * message. */ -STATIC int -connection_write_ext_or_command(connection_t *conn, - uint16_t command, - const char *body, - size_t bodylen) -{ - char header[4]; - if (bodylen > UINT16_MAX) - return -1; - set_uint16(header, htons(command)); - set_uint16(header+2, htons(bodylen)); - connection_write_to_buf(header, 4, conn); - if (bodylen) { - tor_assert(body); - connection_write_to_buf(body, bodylen, conn); - } - return 0; -} - -/** Transition from an Extended ORPort which accepts Extended ORPort - * messages, to an Extended ORport which accepts OR traffic. */ -static void -connection_ext_or_transition(or_connection_t *conn) -{ - tor_assert(conn->base_.type == CONN_TYPE_EXT_OR); - - conn->base_.type = CONN_TYPE_OR; - TO_CONN(conn)->state = 0; // set the state to a neutral value - control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0); - connection_tls_start_handshake(conn, 1); -} - -/** Length of authentication cookie. */ -#define EXT_OR_PORT_AUTH_COOKIE_LEN 32 -/** Length of the header of the cookie file. */ -#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32 -/** Static cookie file header. */ -#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a" -/** Length of safe-cookie protocol hashes. */ -#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN -/** Length of safe-cookie protocol nonces. */ -#define EXT_OR_PORT_AUTH_NONCE_LEN 32 -/** Safe-cookie protocol constants. */ -#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \ - "ExtORPort authentication server-to-client hash" -#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \ - "ExtORPort authentication client-to-server hash" - -/* Code to indicate cookie authentication */ -#define EXT_OR_AUTHTYPE_SAFECOOKIE 0x01 - -/** If true, we've set ext_or_auth_cookie to a secret code and stored - * it to disk. */ -STATIC int ext_or_auth_cookie_is_set = 0; -/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk - * and which we're using to authenticate controllers. (If the controller can - * read it off disk, it has permission to connect.) */ -STATIC uint8_t *ext_or_auth_cookie = NULL; - -/** Helper: Return a newly allocated string containing a path to the - * file where we store our authentication cookie. */ -char * -get_ext_or_auth_cookie_file_name(void) -{ - const or_options_t *options = get_options(); - if (options->ExtORPortCookieAuthFile && - strlen(options->ExtORPortCookieAuthFile)) { - return tor_strdup(options->ExtORPortCookieAuthFile); - } else { - return get_datadir_fname("extended_orport_auth_cookie"); - } -} - -/* Initialize the cookie-based authentication system of the - * Extended ORPort. If is_enabled is 0, then disable the cookie - * authentication system. */ -int -init_ext_or_cookie_authentication(int is_enabled) -{ - char *fname = NULL; - int retval; - - if (!is_enabled) { - ext_or_auth_cookie_is_set = 0; - return 0; - } - - fname = get_ext_or_auth_cookie_file_name(); - retval = init_cookie_authentication(fname, EXT_OR_PORT_AUTH_COOKIE_HEADER, - EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN, - &ext_or_auth_cookie, - &ext_or_auth_cookie_is_set); - tor_free(fname); - return retval; -} - -/** Read data from conn and see if the client sent us the - * authentication type that she prefers to use in this session. - * - * Return -1 if we received corrupted data or if we don't support the - * authentication type. Return 0 if we need more data in - * conn. Return 1 if the authentication type negotiation was - * successful. */ -static int -connection_ext_or_auth_neg_auth_type(connection_t *conn) -{ - char authtype[1] = {0}; - - if (connection_get_inbuf_len(conn) < 1) - return 0; - - if (connection_fetch_from_buf(authtype, 1, conn) < 0) - return -1; - - log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]); - if (authtype[0] != EXT_OR_AUTHTYPE_SAFECOOKIE) { - /* '1' is the only auth type supported atm */ - return -1; - } - - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE; - return 1; -} - -/** DOCDOC */ -STATIC int -handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len, - char **client_hash_out, - char **reply_out, size_t *reply_len_out) -{ - char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; - char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0}; - char *reply; - size_t reply_len; - - if (client_nonce_len != EXT_OR_PORT_AUTH_NONCE_LEN) - return -1; - - /* Get our nonce */ - if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0) - return -1; - - { /* set up macs */ - size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + - 2*EXT_OR_PORT_AUTH_NONCE_LEN; - size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + - 2*EXT_OR_PORT_AUTH_NONCE_LEN; - - char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len); - char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len); - char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN); - - memcpy(hmac_s_msg, - EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST, - strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST)); - memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST), - client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + - EXT_OR_PORT_AUTH_NONCE_LEN, - server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - - memcpy(hmac_c_msg, - EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST, - strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST)); - memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST), - client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) + - EXT_OR_PORT_AUTH_NONCE_LEN, - server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); - - crypto_hmac_sha256(server_hash, - (char*)ext_or_auth_cookie, - EXT_OR_PORT_AUTH_COOKIE_LEN, - hmac_s_msg, - hmac_s_msg_len); - - crypto_hmac_sha256(correct_client_hash, - (char*)ext_or_auth_cookie, - EXT_OR_PORT_AUTH_COOKIE_LEN, - hmac_c_msg, - hmac_c_msg_len); - - /* Store the client hash we generated. We will need to compare it - with the hash sent by the client. */ - *client_hash_out = correct_client_hash; - - memwipe(hmac_s_msg, 0, hmac_s_msg_len); - memwipe(hmac_c_msg, 0, hmac_c_msg_len); - - tor_free(hmac_s_msg); - tor_free(hmac_c_msg); - } - - { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/ - char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1]; - char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; - char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1]; - - base16_encode(server_hash_encoded, sizeof(server_hash_encoded), - server_hash, sizeof(server_hash)); - base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded), - server_nonce, sizeof(server_nonce)); - base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded), - client_nonce, sizeof(client_nonce)); - - log_debug(LD_GENERAL, - "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'", - server_hash_encoded, server_nonce_encoded, client_nonce_encoded); - - memwipe(server_hash_encoded, 0, sizeof(server_hash_encoded)); - memwipe(server_nonce_encoded, 0, sizeof(server_nonce_encoded)); - memwipe(client_nonce_encoded, 0, sizeof(client_nonce_encoded)); - } - - { /* write reply: (server_hash, server_nonce) */ - - reply_len = EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN; - reply = tor_malloc_zero(reply_len); - memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN); - memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce, - EXT_OR_PORT_AUTH_NONCE_LEN); - } - - *reply_out = reply; - *reply_len_out = reply_len; - - return 0; -} - -/** Read the client's nonce out of conn, setup the safe-cookie - * crypto, and then send our own hash and nonce to the client - * - * Return -1 if there was an error; return 0 if we need more data in - * conn, and return 1 if we successfully retrieved the - * client's nonce and sent our own. */ -static int -connection_ext_or_auth_handle_client_nonce(connection_t *conn) -{ - char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN]; - char *reply=NULL; - size_t reply_len=0; - - if (!ext_or_auth_cookie_is_set) { /* this should not happen */ - log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. " - "That's weird since we should have done that on startup. " - "This might be a Tor bug, please file a bug report. "); - return -1; - } - - if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN) - return 0; - - if (connection_fetch_from_buf(client_nonce, - EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0) - return -1; - - /* We extract the ClientNonce from the received data, and use it to - calculate ServerHash and ServerNonce according to proposal 217. - - We also calculate our own ClientHash value and save it in the - connection state. We validate it later against the ClientHash - sent by the client. */ - if (handle_client_auth_nonce(client_nonce, sizeof(client_nonce), - &TO_OR_CONN(conn)->ext_or_auth_correct_client_hash, - &reply, &reply_len) < 0) - return -1; - - connection_write_to_buf(reply, reply_len, conn); - - memwipe(reply, 0, reply_len); - tor_free(reply); - - log_debug(LD_GENERAL, "Got client nonce, and sent our own nonce and hash."); - - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH; - return 1; -} - -#define connection_ext_or_auth_send_result_success(c) \ - connection_ext_or_auth_send_result(c, 1) -#define connection_ext_or_auth_send_result_fail(c) \ - connection_ext_or_auth_send_result(c, 0) - -/** Send authentication results to conn. Successful results if - * success is set; failure results otherwise. */ -static void -connection_ext_or_auth_send_result(connection_t *conn, int success) -{ - if (success) - connection_write_to_buf("\x01", 1, conn); - else - connection_write_to_buf("\x00", 1, conn); -} - -/** Receive the client's hash from conn, validate that it's - * correct, and then send the authentication results to the client. - * - * Return -1 if there was an error during validation; return 0 if we - * need more data in conn, and return 1 if we successfully - * validated the client's hash and sent a happy authentication - * result. */ -static int -connection_ext_or_auth_handle_client_hash(connection_t *conn) -{ - char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0}; - - if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN) - return 0; - - if (connection_fetch_from_buf(provided_client_hash, - EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0) - return -1; - - if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash, - provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) { - log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed."); - connection_ext_or_auth_send_result_fail(conn); - return -1; - } - - log_debug(LD_GENERAL, "Got client's hash and it was legit."); - - /* send positive auth result */ - connection_ext_or_auth_send_result_success(conn); - conn->state = EXT_OR_CONN_STATE_OPEN; - return 1; -} - -/** Handle data from or_conn received on Extended ORPort. - * Return -1 on error. 0 on unsufficient data. 1 on correct. */ -static int -connection_ext_or_auth_process_inbuf(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - - /* State transitions of the Extended ORPort authentication protocol: - - EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE (start state) -> - EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE -> - EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH -> - EXT_OR_CONN_STATE_OPEN - - During EXT_OR_CONN_STATE_OPEN, data is handled by - connection_ext_or_process_inbuf(). - */ - - switch (conn->state) { /* Functionify */ - case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE: - return connection_ext_or_auth_neg_auth_type(conn); - - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE: - return connection_ext_or_auth_handle_client_nonce(conn); - - case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH: - return connection_ext_or_auth_handle_client_hash(conn); - - default: - log_warn(LD_BUG, "Encountered unexpected connection state %d while trying " - "to process Extended ORPort authentication data.", conn->state); - return -1; - } -} - -/** Extended ORPort commands (Transport-to-Bridge) */ -#define EXT_OR_CMD_TB_DONE 0x0000 -#define EXT_OR_CMD_TB_USERADDR 0x0001 -#define EXT_OR_CMD_TB_TRANSPORT 0x0002 - -/** Extended ORPort commands (Bridge-to-Transport) */ -#define EXT_OR_CMD_BT_OKAY 0x1000 -#define EXT_OR_CMD_BT_DENY 0x1001 -#define EXT_OR_CMD_BT_CONTROL 0x1002 - -/** Process a USERADDR command from the Extended - * ORPort. payload is a payload of size len. - * - * If the USERADDR command was well formed, change the address of - * conn to the address on the USERADDR command. - * - * Return 0 on success and -1 on error. */ -static int -connection_ext_or_handle_cmd_useraddr(connection_t *conn, - const char *payload, uint16_t len) -{ - /* Copy address string. */ - tor_addr_t addr; - uint16_t port; - char *addr_str; - char *address_part=NULL; - int res; - if (memchr(payload, '\0', len)) { - log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort UserAddr"); - return -1; - } - - addr_str = tor_memdup_nulterm(payload, len); - - res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port); - tor_free(addr_str); - if (res<0) - return -1; - - res = tor_addr_parse(&addr, address_part); - tor_free(address_part); - if (res<0) - return -1; - - { /* do some logging */ - char *old_address = tor_dup_addr(&conn->addr); - char *new_address = tor_dup_addr(&addr); - - log_debug(LD_NET, "Received USERADDR." - "We rewrite our address from '%s:%u' to '%s:%u'.", - safe_str(old_address), conn->port, safe_str(new_address), port); - - tor_free(old_address); - tor_free(new_address); - } - - /* record the address */ - tor_addr_copy(&conn->addr, &addr); - conn->port = port; - if (conn->address) { - tor_free(conn->address); - } - conn->address = tor_dup_addr(&addr); - - return 0; -} - -/** Process a TRANSPORT command from the Extended - * ORPort. payload is a payload of size len. - * - * If the TRANSPORT command was well formed, register the name of the - * transport on conn. - * - * Return 0 on success and -1 on error. */ -static int -connection_ext_or_handle_cmd_transport(or_connection_t *conn, - const char *payload, uint16_t len) -{ - char *transport_str; - if (memchr(payload, '\0', len)) { - log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort Transport"); - return -1; - } - - transport_str = tor_memdup_nulterm(payload, len); - - /* Transport names MUST be C-identifiers. */ - if (!string_is_C_identifier(transport_str)) { - tor_free(transport_str); - return -1; - } - - /* If ext_or_transport is already occupied (because the PT sent two - * TRANSPORT commands), deallocate the old name and keep the new - * one */ - if (conn->ext_or_transport) - tor_free(conn->ext_or_transport); - - conn->ext_or_transport = transport_str; - return 0; -} - -#define EXT_OR_CONN_STATE_IS_AUTHENTICATING(st) \ - ((st) <= EXT_OR_CONN_STATE_AUTH_MAX) - -/** Process Extended ORPort messages from or_conn. */ -int -connection_ext_or_process_inbuf(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - ext_or_cmd_t *command; - int r; - - /* DOCDOC Document the state machine and transitions in this function */ - - /* If we are still in the authentication stage, process traffic as - authentication data: */ - while (EXT_OR_CONN_STATE_IS_AUTHENTICATING(conn->state)) { - log_debug(LD_GENERAL, "Got Extended ORPort authentication data (%u).", - (unsigned int) connection_get_inbuf_len(conn)); - r = connection_ext_or_auth_process_inbuf(or_conn); - if (r < 0) { - connection_mark_for_close(conn); - return -1; - } else if (r == 0) { - return 0; - } - /* if r > 0, loop and process more data (if any). */ - } - - while (1) { - log_debug(LD_GENERAL, "Got Extended ORPort data."); - command = NULL; - r = connection_fetch_ext_or_cmd_from_buf(conn, &command); - if (r < 0) - goto err; - else if (r == 0) - return 0; /* need to wait for more data */ - - /* Got a command! */ - tor_assert(command); - - if (command->cmd == EXT_OR_CMD_TB_DONE) { - if (connection_get_inbuf_len(conn)) { - /* The inbuf isn't empty; the client is misbehaving. */ - goto err; - } - - log_debug(LD_NET, "Received DONE."); - - /* If the transport proxy did not use the TRANSPORT command to - * specify the transport name, mark this as unknown transport. */ - if (!or_conn->ext_or_transport) { - /* We write this string this way to avoid ??>, which is a C - * trigraph. */ - or_conn->ext_or_transport = tor_strdup(""); - } - - connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0); - - /* can't transition immediately; need to flush first. */ - conn->state = EXT_OR_CONN_STATE_FLUSHING; - connection_stop_reading(conn); - } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) { - if (connection_ext_or_handle_cmd_useraddr(conn, - command->body, command->len) < 0) - goto err; - } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) { - if (connection_ext_or_handle_cmd_transport(or_conn, - command->body, command->len) < 0) - goto err; - } else { - log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).", - command->cmd); - } - - ext_or_cmd_free(command); - } - - return 0; - - err: - ext_or_cmd_free(command); - connection_mark_for_close(conn); - return -1; -} - -/** conn finished flushing Extended ORPort messages to the - * network, and is now ready to accept OR traffic. This function - * does the transition. */ -int -connection_ext_or_finished_flushing(or_connection_t *conn) -{ - if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) { - connection_start_reading(TO_CONN(conn)); - connection_ext_or_transition(conn); - } - return 0; -} - -/** Initiate Extended ORPort authentication, by sending the list of - * supported authentication types to the client. */ -int -connection_ext_or_start_auth(or_connection_t *or_conn) -{ - connection_t *conn = TO_CONN(or_conn); - const uint8_t authtypes[] = { - /* We only support authtype '1' for now. */ - EXT_OR_AUTHTYPE_SAFECOOKIE, - /* Marks the end of the list. */ - 0 - }; - - log_debug(LD_GENERAL, - "ExtORPort authentication: Sending supported authentication types"); - - connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn); - conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE; - - return 0; -} - -/** Free any leftover allocated memory of the ext_orport.c subsystem. */ -void -ext_orport_free_all(void) -{ - if (ext_or_auth_cookie) /* Free the auth cookie */ - tor_free(ext_or_auth_cookie); -} - diff --git a/src/tor/ext_orport.h b/src/tor/ext_orport.h deleted file mode 100644 index ce45e5f..0000000 --- a/src/tor/ext_orport.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef EXT_ORPORT_H -#define EXT_ORPORT_H - -int connection_ext_or_start_auth(or_connection_t *or_conn); - -ext_or_cmd_t *ext_or_cmd_new(uint16_t len); -void ext_or_cmd_free(ext_or_cmd_t *cmd); -void connection_or_set_ext_or_identifier(or_connection_t *conn); -void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); -void connection_or_clear_ext_or_id_map(void); -or_connection_t *connection_or_get_by_ext_or_id(const char *id); - -int connection_ext_or_finished_flushing(or_connection_t *conn); -int connection_ext_or_process_inbuf(or_connection_t *or_conn); - -int init_ext_or_cookie_authentication(int is_enabled); -char *get_ext_or_auth_cookie_file_name(void); -void ext_orport_free_all(void); - -#ifdef EXT_ORPORT_PRIVATE -STATIC int connection_write_ext_or_command(connection_t *conn, - uint16_t command, - const char *body, - size_t bodylen); -STATIC int handle_client_auth_nonce(const char *client_nonce, - size_t client_nonce_len, - char **client_hash_out, - char **reply_out, size_t *reply_len_out); -#ifdef TOR_UNIT_TESTS -extern uint8_t *ext_or_auth_cookie; -extern int ext_or_auth_cookie_is_set; -#endif -#endif - -#endif - diff --git a/src/tor/fp_pair.c b/src/tor/fp_pair.c deleted file mode 100644 index 4d8a835..0000000 --- a/src/tor/fp_pair.c +++ /dev/null @@ -1,308 +0,0 @@ -/* Copyright (c) 2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "fp_pair.h" - -/* Define fp_pair_map_t structures */ - -struct fp_pair_map_entry_s { - HT_ENTRY(fp_pair_map_entry_s) node; - void *val; - fp_pair_t key; -}; - -struct fp_pair_map_s { - HT_HEAD(fp_pair_map_impl, fp_pair_map_entry_s) head; -}; - -/* - * Hash function and equality checker for fp_pair_map_t - */ - -/** Compare fp_pair_entry_t objects by key value. */ -static INLINE int -fp_pair_map_entries_eq(const fp_pair_map_entry_t *a, - const fp_pair_map_entry_t *b) -{ - return tor_memeq(&(a->key), &(b->key), sizeof(fp_pair_t)); -} - -/** Return a hash value for an fp_pair_entry_t. */ -static INLINE unsigned int -fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) -{ - const uint32_t *p; - unsigned int hash; - - p = (const uint32_t *)(a->key.first); - /* Hashes are 20 bytes long, so 5 times uint32_t */ - hash = p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; - /* Now XOR in the second fingerprint */ - p = (const uint32_t *)(a->key.second); - hash ^= p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; - - return hash; -} - -/* - * Hash table functions for fp_pair_map_t - */ - -HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_s, node, - fp_pair_map_entry_hash, fp_pair_map_entries_eq) -HT_GENERATE(fp_pair_map_impl, fp_pair_map_entry_s, node, - fp_pair_map_entry_hash, fp_pair_map_entries_eq, - 0.6, tor_malloc, tor_realloc, tor_free) - -/** Constructor to create a new empty map from fp_pair_t to void * - */ - -fp_pair_map_t * -fp_pair_map_new(void) -{ - fp_pair_map_t *result; - - result = tor_malloc(sizeof(fp_pair_map_t)); - HT_INIT(fp_pair_map_impl, &result->head); - return result; -} - -/** Set the current value for key to val; returns the previous - * value for key if one was set, or NULL if one was not. - */ - -void * -fp_pair_map_set(fp_pair_map_t *map, const fp_pair_t *key, void *val) -{ - fp_pair_map_entry_t *resolve; - fp_pair_map_entry_t search; - void *oldval; - - tor_assert(map); - tor_assert(key); - tor_assert(val); - - memcpy(&(search.key), key, sizeof(*key)); - resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); - if (resolve) { - oldval = resolve->val; - resolve->val = val; - } else { - resolve = tor_malloc_zero(sizeof(fp_pair_map_entry_t)); - memcpy(&(resolve->key), key, sizeof(*key)); - resolve->val = val; - HT_INSERT(fp_pair_map_impl, &(map->head), resolve); - oldval = NULL; - } - - return oldval; -} - -/** Set the current value for the key (first, second) to val; returns - * the previous value for key if one was set, or NULL if one was not. - */ - -void * -fp_pair_map_set_by_digests(fp_pair_map_t *map, - const char *first, const char *second, - void *val) -{ - fp_pair_t k; - - tor_assert(first); - tor_assert(second); - - memcpy(k.first, first, DIGEST_LEN); - memcpy(k.second, second, DIGEST_LEN); - - return fp_pair_map_set(map, &k, val); -} - -/** Return the current value associated with key, or NULL if no value is set. - */ - -void * -fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key) -{ - fp_pair_map_entry_t *resolve; - fp_pair_map_entry_t search; - void *val = NULL; - - tor_assert(map); - tor_assert(key); - - memcpy(&(search.key), key, sizeof(*key)); - resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); - if (resolve) val = resolve->val; - - return val; -} - -/** Return the current value associated the key (first, second), or - * NULL if no value is set. - */ - -void * -fp_pair_map_get_by_digests(const fp_pair_map_t *map, - const char *first, const char *second) -{ - fp_pair_t k; - - tor_assert(first); - tor_assert(second); - - memcpy(k.first, first, DIGEST_LEN); - memcpy(k.second, second, DIGEST_LEN); - - return fp_pair_map_get(map, &k); -} - -/** Remove the value currently associated with key from the map. - * Return the value if one was set, or NULL if there was no entry for - * key. The caller must free any storage associated with the - * returned value. - */ - -void * -fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) -{ - fp_pair_map_entry_t *resolve; - fp_pair_map_entry_t search; - void *val = NULL; - - tor_assert(map); - tor_assert(key); - - memcpy(&(search.key), key, sizeof(*key)); - resolve = HT_REMOVE(fp_pair_map_impl, &(map->head), &search); - if (resolve) { - val = resolve->val; - tor_free(resolve); - } - - return val; -} - -/** Remove all entries from map, and deallocate storage for those entries. - * If free_val is provided, it is invoked on every value in map. - */ - -void -fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) -{ - fp_pair_map_entry_t **ent, **next, *this; - - if (map) { - for (ent = HT_START(fp_pair_map_impl, &(map->head)); - ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), ent); - if (free_val) free_val(this->val); - tor_free(this); - } - tor_assert(HT_EMPTY(&(map->head))); - HT_CLEAR(fp_pair_map_impl, &(map->head)); - tor_free(map); - } -} - -/** Return true iff map has no entries. - */ - -int -fp_pair_map_isempty(const fp_pair_map_t *map) -{ - tor_assert(map); - - return HT_EMPTY(&(map->head)); -} - -/** Return the number of items in map. - */ - -int -fp_pair_map_size(const fp_pair_map_t *map) -{ - tor_assert(map); - - return HT_SIZE(&(map->head)); -} - -/** return an iterator pointing to the start of map. - */ - -fp_pair_map_iter_t * -fp_pair_map_iter_init(fp_pair_map_t *map) -{ - tor_assert(map); - - return HT_START(fp_pair_map_impl, &(map->head)); -} - -/** Advance iter a single step to the next entry of map, and return - * its new value. - */ - -fp_pair_map_iter_t * -fp_pair_map_iter_next(fp_pair_map_t *map, fp_pair_map_iter_t *iter) -{ - tor_assert(map); - tor_assert(iter); - - return HT_NEXT(fp_pair_map_impl, &(map->head), iter); -} - -/** Advance iter a single step to the next entry of map, removing the current - * entry, and return its new value. - */ - -fp_pair_map_iter_t * -fp_pair_map_iter_next_rmv(fp_pair_map_t *map, fp_pair_map_iter_t *iter) -{ - fp_pair_map_entry_t *rmv; - - tor_assert(map); - tor_assert(iter); - tor_assert(*iter); - - rmv = *iter; - iter = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), iter); - tor_free(rmv); - - return iter; -} - -/** Set *key_out and *val_out to the current entry pointed to by iter. - */ - -void -fp_pair_map_iter_get(fp_pair_map_iter_t *iter, - fp_pair_t *key_out, void **val_out) -{ - tor_assert(iter); - tor_assert(*iter); - - if (key_out) memcpy(key_out, &((*iter)->key), sizeof(fp_pair_t)); - if (val_out) *val_out = (*iter)->val; -} - -/** Return true iff iter has advanced past the last entry of its map. - */ - -int -fp_pair_map_iter_done(fp_pair_map_iter_t *iter) -{ - return (iter == NULL); -} - -/** Assert if anything has gone wrong with the internal - * representation of map. - */ - -void -fp_pair_map_assert_ok(const fp_pair_map_t *map) -{ - tor_assert(!fp_pair_map_impl_HT_REP_IS_BAD_(&(map->head))); -} - diff --git a/src/tor/fp_pair.h b/src/tor/fp_pair.h deleted file mode 100644 index 89f664a..0000000 --- a/src/tor/fp_pair.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file fp_pair.h - * \brief Header file for fp_pair.c. - **/ - -#ifndef _TOR_FP_PAIR_H -#define _TOR_FP_PAIR_H - -/* - * Declare fp_pair_map_t functions and structs - */ - -typedef struct fp_pair_map_entry_s fp_pair_map_entry_t; -typedef struct fp_pair_map_s fp_pair_map_t; -typedef fp_pair_map_entry_t *fp_pair_map_iter_t; - -fp_pair_map_t * fp_pair_map_new(void); -void * fp_pair_map_set(fp_pair_map_t *map, const fp_pair_t *key, void *val); -void * fp_pair_map_set_by_digests(fp_pair_map_t *map, - const char *first, const char *second, - void *val); -void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key); -void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, - const char *first, const char *second); -void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key); -void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)); -int fp_pair_map_isempty(const fp_pair_map_t *map); -int fp_pair_map_size(const fp_pair_map_t *map); -fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map); -fp_pair_map_iter_t * fp_pair_map_iter_next(fp_pair_map_t *map, - fp_pair_map_iter_t *iter); -fp_pair_map_iter_t * fp_pair_map_iter_next_rmv(fp_pair_map_t *map, - fp_pair_map_iter_t *iter); -void fp_pair_map_iter_get(fp_pair_map_iter_t *iter, - fp_pair_t *key_out, void **val_out); -int fp_pair_map_iter_done(fp_pair_map_iter_t *iter); -void fp_pair_map_assert_ok(const fp_pair_map_t *map); - -#undef DECLARE_MAP_FNS - -#endif - diff --git a/src/tor/geoip.c b/src/tor/geoip.c deleted file mode 100644 index dc4730c..0000000 --- a/src/tor/geoip.c +++ /dev/null @@ -1,1684 +0,0 @@ -/* Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file geoip.c - * \brief Functions related to maintaining an IP-to-country database; - * to summarizing client connections by country to entry guards, bridges, - * and directory servers; and for statistics on answering network status - * requests. - */ - -#define GEOIP_PRIVATE -#include "or.h" -#include "ht.h" -#include "config.h" -#include "control.h" -#include "dnsserv.h" -#include "geoip.h" -#include "routerlist.h" - -static void clear_geoip_db(void); -static void init_geoip_countries(void); - -/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */ -typedef struct geoip_ipv4_entry_t { - uint32_t ip_low; /**< The lowest IP in the range, in host order */ - uint32_t ip_high; /**< The highest IP in the range, in host order */ - intptr_t country; /**< An index into geoip_countries */ -} geoip_ipv4_entry_t; - -/** An entry from the GeoIP IPv6 file: maps an IPv6 range to a country. */ -typedef struct geoip_ipv6_entry_t { - struct in6_addr ip_low; /**< The lowest IP in the range, in host order */ - struct in6_addr ip_high; /**< The highest IP in the range, in host order */ - intptr_t country; /**< An index into geoip_countries */ -} geoip_ipv6_entry_t; - -/** A per-country record for GeoIP request history. */ -typedef struct geoip_country_t { - char countrycode[3]; - uint32_t n_v3_ns_requests; -} geoip_country_t; - -/** A list of geoip_country_t */ -static smartlist_t *geoip_countries = NULL; -/** A map from lowercased country codes to their position in geoip_countries. - * The index is encoded in the pointer, and 1 is added so that NULL can mean - * not found. */ -static strmap_t *country_idxplus1_by_lc_code = NULL; -/** Lists of all known geoip_ipv4_entry_t and geoip_ipv6_entry_t, sorted - * by their respective ip_low. */ -static smartlist_t *geoip_ipv4_entries = NULL, *geoip_ipv6_entries = NULL; - -/** SHA1 digest of the GeoIP files to include in extra-info descriptors. */ -static char geoip_digest[DIGEST_LEN]; -static char geoip6_digest[DIGEST_LEN]; - -/** Return the index of the country's entry in the GeoIP - * country list if it is a valid 2-letter country code, otherwise - * return -1. */ -country_t -geoip_get_country(const char *country) -{ - void *idxplus1_; - intptr_t idx; - - idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); - if (!idxplus1_) - return -1; - - idx = ((uintptr_t)idxplus1_)-1; - return (country_t)idx; -} - -/** Add an entry to a GeoIP table, mapping all IP addresses between low - * and high, inclusive, to the 2-letter country code country. */ -static void -geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high, - const char *country) -{ - intptr_t idx; - void *idxplus1_; - - if (tor_addr_family(low) != tor_addr_family(high)) - return; - if (tor_addr_compare(high, low, CMP_EXACT) < 0) - return; - - idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); - - if (!idxplus1_) { - geoip_country_t *c = tor_malloc_zero(sizeof(geoip_country_t)); - strlcpy(c->countrycode, country, sizeof(c->countrycode)); - tor_strlower(c->countrycode); - smartlist_add(geoip_countries, c); - idx = smartlist_len(geoip_countries) - 1; - strmap_set_lc(country_idxplus1_by_lc_code, country, (void*)(idx+1)); - } else { - idx = ((uintptr_t)idxplus1_)-1; - } - { - geoip_country_t *c = smartlist_get(geoip_countries, idx); - tor_assert(!strcasecmp(c->countrycode, country)); - } - - if (tor_addr_family(low) == AF_INET) { - geoip_ipv4_entry_t *ent = tor_malloc_zero(sizeof(geoip_ipv4_entry_t)); - ent->ip_low = tor_addr_to_ipv4h(low); - ent->ip_high = tor_addr_to_ipv4h(high); - ent->country = idx; - smartlist_add(geoip_ipv4_entries, ent); - } else if (tor_addr_family(low) == AF_INET6) { - geoip_ipv6_entry_t *ent = tor_malloc_zero(sizeof(geoip_ipv6_entry_t)); - ent->ip_low = *tor_addr_to_in6(low); - ent->ip_high = *tor_addr_to_in6(high); - ent->country = idx; - smartlist_add(geoip_ipv6_entries, ent); - } -} - -/** Add an entry to the GeoIP table indicated by family, - * parsing it from line. The format is as for geoip_load_file(). */ -STATIC int -geoip_parse_entry(const char *line, sa_family_t family) -{ - tor_addr_t low_addr, high_addr; - char c[3]; - char *country = NULL; - - if (!geoip_countries) - init_geoip_countries(); - if (family == AF_INET) { - if (!geoip_ipv4_entries) - geoip_ipv4_entries = smartlist_new(); - } else if (family == AF_INET6) { - if (!geoip_ipv6_entries) - geoip_ipv6_entries = smartlist_new(); - } else { - log_warn(LD_GENERAL, "Unsupported family: %d", family); - return -1; - } - - while (TOR_ISSPACE(*line)) - ++line; - if (*line == '#') - return 0; - - if (family == AF_INET) { - unsigned int low, high; - if (tor_sscanf(line,"%u,%u,%2s", &low, &high, c) == 3 || - tor_sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, c) == 3) { - tor_addr_from_ipv4h(&low_addr, low); - tor_addr_from_ipv4h(&high_addr, high); - } else - goto fail; - country = c; - } else { /* AF_INET6 */ - char buf[512]; - char *low_str, *high_str; - struct in6_addr low, high; - char *strtok_state; - strlcpy(buf, line, sizeof(buf)); - low_str = tor_strtok_r(buf, ",", &strtok_state); - if (!low_str) - goto fail; - high_str = tor_strtok_r(NULL, ",", &strtok_state); - if (!high_str) - goto fail; - country = tor_strtok_r(NULL, "\n", &strtok_state); - if (!country) - goto fail; - if (strlen(country) != 2) - goto fail; - if (tor_inet_pton(AF_INET6, low_str, &low) <= 0) - goto fail; - tor_addr_from_in6(&low_addr, &low); - if (tor_inet_pton(AF_INET6, high_str, &high) <= 0) - goto fail; - tor_addr_from_in6(&high_addr, &high); - } - geoip_add_entry(&low_addr, &high_addr, country); - return 0; - - fail: - log_warn(LD_GENERAL, "Unable to parse line from GEOIP %s file: %s", - family == AF_INET ? "IPv4" : "IPv6", escaped(line)); - return -1; -} - -/** Sorting helper: return -1, 1, or 0 based on comparison of two - * geoip_ipv4_entry_t */ -static int -geoip_ipv4_compare_entries_(const void **_a, const void **_b) -{ - const geoip_ipv4_entry_t *a = *_a, *b = *_b; - if (a->ip_low < b->ip_low) - return -1; - else if (a->ip_low > b->ip_low) - return 1; - else - return 0; -} - -/** bsearch helper: return -1, 1, or 0 based on comparison of an IP (a pointer - * to a uint32_t in host order) to a geoip_ipv4_entry_t */ -static int -geoip_ipv4_compare_key_to_entry_(const void *_key, const void **_member) -{ - /* No alignment issue here, since _key really is a pointer to uint32_t */ - const uint32_t addr = *(uint32_t *)_key; - const geoip_ipv4_entry_t *entry = *_member; - if (addr < entry->ip_low) - return -1; - else if (addr > entry->ip_high) - return 1; - else - return 0; -} - -/** Sorting helper: return -1, 1, or 0 based on comparison of two - * geoip_ipv6_entry_t */ -static int -geoip_ipv6_compare_entries_(const void **_a, const void **_b) -{ - const geoip_ipv6_entry_t *a = *_a, *b = *_b; - return fast_memcmp(a->ip_low.s6_addr, b->ip_low.s6_addr, - sizeof(struct in6_addr)); -} - -/** bsearch helper: return -1, 1, or 0 based on comparison of an IPv6 - * (a pointer to a in6_addr) to a geoip_ipv6_entry_t */ -static int -geoip_ipv6_compare_key_to_entry_(const void *_key, const void **_member) -{ - const struct in6_addr *addr = (struct in6_addr *)_key; - const geoip_ipv6_entry_t *entry = *_member; - - if (fast_memcmp(addr->s6_addr, entry->ip_low.s6_addr, - sizeof(struct in6_addr)) < 0) - return -1; - else if (fast_memcmp(addr->s6_addr, entry->ip_high.s6_addr, - sizeof(struct in6_addr)) > 0) - return 1; - else - return 0; -} - -/** Return 1 if we should collect geoip stats on bridge users, and - * include them in our extrainfo descriptor. Else return 0. */ -int -should_record_bridge_info(const or_options_t *options) -{ - return options->BridgeRelay && options->BridgeRecordUsageByCountry; -} - -/** Set up a new list of geoip countries with no countries (yet) set in it, - * except for the unknown country. - */ -static void -init_geoip_countries(void) -{ - geoip_country_t *geoip_unresolved; - geoip_countries = smartlist_new(); - /* Add a geoip_country_t for requests that could not be resolved to a - * country as first element (index 0) to geoip_countries. */ - geoip_unresolved = tor_malloc_zero(sizeof(geoip_country_t)); - strlcpy(geoip_unresolved->countrycode, "??", - sizeof(geoip_unresolved->countrycode)); - smartlist_add(geoip_countries, geoip_unresolved); - country_idxplus1_by_lc_code = strmap_new(); - strmap_set_lc(country_idxplus1_by_lc_code, "??", (void*)(1)); -} - -/** Clear appropriate GeoIP database, based on family, and - * reload it from the file filename. Return 0 on success, -1 on - * failure. - * - * Recognized line formats for IPv4 are: - * INTIPLOW,INTIPHIGH,CC - * and - * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME" - * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned - * integers, and CC is a country code. - * - * Recognized line format for IPv6 is: - * IPV6LOW,IPV6HIGH,CC - * where IPV6LOW and IPV6HIGH are IPv6 addresses and CC is a country code. - * - * It also recognizes, and skips over, blank lines and lines that start - * with '#' (comments). - */ -int -geoip_load_file(sa_family_t family, const char *filename) -{ - FILE *f; - const char *msg = ""; - const or_options_t *options = get_options(); - int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO; - crypto_digest_t *geoip_digest_env = NULL; - - tor_assert(family == AF_INET || family == AF_INET6); - - if (!(f = tor_fopen_cloexec(filename, "r"))) { - log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s", - filename, msg); - return -1; - } - if (!geoip_countries) - init_geoip_countries(); - - if (family == AF_INET) { - if (geoip_ipv4_entries) { - SMARTLIST_FOREACH(geoip_ipv4_entries, geoip_ipv4_entry_t *, e, - tor_free(e)); - smartlist_free(geoip_ipv4_entries); - } - geoip_ipv4_entries = smartlist_new(); - } else { /* AF_INET6 */ - if (geoip_ipv6_entries) { - SMARTLIST_FOREACH(geoip_ipv6_entries, geoip_ipv6_entry_t *, e, - tor_free(e)); - smartlist_free(geoip_ipv6_entries); - } - geoip_ipv6_entries = smartlist_new(); - } - geoip_digest_env = crypto_digest_new(); - - log_notice(LD_GENERAL, "Parsing GEOIP %s file %s.", - (family == AF_INET) ? "IPv4" : "IPv6", filename); - while (!feof(f)) { - char buf[512]; - if (fgets(buf, (int)sizeof(buf), f) == NULL) - break; - crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf)); - /* FFFF track full country name. */ - geoip_parse_entry(buf, family); - } - /*XXXX abort and return -1 if no entries/illformed?*/ - fclose(f); - - /* Sort list and remember file digests so that we can include it in - * our extra-info descriptors. */ - if (family == AF_INET) { - smartlist_sort(geoip_ipv4_entries, geoip_ipv4_compare_entries_); - /* Okay, now we need to maybe change our mind about what is in - * which country. We do this for IPv4 only since that's what we - * store in node->country. */ - refresh_all_country_info(); - crypto_digest_get_digest(geoip_digest_env, geoip_digest, DIGEST_LEN); - } else { - /* AF_INET6 */ - smartlist_sort(geoip_ipv6_entries, geoip_ipv6_compare_entries_); - crypto_digest_get_digest(geoip_digest_env, geoip6_digest, DIGEST_LEN); - } - crypto_digest_free(geoip_digest_env); - - return 0; -} - -/** Given an IP address in host order, return a number representing the - * country to which that address belongs, -1 for "No geoip information - * available", or 0 for the 'unknown country'. The return value will always - * be less than geoip_get_n_countries(). To decode it, call - * geoip_get_country_name(). - */ -STATIC int -geoip_get_country_by_ipv4(uint32_t ipaddr) -{ - geoip_ipv4_entry_t *ent; - if (!geoip_ipv4_entries) - return -1; - ent = smartlist_bsearch(geoip_ipv4_entries, &ipaddr, - geoip_ipv4_compare_key_to_entry_); - return ent ? (int)ent->country : 0; -} - -/** Given an IPv6 address, return a number representing the country to - * which that address belongs, -1 for "No geoip information available", or - * 0 for the 'unknown country'. The return value will always be less than - * geoip_get_n_countries(). To decode it, call geoip_get_country_name(). - */ -STATIC int -geoip_get_country_by_ipv6(const struct in6_addr *addr) -{ - geoip_ipv6_entry_t *ent; - - if (!geoip_ipv6_entries) - return -1; - ent = smartlist_bsearch(geoip_ipv6_entries, addr, - geoip_ipv6_compare_key_to_entry_); - return ent ? (int)ent->country : 0; -} - -/** Given an IP address, return a number representing the country to which - * that address belongs, -1 for "No geoip information available", or 0 for - * the 'unknown country'. The return value will always be less than - * geoip_get_n_countries(). To decode it, call geoip_get_country_name(). - */ -int -geoip_get_country_by_addr(const tor_addr_t *addr) -{ - if (tor_addr_family(addr) == AF_INET) { - return geoip_get_country_by_ipv4(tor_addr_to_ipv4h(addr)); - } else if (tor_addr_family(addr) == AF_INET6) { - return geoip_get_country_by_ipv6(tor_addr_to_in6(addr)); - } else { - return -1; - } -} - -/** Return the number of countries recognized by the GeoIP country list. */ -int -geoip_get_n_countries(void) -{ - if (!geoip_countries) - init_geoip_countries(); - return (int) smartlist_len(geoip_countries); -} - -/** Return the two-letter country code associated with the number num, - * or "??" for an unknown value. */ -const char * -geoip_get_country_name(country_t num) -{ - if (geoip_countries && num >= 0 && num < smartlist_len(geoip_countries)) { - geoip_country_t *c = smartlist_get(geoip_countries, num); - return c->countrycode; - } else - return "??"; -} - -/** Return true iff we have loaded a GeoIP database.*/ -int -geoip_is_loaded(sa_family_t family) -{ - tor_assert(family == AF_INET || family == AF_INET6); - if (geoip_countries == NULL) - return 0; - if (family == AF_INET) - return geoip_ipv4_entries != NULL; - else /* AF_INET6 */ - return geoip_ipv6_entries != NULL; -} - -/** Return the hex-encoded SHA1 digest of the loaded GeoIP file. The - * result does not need to be deallocated, but will be overwritten by the - * next call of hex_str(). */ -const char * -geoip_db_digest(sa_family_t family) -{ - tor_assert(family == AF_INET || family == AF_INET6); - if (family == AF_INET) - return hex_str(geoip_digest, DIGEST_LEN); - else /* AF_INET6 */ - return hex_str(geoip6_digest, DIGEST_LEN); -} - -/** Entry in a map from IP address to the last time we've seen an incoming - * connection from that IP address. Used by bridges only, to track which - * countries have them blocked. */ -typedef struct clientmap_entry_t { - HT_ENTRY(clientmap_entry_t) node; - tor_addr_t addr; - /* Name of pluggable transport used by this client. NULL if no - pluggable transport was used. */ - char *transport_name; - - /** Time when we last saw this IP address, in MINUTES since the epoch. - * - * (This will run out of space around 4011 CE. If Tor is still in use around - * 4000 CE, please remember to add more bits to last_seen_in_minutes.) */ - unsigned int last_seen_in_minutes:30; - unsigned int action:2; -} clientmap_entry_t; - -/** Largest allowable value for last_seen_in_minutes. (It's a 30-bit field, - * so it can hold up to (1u<<30)-1, or 0x3fffffffu. - */ -#define MAX_LAST_SEEN_IN_MINUTES 0X3FFFFFFFu - -/** Map from client IP address to last time seen. */ -static HT_HEAD(clientmap, clientmap_entry_t) client_history = - HT_INITIALIZER(); - -/** Hashtable helper: compute a hash of a clientmap_entry_t. */ -static INLINE unsigned -clientmap_entry_hash(const clientmap_entry_t *a) -{ - unsigned h = tor_addr_hash(&a->addr); - if (a->transport_name) - h += ht_string_hash(a->transport_name); - return ht_improve_hash(h); -} -/** Hashtable helper: compare two clientmap_entry_t values for equality. */ -static INLINE int -clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b) -{ - if (strcmp_opt(a->transport_name, b->transport_name)) - return 0; - - return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) && - a->action == b->action; -} - -HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, - clientmap_entries_eq); -HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, - clientmap_entries_eq, 0.6, malloc, realloc, free); - -/** Free all storage held by ent. */ -static void -clientmap_entry_free(clientmap_entry_t *ent) -{ - if (!ent) - return; - - tor_free(ent->transport_name); - tor_free(ent); -} - -/** Clear history of connecting clients used by entry and bridge stats. */ -static void -client_history_clear(void) -{ - clientmap_entry_t **ent, **next, *this; - for (ent = HT_START(clientmap, &client_history); ent != NULL; - ent = next) { - if ((*ent)->action == GEOIP_CLIENT_CONNECT) { - this = *ent; - next = HT_NEXT_RMV(clientmap, &client_history, ent); - clientmap_entry_free(this); - } else { - next = HT_NEXT(clientmap, &client_history, ent); - } - } -} - -/** Note that we've seen a client connect from the IP addr - * at time now. Ignored by all but bridges and directories if - * configured accordingly. */ -void -geoip_note_client_seen(geoip_client_action_t action, - const tor_addr_t *addr, - const char *transport_name, - time_t now) -{ - const or_options_t *options = get_options(); - clientmap_entry_t lookup, *ent; - memset(&lookup, 0, sizeof(clientmap_entry_t)); - - if (action == GEOIP_CLIENT_CONNECT) { - /* Only remember statistics as entry guard or as bridge. */ - if (!options->EntryStatistics && - (!(options->BridgeRelay && options->BridgeRecordUsageByCountry))) - return; - } else { - if (options->BridgeRelay || options->BridgeAuthoritativeDir || - !options->DirReqStatistics) - return; - } - - log_debug(LD_GENERAL, "Seen client from '%s' with transport '%s'.", - safe_str_client(fmt_addr((addr))), - transport_name ? transport_name : ""); - - tor_addr_copy(&lookup.addr, addr); - lookup.action = (int)action; - lookup.transport_name = (char*) transport_name; - ent = HT_FIND(clientmap, &client_history, &lookup); - - if (! ent) { - ent = tor_malloc_zero(sizeof(clientmap_entry_t)); - tor_addr_copy(&ent->addr, addr); - if (transport_name) - ent->transport_name = tor_strdup(transport_name); - ent->action = (int)action; - HT_INSERT(clientmap, &client_history, ent); - } - if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0) - ent->last_seen_in_minutes = (unsigned)(now/60); - else - ent->last_seen_in_minutes = 0; - - if (action == GEOIP_CLIENT_NETWORKSTATUS) { - int country_idx = geoip_get_country_by_addr(addr); - if (country_idx < 0) - country_idx = 0; /** unresolved requests are stored at index 0. */ - if (country_idx >= 0 && country_idx < smartlist_len(geoip_countries)) { - geoip_country_t *country = smartlist_get(geoip_countries, country_idx); - ++country->n_v3_ns_requests; - } - } -} - -/** HT_FOREACH helper: remove a clientmap_entry_t from the hashtable if it's - * older than a certain time. */ -static int -remove_old_client_helper_(struct clientmap_entry_t *ent, void *_cutoff) -{ - time_t cutoff = *(time_t*)_cutoff / 60; - if (ent->last_seen_in_minutes < cutoff) { - clientmap_entry_free(ent); - return 1; - } else { - return 0; - } -} - -/** Forget about all clients that haven't connected since cutoff. */ -void -geoip_remove_old_clients(time_t cutoff) -{ - clientmap_HT_FOREACH_FN(&client_history, - remove_old_client_helper_, - &cutoff); -} - -/** How many responses are we giving to clients requesting v3 network - * statuses? */ -static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM]; - -/** Note that we've rejected a client's request for a v3 network status - * for reason reason at time now. */ -void -geoip_note_ns_response(geoip_ns_response_t response) -{ - static int arrays_initialized = 0; - if (!get_options()->DirReqStatistics) - return; - if (!arrays_initialized) { - memset(ns_v3_responses, 0, sizeof(ns_v3_responses)); - arrays_initialized = 1; - } - tor_assert(response < GEOIP_NS_RESPONSE_NUM); - ns_v3_responses[response]++; -} - -/** Do not mention any country from which fewer than this number of IPs have - * connected. This conceivably avoids reporting information that could - * deanonymize users, though analysis is lacking. */ -#define MIN_IPS_TO_NOTE_COUNTRY 1 -/** Do not report any geoip data at all if we have fewer than this number of - * IPs to report about. */ -#define MIN_IPS_TO_NOTE_ANYTHING 1 -/** When reporting geoip data about countries, round up to the nearest - * multiple of this value. */ -#define IP_GRANULARITY 8 - -/** Helper type: used to sort per-country totals by value. */ -typedef struct c_hist_t { - char country[3]; /**< Two-letter country code. */ - unsigned total; /**< Total IP addresses seen in this country. */ -} c_hist_t; - -/** Sorting helper: return -1, 1, or 0 based on comparison of two - * geoip_ipv4_entry_t. Sort in descending order of total, and then by country - * code. */ -static int -c_hist_compare_(const void **_a, const void **_b) -{ - const c_hist_t *a = *_a, *b = *_b; - if (a->total > b->total) - return -1; - else if (a->total < b->total) - return 1; - else - return strcmp(a->country, b->country); -} - -/** When there are incomplete directory requests at the end of a 24-hour - * period, consider those requests running for longer than this timeout as - * failed, the others as still running. */ -#define DIRREQ_TIMEOUT (10*60) - -/** Entry in a map from either chan->global_identifier for direct requests - * or a unique circuit identifier for tunneled requests to request time, - * response size, and completion time of a network status request. Used to - * measure download times of requests to derive average client - * bandwidths. */ -typedef struct dirreq_map_entry_t { - HT_ENTRY(dirreq_map_entry_t) node; - /** Unique identifier for this network status request; this is either the - * chan->global_identifier of the dir channel (direct request) or a new - * locally unique identifier of a circuit (tunneled request). This ID is - * only unique among other direct or tunneled requests, respectively. */ - uint64_t dirreq_id; - unsigned int state:3; /**< State of this directory request. */ - unsigned int type:1; /**< Is this a direct or a tunneled request? */ - unsigned int completed:1; /**< Is this request complete? */ - /** When did we receive the request and started sending the response? */ - struct timeval request_time; - size_t response_size; /**< What is the size of the response in bytes? */ - struct timeval completion_time; /**< When did the request succeed? */ -} dirreq_map_entry_t; - -/** Map of all directory requests asking for v2 or v3 network statuses in - * the current geoip-stats interval. Values are - * of type *dirreq_map_entry_t. */ -static HT_HEAD(dirreqmap, dirreq_map_entry_t) dirreq_map = - HT_INITIALIZER(); - -static int -dirreq_map_ent_eq(const dirreq_map_entry_t *a, - const dirreq_map_entry_t *b) -{ - return a->dirreq_id == b->dirreq_id && a->type == b->type; -} - -/* DOCDOC dirreq_map_ent_hash */ -static unsigned -dirreq_map_ent_hash(const dirreq_map_entry_t *entry) -{ - unsigned u = (unsigned) entry->dirreq_id; - u += entry->type << 20; - return u; -} - -HT_PROTOTYPE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash, - dirreq_map_ent_eq); -HT_GENERATE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash, - dirreq_map_ent_eq, 0.6, malloc, realloc, free); - -/** Helper: Put entry into map of directory requests using - * type and dirreq_id as key parts. If there is - * already an entry for that key, print out a BUG warning and return. */ -static void -dirreq_map_put_(dirreq_map_entry_t *entry, dirreq_type_t type, - uint64_t dirreq_id) -{ - dirreq_map_entry_t *old_ent; - tor_assert(entry->type == type); - tor_assert(entry->dirreq_id == dirreq_id); - - /* XXXX we could switch this to HT_INSERT some time, since it seems that - * this bug doesn't happen. But since this function doesn't seem to be - * critical-path, it's sane to leave it alone. */ - old_ent = HT_REPLACE(dirreqmap, &dirreq_map, entry); - if (old_ent && old_ent != entry) { - log_warn(LD_BUG, "Error when putting directory request into local " - "map. There was already an entry for the same identifier."); - return; - } -} - -/** Helper: Look up and return an entry in the map of directory requests - * using type and dirreq_id as key parts. If there - * is no such entry, return NULL. */ -static dirreq_map_entry_t * -dirreq_map_get_(dirreq_type_t type, uint64_t dirreq_id) -{ - dirreq_map_entry_t lookup; - lookup.type = type; - lookup.dirreq_id = dirreq_id; - return HT_FIND(dirreqmap, &dirreq_map, &lookup); -} - -/** Note that an either direct or tunneled (see type) directory - * request for a v3 network status with unique ID dirreq_id of size - * response_size has started. */ -void -geoip_start_dirreq(uint64_t dirreq_id, size_t response_size, - dirreq_type_t type) -{ - dirreq_map_entry_t *ent; - if (!get_options()->DirReqStatistics) - return; - ent = tor_malloc_zero(sizeof(dirreq_map_entry_t)); - ent->dirreq_id = dirreq_id; - tor_gettimeofday(&ent->request_time); - ent->response_size = response_size; - ent->type = type; - dirreq_map_put_(ent, type, dirreq_id); -} - -/** Change the state of the either direct or tunneled (see type) - * directory request with dirreq_id to new_state and - * possibly mark it as completed. If no entry can be found for the given - * key parts (e.g., if this is a directory request that we are not - * measuring, or one that was started in the previous measurement period), - * or if the state cannot be advanced to new_state, do nothing. */ -void -geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type, - dirreq_state_t new_state) -{ - dirreq_map_entry_t *ent; - if (!get_options()->DirReqStatistics) - return; - ent = dirreq_map_get_(type, dirreq_id); - if (!ent) - return; - if (new_state == DIRREQ_IS_FOR_NETWORK_STATUS) - return; - if (new_state - 1 != ent->state) - return; - ent->state = new_state; - if ((type == DIRREQ_DIRECT && - new_state == DIRREQ_FLUSHING_DIR_CONN_FINISHED) || - (type == DIRREQ_TUNNELED && - new_state == DIRREQ_CHANNEL_BUFFER_FLUSHED)) { - tor_gettimeofday(&ent->completion_time); - ent->completed = 1; - } -} - -/** Return the bridge-ip-transports string that should be inserted in - * our extra-info descriptor. Return NULL if the bridge-ip-transports - * line should be empty. */ -char * -geoip_get_transport_history(void) -{ - unsigned granularity = IP_GRANULARITY; - /** String hash table -> . */ - strmap_t *transport_counts = strmap_new(); - - /** Smartlist that contains copies of the names of the transports - that have been used. */ - smartlist_t *transports_used = smartlist_new(); - - /* Special string to signify that no transport was used for this - connection. Pluggable transport names can't have symbols in their - names, so this string will never collide with a real transport. */ - static const char* no_transport_str = ""; - - clientmap_entry_t **ent; - const char *transport_name = NULL; - smartlist_t *string_chunks = smartlist_new(); - char *the_string = NULL; - - /* If we haven't seen any clients yet, return NULL. */ - if (HT_EMPTY(&client_history)) - goto done; - - /** We do the following steps to form the transport history string: - * a) Foreach client that uses a pluggable transport, we increase the - * times that transport was used by one. If the client did not use - * a transport, we increase the number of times someone connected - * without obfuscation. - * b) Foreach transport we observed, we write its transport history - * string and push it to string_chunks. So, for example, if we've - * seen 665 obfs2 clients, we write "obfs2=665". - * c) We concatenate string_chunks to form the final string. - */ - - log_debug(LD_GENERAL,"Starting iteration for transport history. %d clients.", - HT_SIZE(&client_history)); - - /* Loop through all clients. */ - HT_FOREACH(ent, clientmap, &client_history) { - uintptr_t val; - void *ptr; - transport_name = (*ent)->transport_name; - if (!transport_name) - transport_name = no_transport_str; - - /* Increase the count for this transport name. */ - ptr = strmap_get(transport_counts, transport_name); - val = (uintptr_t)ptr; - val++; - ptr = (void*)val; - strmap_set(transport_counts, transport_name, ptr); - - /* If it's the first time we see this transport, note it. */ - if (val == 1) - smartlist_add(transports_used, tor_strdup(transport_name)); - - log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. " - "I've now seen %d clients.", - safe_str_client(fmt_addr(&(*ent)->addr)), - transport_name ? transport_name : "", - (int)val); - } - - /* Sort the transport names (helps with unit testing). */ - smartlist_sort_strings(transports_used); - - /* Loop through all seen transports. */ - SMARTLIST_FOREACH_BEGIN(transports_used, const char *, transport_name) { - void *transport_count_ptr = strmap_get(transport_counts, transport_name); - uintptr_t transport_count = (uintptr_t) transport_count_ptr; - - log_debug(LD_GENERAL, "We got "U64_FORMAT" clients with transport '%s'.", - U64_PRINTF_ARG((uint64_t)transport_count), transport_name); - - smartlist_add_asprintf(string_chunks, "%s="U64_FORMAT, - transport_name, - U64_PRINTF_ARG(round_uint64_to_next_multiple_of( - (uint64_t)transport_count, - granularity))); - } SMARTLIST_FOREACH_END(transport_name); - - the_string = smartlist_join_strings(string_chunks, ",", 0, NULL); - - log_debug(LD_GENERAL, "Final bridge-ip-transports string: '%s'", the_string); - - done: - strmap_free(transport_counts, NULL); - SMARTLIST_FOREACH(transports_used, char *, s, tor_free(s)); - smartlist_free(transports_used); - SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s)); - smartlist_free(string_chunks); - - return the_string; -} - -/** Return a newly allocated comma-separated string containing statistics - * on network status downloads. The string contains the number of completed - * requests, timeouts, and still running requests as well as the download - * times by deciles and quartiles. Return NULL if we have not observed - * requests for long enough. */ -static char * -geoip_get_dirreq_history(dirreq_type_t type) -{ - char *result = NULL; - smartlist_t *dirreq_completed = NULL; - uint32_t complete = 0, timeouts = 0, running = 0; - int bufsize = 1024, written; - dirreq_map_entry_t **ptr, **next, *ent; - struct timeval now; - - tor_gettimeofday(&now); - dirreq_completed = smartlist_new(); - for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) { - ent = *ptr; - if (ent->type != type) { - next = HT_NEXT(dirreqmap, &dirreq_map, ptr); - continue; - } else { - if (ent->completed) { - smartlist_add(dirreq_completed, ent); - complete++; - next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr); - } else { - if (tv_mdiff(&ent->request_time, &now) / 1000 > DIRREQ_TIMEOUT) - timeouts++; - else - running++; - next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr); - tor_free(ent); - } - } - } -#define DIR_REQ_GRANULARITY 4 - complete = round_uint32_to_next_multiple_of(complete, - DIR_REQ_GRANULARITY); - timeouts = round_uint32_to_next_multiple_of(timeouts, - DIR_REQ_GRANULARITY); - running = round_uint32_to_next_multiple_of(running, - DIR_REQ_GRANULARITY); - result = tor_malloc_zero(bufsize); - written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u," - "running=%u", complete, timeouts, running); - if (written < 0) { - tor_free(result); - goto done; - } - -#define MIN_DIR_REQ_RESPONSES 16 - if (complete >= MIN_DIR_REQ_RESPONSES) { - uint32_t *dltimes; - /* We may have rounded 'completed' up. Here we want to use the - * real value. */ - complete = smartlist_len(dirreq_completed); - dltimes = tor_malloc_zero(sizeof(uint32_t) * complete); - SMARTLIST_FOREACH_BEGIN(dirreq_completed, dirreq_map_entry_t *, ent) { - uint32_t bytes_per_second; - uint32_t time_diff = (uint32_t) tv_mdiff(&ent->request_time, - &ent->completion_time); - if (time_diff == 0) - time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible - * by law of nature or something, but a milisecond - * is a bit greater than "instantly" */ - bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff); - dltimes[ent_sl_idx] = bytes_per_second; - } SMARTLIST_FOREACH_END(ent); - median_uint32(dltimes, complete); /* sorts as a side effect. */ - written = tor_snprintf(result + written, bufsize - written, - ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u," - "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u", - dltimes[0], - dltimes[1*complete/10-1], - dltimes[2*complete/10-1], - dltimes[1*complete/4-1], - dltimes[3*complete/10-1], - dltimes[4*complete/10-1], - dltimes[5*complete/10-1], - dltimes[6*complete/10-1], - dltimes[7*complete/10-1], - dltimes[3*complete/4-1], - dltimes[8*complete/10-1], - dltimes[9*complete/10-1], - dltimes[complete-1]); - if (written<0) - tor_free(result); - tor_free(dltimes); - } - done: - SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent, - tor_free(ent)); - smartlist_free(dirreq_completed); - return result; -} - -/** Store a newly allocated comma-separated string in - * *country_str containing entries for all the countries from - * which we've seen enough clients connect as a bridge, directory - * server, or entry guard. The entry format is cc=num where num is the - * number of IPs we've seen connecting from that country, and cc is a - * lowercased country code. *country_str is set to NULL if - * we're not ready to export per country data yet. - * - * Store a newly allocated comma-separated string in ipver_str - * containing entries for clients connecting over IPv4 and IPv6. The - * format is family=num where num is the nubmer of IPs we've seen - * connecting over that protocol family, and family is 'v4' or 'v6'. - * - * Return 0 on success and -1 if we're missing geoip data. */ -int -geoip_get_client_history(geoip_client_action_t action, - char **country_str, char **ipver_str) -{ - unsigned granularity = IP_GRANULARITY; - smartlist_t *entries = NULL; - int n_countries = geoip_get_n_countries(); - int i; - clientmap_entry_t **ent; - unsigned *counts = NULL; - unsigned total = 0; - unsigned ipv4_count = 0, ipv6_count = 0; - - if (!geoip_is_loaded(AF_INET) && !geoip_is_loaded(AF_INET6)) - return -1; - - counts = tor_malloc_zero(sizeof(unsigned)*n_countries); - HT_FOREACH(ent, clientmap, &client_history) { - int country; - if ((*ent)->action != (int)action) - continue; - country = geoip_get_country_by_addr(&(*ent)->addr); - if (country < 0) - country = 0; /** unresolved requests are stored at index 0. */ - tor_assert(0 <= country && country < n_countries); - ++counts[country]; - ++total; - switch (tor_addr_family(&(*ent)->addr)) { - case AF_INET: - ipv4_count++; - break; - case AF_INET6: - ipv6_count++; - break; - } - } - if (ipver_str) { - smartlist_t *chunks = smartlist_new(); - smartlist_add_asprintf(chunks, "v4=%u", - round_to_next_multiple_of(ipv4_count, granularity)); - smartlist_add_asprintf(chunks, "v6=%u", - round_to_next_multiple_of(ipv6_count, granularity)); - *ipver_str = smartlist_join_strings(chunks, ",", 0, NULL); - SMARTLIST_FOREACH(chunks, char *, c, tor_free(c)); - smartlist_free(chunks); - } - - /* Don't record per country data if we haven't seen enough IPs. */ - if (total < MIN_IPS_TO_NOTE_ANYTHING) { - tor_free(counts); - if (country_str) - *country_str = NULL; - return 0; - } - - /* Make a list of c_hist_t */ - entries = smartlist_new(); - for (i = 0; i < n_countries; ++i) { - unsigned c = counts[i]; - const char *countrycode; - c_hist_t *ent; - /* Only report a country if it has a minimum number of IPs. */ - if (c >= MIN_IPS_TO_NOTE_COUNTRY) { - c = round_to_next_multiple_of(c, granularity); - countrycode = geoip_get_country_name(i); - ent = tor_malloc(sizeof(c_hist_t)); - strlcpy(ent->country, countrycode, sizeof(ent->country)); - ent->total = c; - smartlist_add(entries, ent); - } - } - /* Sort entries. Note that we must do this _AFTER_ rounding, or else - * the sort order could leak info. */ - smartlist_sort(entries, c_hist_compare_); - - if (country_str) { - smartlist_t *chunks = smartlist_new(); - SMARTLIST_FOREACH(entries, c_hist_t *, ch, { - smartlist_add_asprintf(chunks, "%s=%u", ch->country, ch->total); - }); - *country_str = smartlist_join_strings(chunks, ",", 0, NULL); - SMARTLIST_FOREACH(chunks, char *, c, tor_free(c)); - smartlist_free(chunks); - } - - SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c)); - smartlist_free(entries); - tor_free(counts); - - return 0; -} - -/** Return a newly allocated string holding the per-country request history - * for v3 network statuses in a format suitable for an extra-info document, - * or NULL on failure. */ -char * -geoip_get_request_history(void) -{ - smartlist_t *entries, *strings; - char *result; - unsigned granularity = IP_GRANULARITY; - - if (!geoip_countries) - return NULL; - - entries = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(geoip_countries, geoip_country_t *, c) { - uint32_t tot = 0; - c_hist_t *ent; - tot = c->n_v3_ns_requests; - if (!tot) - continue; - ent = tor_malloc_zero(sizeof(c_hist_t)); - strlcpy(ent->country, c->countrycode, sizeof(ent->country)); - ent->total = round_to_next_multiple_of(tot, granularity); - smartlist_add(entries, ent); - } SMARTLIST_FOREACH_END(c); - smartlist_sort(entries, c_hist_compare_); - - strings = smartlist_new(); - SMARTLIST_FOREACH(entries, c_hist_t *, ent, { - smartlist_add_asprintf(strings, "%s=%u", ent->country, ent->total); - }); - result = smartlist_join_strings(strings, ",", 0, NULL); - SMARTLIST_FOREACH(strings, char *, cp, tor_free(cp)); - SMARTLIST_FOREACH(entries, c_hist_t *, ent, tor_free(ent)); - smartlist_free(strings); - smartlist_free(entries); - return result; -} - -/** Start time of directory request stats or 0 if we're not collecting - * directory request statistics. */ -static time_t start_of_dirreq_stats_interval; - -/** Initialize directory request stats. */ -void -geoip_dirreq_stats_init(time_t now) -{ - start_of_dirreq_stats_interval = now; -} - -/** Reset counters for dirreq stats. */ -void -geoip_reset_dirreq_stats(time_t now) -{ - SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, { - c->n_v3_ns_requests = 0; - }); - { - clientmap_entry_t **ent, **next, *this; - for (ent = HT_START(clientmap, &client_history); ent != NULL; - ent = next) { - if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) { - this = *ent; - next = HT_NEXT_RMV(clientmap, &client_history, ent); - clientmap_entry_free(this); - } else { - next = HT_NEXT(clientmap, &client_history, ent); - } - } - } - memset(ns_v3_responses, 0, sizeof(ns_v3_responses)); - { - dirreq_map_entry_t **ent, **next, *this; - for (ent = HT_START(dirreqmap, &dirreq_map); ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent); - tor_free(this); - } - } - start_of_dirreq_stats_interval = now; -} - -/** Stop collecting directory request stats in a way that we can re-start - * doing so in geoip_dirreq_stats_init(). */ -void -geoip_dirreq_stats_term(void) -{ - geoip_reset_dirreq_stats(0); -} - -/** Return a newly allocated string containing the dirreq statistics - * until now, or NULL if we're not collecting dirreq stats. Caller - * must ensure start_of_dirreq_stats_interval is in the past. */ -char * -geoip_format_dirreq_stats(time_t now) -{ - char t[ISO_TIME_LEN+1]; - int i; - char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string, - *v3_tunneled_dl_string; - char *result; - - if (!start_of_dirreq_stats_interval) - return NULL; /* Not initialized. */ - - tor_assert(now >= start_of_dirreq_stats_interval); - - format_iso_time(t, now); - geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS, &v3_ips_string, NULL); - v3_reqs_string = geoip_get_request_history(); - -#define RESPONSE_GRANULARITY 8 - for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) { - ns_v3_responses[i] = round_uint32_to_next_multiple_of( - ns_v3_responses[i], RESPONSE_GRANULARITY); - } -#undef RESPONSE_GRANULARITY - - v3_direct_dl_string = geoip_get_dirreq_history(DIRREQ_DIRECT); - v3_tunneled_dl_string = geoip_get_dirreq_history(DIRREQ_TUNNELED); - - /* Put everything together into a single string. */ - tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n" - "dirreq-v3-ips %s\n" - "dirreq-v3-reqs %s\n" - "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u," - "not-found=%u,not-modified=%u,busy=%u\n" - "dirreq-v3-direct-dl %s\n" - "dirreq-v3-tunneled-dl %s\n", - t, - (unsigned) (now - start_of_dirreq_stats_interval), - v3_ips_string ? v3_ips_string : "", - v3_reqs_string ? v3_reqs_string : "", - ns_v3_responses[GEOIP_SUCCESS], - ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS], - ns_v3_responses[GEOIP_REJECT_UNAVAILABLE], - ns_v3_responses[GEOIP_REJECT_NOT_FOUND], - ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED], - ns_v3_responses[GEOIP_REJECT_BUSY], - v3_direct_dl_string ? v3_direct_dl_string : "", - v3_tunneled_dl_string ? v3_tunneled_dl_string : ""); - - /* Free partial strings. */ - tor_free(v3_ips_string); - tor_free(v3_reqs_string); - tor_free(v3_direct_dl_string); - tor_free(v3_tunneled_dl_string); - - return result; -} - -/** If 24 hours have passed since the beginning of the current dirreq - * stats period, write dirreq stats to $DATADIR/stats/dirreq-stats - * (possibly overwriting an existing file) and reset counters. Return - * when we would next want to write dirreq stats or 0 if we never want to - * write. */ -time_t -geoip_dirreq_stats_write(time_t now) -{ - char *str = NULL; - - if (!start_of_dirreq_stats_interval) - return 0; /* Not initialized. */ - if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now) - goto done; /* Not ready to write. */ - - /* Discard all items in the client history that are too old. */ - geoip_remove_old_clients(start_of_dirreq_stats_interval); - - /* Generate history string .*/ - str = geoip_format_dirreq_stats(now); - - /* Write dirreq-stats string to disk. */ - if (!check_or_create_data_subdir("stats")) { - write_to_data_subdir("stats", "dirreq-stats", str, "dirreq statistics"); - /* Reset measurement interval start. */ - geoip_reset_dirreq_stats(now); - } - - done: - tor_free(str); - return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL; -} - -/** Start time of bridge stats or 0 if we're not collecting bridge - * statistics. */ -static time_t start_of_bridge_stats_interval; - -/** Initialize bridge stats. */ -void -geoip_bridge_stats_init(time_t now) -{ - start_of_bridge_stats_interval = now; -} - -/** Stop collecting bridge stats in a way that we can re-start doing so in - * geoip_bridge_stats_init(). */ -void -geoip_bridge_stats_term(void) -{ - client_history_clear(); - start_of_bridge_stats_interval = 0; -} - -/** Validate a bridge statistics string as it would be written to a - * current extra-info descriptor. Return 1 if the string is valid and - * recent enough, or 0 otherwise. */ -static int -validate_bridge_stats(const char *stats_str, time_t now) -{ - char stats_end_str[ISO_TIME_LEN+1], stats_start_str[ISO_TIME_LEN+1], - *eos; - - const char *BRIDGE_STATS_END = "bridge-stats-end "; - const char *BRIDGE_IPS = "bridge-ips "; - const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n"; - const char *BRIDGE_TRANSPORTS = "bridge-ip-transports "; - const char *BRIDGE_TRANSPORTS_EMPTY_LINE = "bridge-ip-transports\n"; - const char *tmp; - time_t stats_end_time; - int seconds; - tor_assert(stats_str); - - /* Parse timestamp and number of seconds from - "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */ - tmp = find_str_at_start_of_line(stats_str, BRIDGE_STATS_END); - if (!tmp) - return 0; - tmp += strlen(BRIDGE_STATS_END); - - if (strlen(tmp) < ISO_TIME_LEN + 6) - return 0; - strlcpy(stats_end_str, tmp, sizeof(stats_end_str)); - if (parse_iso_time(stats_end_str, &stats_end_time) < 0) - return 0; - if (stats_end_time < now - (25*60*60) || - stats_end_time > now + (1*60*60)) - return 0; - seconds = (int)strtol(tmp + ISO_TIME_LEN + 2, &eos, 10); - if (!eos || seconds < 23*60*60) - return 0; - format_iso_time(stats_start_str, stats_end_time - seconds); - - /* Parse: "bridge-ips CC=N,CC=N,..." */ - tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS); - if (!tmp) { - /* Look if there is an empty "bridge-ips" line */ - tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS_EMPTY_LINE); - if (!tmp) - return 0; - } - - /* Parse: "bridge-ip-transports PT=N,PT=N,..." */ - tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS); - if (!tmp) { - /* Look if there is an empty "bridge-ip-transports" line */ - tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS_EMPTY_LINE); - if (!tmp) - return 0; - } - - return 1; -} - -/** Most recent bridge statistics formatted to be written to extra-info - * descriptors. */ -static char *bridge_stats_extrainfo = NULL; - -/** Return a newly allocated string holding our bridge usage stats by country - * in a format suitable for inclusion in an extrainfo document. Return NULL on - * failure. */ -char * -geoip_format_bridge_stats(time_t now) -{ - char *out = NULL; - char *country_data = NULL, *ipver_data = NULL, *transport_data = NULL; - long duration = now - start_of_bridge_stats_interval; - char written[ISO_TIME_LEN+1]; - - if (duration < 0) - return NULL; - if (!start_of_bridge_stats_interval) - return NULL; /* Not initialized. */ - - format_iso_time(written, now); - geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data); - transport_data = geoip_get_transport_history(); - - tor_asprintf(&out, - "bridge-stats-end %s (%ld s)\n" - "bridge-ips %s\n" - "bridge-ip-versions %s\n" - "bridge-ip-transports %s\n", - written, duration, - country_data ? country_data : "", - ipver_data ? ipver_data : "", - transport_data ? transport_data : ""); - tor_free(country_data); - tor_free(ipver_data); - tor_free(transport_data); - - return out; -} - -/** Return a newly allocated string holding our bridge usage stats by country - * in a format suitable for the answer to a controller request. Return NULL on - * failure. */ -static char * -format_bridge_stats_controller(time_t now) -{ - char *out = NULL, *country_data = NULL, *ipver_data = NULL; - char started[ISO_TIME_LEN+1]; - (void) now; - - format_iso_time(started, start_of_bridge_stats_interval); - geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data); - - tor_asprintf(&out, - "TimeStarted=\"%s\" CountrySummary=%s IPVersions=%s", - started, - country_data ? country_data : "", - ipver_data ? ipver_data : ""); - tor_free(country_data); - tor_free(ipver_data); - return out; -} - -/** Write bridge statistics to $DATADIR/stats/bridge-stats and return - * when we should next try to write statistics. */ -time_t -geoip_bridge_stats_write(time_t now) -{ - char *val = NULL; - - /* Check if 24 hours have passed since starting measurements. */ - if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL) - return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL; - - /* Discard all items in the client history that are too old. */ - geoip_remove_old_clients(start_of_bridge_stats_interval); - - /* Generate formatted string */ - val = geoip_format_bridge_stats(now); - if (val == NULL) - goto done; - - /* Update the stored value. */ - tor_free(bridge_stats_extrainfo); - bridge_stats_extrainfo = val; - start_of_bridge_stats_interval = now; - - /* Write it to disk. */ - if (!check_or_create_data_subdir("stats")) { - write_to_data_subdir("stats", "bridge-stats", - bridge_stats_extrainfo, "bridge statistics"); - - /* Tell the controller, "hey, there are clients!" */ - { - char *controller_str = format_bridge_stats_controller(now); - if (controller_str) - control_event_clients_seen(controller_str); - tor_free(controller_str); - } - } - - done: - return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL; -} - -/** Try to load the most recent bridge statistics from disk, unless we - * have finished a measurement interval lately, and check whether they - * are still recent enough. */ -static void -load_bridge_stats(time_t now) -{ - char *fname, *contents; - if (bridge_stats_extrainfo) - return; - - fname = get_datadir_fname2("stats", "bridge-stats"); - contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); - if (contents && validate_bridge_stats(contents, now)) { - bridge_stats_extrainfo = contents; - } else { - tor_free(contents); - } - - tor_free(fname); -} - -/** Return most recent bridge statistics for inclusion in extra-info - * descriptors, or NULL if we don't have recent bridge statistics. */ -const char * -geoip_get_bridge_stats_extrainfo(time_t now) -{ - load_bridge_stats(now); - return bridge_stats_extrainfo; -} - -/** Return a new string containing the recent bridge statistics to be returned - * to controller clients, or NULL if we don't have any bridge statistics. */ -char * -geoip_get_bridge_stats_controller(time_t now) -{ - return format_bridge_stats_controller(now); -} - -/** Start time of entry stats or 0 if we're not collecting entry - * statistics. */ -static time_t start_of_entry_stats_interval; - -/** Initialize entry stats. */ -void -geoip_entry_stats_init(time_t now) -{ - start_of_entry_stats_interval = now; -} - -/** Reset counters for entry stats. */ -void -geoip_reset_entry_stats(time_t now) -{ - client_history_clear(); - start_of_entry_stats_interval = now; -} - -/** Stop collecting entry stats in a way that we can re-start doing so in - * geoip_entry_stats_init(). */ -void -geoip_entry_stats_term(void) -{ - geoip_reset_entry_stats(0); -} - -/** Return a newly allocated string containing the entry statistics - * until now, or NULL if we're not collecting entry stats. Caller - * must ensure start_of_entry_stats_interval lies in the past. */ -char * -geoip_format_entry_stats(time_t now) -{ - char t[ISO_TIME_LEN+1]; - char *data = NULL; - char *result; - - if (!start_of_entry_stats_interval) - return NULL; /* Not initialized. */ - - tor_assert(now >= start_of_entry_stats_interval); - - geoip_get_client_history(GEOIP_CLIENT_CONNECT, &data, NULL); - format_iso_time(t, now); - tor_asprintf(&result, - "entry-stats-end %s (%u s)\n" - "entry-ips %s\n", - t, (unsigned) (now - start_of_entry_stats_interval), - data ? data : ""); - tor_free(data); - return result; -} - -/** If 24 hours have passed since the beginning of the current entry stats - * period, write entry stats to $DATADIR/stats/entry-stats (possibly - * overwriting an existing file) and reset counters. Return when we would - * next want to write entry stats or 0 if we never want to write. */ -time_t -geoip_entry_stats_write(time_t now) -{ - char *str = NULL; - - if (!start_of_entry_stats_interval) - return 0; /* Not initialized. */ - if (start_of_entry_stats_interval + WRITE_STATS_INTERVAL > now) - goto done; /* Not ready to write. */ - - /* Discard all items in the client history that are too old. */ - geoip_remove_old_clients(start_of_entry_stats_interval); - - /* Generate history string .*/ - str = geoip_format_entry_stats(now); - - /* Write entry-stats string to disk. */ - if (!check_or_create_data_subdir("stats")) { - write_to_data_subdir("stats", "entry-stats", str, "entry statistics"); - - /* Reset measurement interval start. */ - geoip_reset_entry_stats(now); - } - - done: - tor_free(str); - return start_of_entry_stats_interval + WRITE_STATS_INTERVAL; -} - -/** Helper used to implement GETINFO ip-to-country/... controller command. */ -int -getinfo_helper_geoip(control_connection_t *control_conn, - const char *question, char **answer, - const char **errmsg) -{ - (void)control_conn; - if (!strcmpstart(question, "ip-to-country/")) { - int c; - sa_family_t family; - tor_addr_t addr; - question += strlen("ip-to-country/"); - family = tor_addr_parse(&addr, question); - if (family != AF_INET && family != AF_INET6) { - *errmsg = "Invalid address family"; - return -1; - } - if (!geoip_is_loaded(family)) { - *errmsg = "GeoIP data not loaded"; - return -1; - } - if (family == AF_INET) - c = geoip_get_country_by_ipv4(tor_addr_to_ipv4h(&addr)); - else /* AF_INET6 */ - c = geoip_get_country_by_ipv6(tor_addr_to_in6(&addr)); - *answer = tor_strdup(geoip_get_country_name(c)); - } - return 0; -} - -/** Release all storage held by the GeoIP databases and country list. */ -static void -clear_geoip_db(void) -{ - if (geoip_countries) { - SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, tor_free(c)); - smartlist_free(geoip_countries); - } - - strmap_free(country_idxplus1_by_lc_code, NULL); - if (geoip_ipv4_entries) { - SMARTLIST_FOREACH(geoip_ipv4_entries, geoip_ipv4_entry_t *, ent, - tor_free(ent)); - smartlist_free(geoip_ipv4_entries); - } - if (geoip_ipv6_entries) { - SMARTLIST_FOREACH(geoip_ipv6_entries, geoip_ipv6_entry_t *, ent, - tor_free(ent)); - smartlist_free(geoip_ipv6_entries); - } - geoip_countries = NULL; - country_idxplus1_by_lc_code = NULL; - geoip_ipv4_entries = NULL; - geoip_ipv6_entries = NULL; -} - -/** Release all storage held in this file. */ -void -geoip_free_all(void) -{ - { - clientmap_entry_t **ent, **next, *this; - for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(clientmap, &client_history, ent); - clientmap_entry_free(this); - } - HT_CLEAR(clientmap, &client_history); - } - { - dirreq_map_entry_t **ent, **next, *this; - for (ent = HT_START(dirreqmap, &dirreq_map); ent != NULL; ent = next) { - this = *ent; - next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent); - tor_free(this); - } - HT_CLEAR(dirreqmap, &dirreq_map); - } - - clear_geoip_db(); - tor_free(bridge_stats_extrainfo); -} - diff --git a/src/tor/geoip.h b/src/tor/geoip.h deleted file mode 100644 index b9b53c3..0000000 --- a/src/tor/geoip.h +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file geoip.h - * \brief Header file for geoip.c. - **/ - -#ifndef TOR_GEOIP_H -#define TOR_GEOIP_H - -#include "testsupport.h" - -#ifdef GEOIP_PRIVATE -STATIC int geoip_parse_entry(const char *line, sa_family_t family); -STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr); -STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr); -#endif -int should_record_bridge_info(const or_options_t *options); -int geoip_load_file(sa_family_t family, const char *filename); -int geoip_get_country_by_addr(const tor_addr_t *addr); -int geoip_get_n_countries(void); -const char *geoip_get_country_name(country_t num); -int geoip_is_loaded(sa_family_t family); -const char *geoip_db_digest(sa_family_t family); -country_t geoip_get_country(const char *countrycode); - -void geoip_note_client_seen(geoip_client_action_t action, - const tor_addr_t *addr, const char *transport_name, - time_t now); -void geoip_remove_old_clients(time_t cutoff); - -void geoip_note_ns_response(geoip_ns_response_t response); -char *geoip_get_transport_history(void); -int geoip_get_client_history(geoip_client_action_t action, - char **country_str, char **ipver_str); -char *geoip_get_request_history(void); -int getinfo_helper_geoip(control_connection_t *control_conn, - const char *question, char **answer, - const char **errmsg); -void geoip_free_all(void); - -void geoip_start_dirreq(uint64_t dirreq_id, size_t response_size, - dirreq_type_t type); -void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type, - dirreq_state_t new_state); - -void geoip_dirreq_stats_init(time_t now); -void geoip_reset_dirreq_stats(time_t now); -char *geoip_format_dirreq_stats(time_t now); -time_t geoip_dirreq_stats_write(time_t now); -void geoip_dirreq_stats_term(void); -void geoip_entry_stats_init(time_t now); -time_t geoip_entry_stats_write(time_t now); -void geoip_entry_stats_term(void); -void geoip_reset_entry_stats(time_t now); -char *geoip_format_entry_stats(time_t now); -void geoip_bridge_stats_init(time_t now); -char *geoip_format_bridge_stats(time_t now); -time_t geoip_bridge_stats_write(time_t now); -void geoip_bridge_stats_term(void); -const char *geoip_get_bridge_stats_extrainfo(time_t); -char *geoip_get_bridge_stats_controller(time_t); - -#endif - diff --git a/src/tor/hibernate.c b/src/tor/hibernate.c deleted file mode 100644 index c234e4e..0000000 --- a/src/tor/hibernate.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file hibernate.c - * \brief Functions to close listeners, stop allowing new circuits, - * etc in preparation for closing down or going dormant; and to track - * bandwidth and time intervals to know when to hibernate and when to - * stop hibernating. - **/ - -/* -hibernating, phase 1: - - send destroy in response to create cells - - send end (policy failed) in response to begin cells - - close an OR conn when it has no circuits - -hibernating, phase 2: - (entered when bandwidth hard limit reached) - - close all OR/AP/exit conns) -*/ - -#define HIBERNATE_PRIVATE -#include "or.h" -#include "channel.h" -#include "channeltls.h" -#include "config.h" -#include "connection.h" -#include "connection_edge.h" -#include "hibernate.h" -#include "onion_main.h" -#include "router.h" -#include "statefile.h" - -extern long stats_n_seconds_working; /* published uptime */ - -/** Are we currently awake, asleep, running out of bandwidth, or shutting - * down? */ -static hibernate_state_t hibernate_state = HIBERNATE_STATE_INITIAL; -/** If are hibernating, when do we plan to wake up? Set to 0 if we - * aren't hibernating. */ -static time_t hibernate_end_time = 0; -/** If we are shutting down, when do we plan finally exit? Set to 0 if - * we aren't shutting down. */ -static time_t shutdown_time = 0; - -/** Possible accounting periods. */ -typedef enum { - UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3, -} time_unit_t; - -/* Fields for accounting logic. Accounting overview: - * - * Accounting is designed to ensure that no more than N bytes are sent in - * either direction over a given interval (currently, one month, one week, or - * one day) We could - * try to do this by choking our bandwidth to a trickle, but that - * would make our streams useless. Instead, we estimate what our - * bandwidth usage will be, and guess how long we'll be able to - * provide that much bandwidth before hitting our limit. We then - * choose a random time within the accounting interval to come up (so - * that we don't get 50 Tors running on the 1st of the month and none - * on the 30th). - * - * Each interval runs as follows: - * - * 1. We guess our bandwidth usage, based on how much we used - * last time. We choose a "wakeup time" within the interval to come up. - * 2. Until the chosen wakeup time, we hibernate. - * 3. We come up at the wakeup time, and provide bandwidth until we are - * "very close" to running out. - * 4. Then we go into low-bandwidth mode, and stop accepting new - * connections, but provide bandwidth until we run out. - * 5. Then we hibernate until the end of the interval. - * - * If the interval ends before we run out of bandwidth, we go back to - * step one. - */ - -/** How many bytes have we read in this accounting interval? */ -static uint64_t n_bytes_read_in_interval = 0; -/** How many bytes have we written in this accounting interval? */ -static uint64_t n_bytes_written_in_interval = 0; -/** How many seconds have we been running this interval? */ -static uint32_t n_seconds_active_in_interval = 0; -/** How many seconds were we active in this interval before we hit our soft - * limit? */ -static int n_seconds_to_hit_soft_limit = 0; -/** When in this interval was the soft limit hit. */ -static time_t soft_limit_hit_at = 0; -/** How many bytes had we read/written when we hit the soft limit? */ -static uint64_t n_bytes_at_soft_limit = 0; -/** When did this accounting interval start? */ -static time_t interval_start_time = 0; -/** When will this accounting interval end? */ -static time_t interval_end_time = 0; -/** How far into the accounting interval should we hibernate? */ -static time_t interval_wakeup_time = 0; -/** How much bandwidth do we 'expect' to use per minute? (0 if we have no - * info from the last period.) */ -static uint64_t expected_bandwidth_usage = 0; -/** What unit are we using for our accounting? */ -static time_unit_t cfg_unit = UNIT_MONTH; - -/** How many days,hours,minutes into each unit does our accounting interval - * start? */ -/** @{ */ -static int cfg_start_day = 0, - cfg_start_hour = 0, - cfg_start_min = 0; -/** @} */ - -static void reset_accounting(time_t now); -static int read_bandwidth_usage(void); -static time_t start_of_accounting_period_after(time_t now); -static time_t start_of_accounting_period_containing(time_t now); -static void accounting_set_wakeup_time(void); - -/* ************ - * Functions for bandwidth accounting. - * ************/ - -/** Configure accounting start/end time settings based on - * options->AccountingStart. Return 0 on success, -1 on failure. If - * validate_only is true, do not change the current settings. */ -int -accounting_parse_options(const or_options_t *options, int validate_only) -{ - time_unit_t unit; - int ok, idx; - long d,h,m; - smartlist_t *items; - const char *v = options->AccountingStart; - const char *s; - char *cp; - - if (!v) { - if (!validate_only) { - cfg_unit = UNIT_MONTH; - cfg_start_day = 1; - cfg_start_hour = 0; - cfg_start_min = 0; - } - return 0; - } - - items = smartlist_new(); - smartlist_split_string(items, v, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK,0); - if (smartlist_len(items)<2) { - log_warn(LD_CONFIG, "Too few arguments to AccountingStart"); - goto err; - } - s = smartlist_get(items,0); - if (0==strcasecmp(s, "month")) { - unit = UNIT_MONTH; - } else if (0==strcasecmp(s, "week")) { - unit = UNIT_WEEK; - } else if (0==strcasecmp(s, "day")) { - unit = UNIT_DAY; - } else { - log_warn(LD_CONFIG, - "Unrecognized accounting unit '%s': only 'month', 'week'," - " and 'day' are supported.", s); - goto err; - } - - switch (unit) { - case UNIT_WEEK: - d = tor_parse_long(smartlist_get(items,1), 10, 1, 7, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Weekly accounting must begin on a day between " - "1 (Monday) and 7 (Sunday)"); - goto err; - } - break; - case UNIT_MONTH: - d = tor_parse_long(smartlist_get(items,1), 10, 1, 28, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Monthly accounting must begin on a day between " - "1 and 28"); - goto err; - } - break; - case UNIT_DAY: - d = 0; - break; - /* Coverity dislikes unreachable default cases; some compilers warn on - * switch statements missing a case. Tell Coverity not to worry. */ - /* coverity[dead_error_begin] */ - default: - tor_assert(0); - } - - idx = unit==UNIT_DAY?1:2; - if (smartlist_len(items) != (idx+1)) { - log_warn(LD_CONFIG,"Accounting unit '%s' requires %d argument%s.", - s, idx, (idx>1)?"s":""); - goto err; - } - s = smartlist_get(items, idx); - h = tor_parse_long(s, 10, 0, 23, &ok, &cp); - if (!ok) { - log_warn(LD_CONFIG,"Accounting start time not parseable: bad hour."); - goto err; - } - if (!cp || *cp!=':') { - log_warn(LD_CONFIG, - "Accounting start time not parseable: not in HH:MM format"); - goto err; - } - m = tor_parse_long(cp+1, 10, 0, 59, &ok, &cp); - if (!ok) { - log_warn(LD_CONFIG, "Accounting start time not parseable: bad minute"); - goto err; - } - if (!cp || *cp!='\0') { - log_warn(LD_CONFIG, - "Accounting start time not parseable: not in HH:MM format"); - goto err; - } - - if (!validate_only) { - cfg_unit = unit; - cfg_start_day = (int)d; - cfg_start_hour = (int)h; - cfg_start_min = (int)m; - } - SMARTLIST_FOREACH(items, char *, item, tor_free(item)); - smartlist_free(items); - return 0; - err: - SMARTLIST_FOREACH(items, char *, item, tor_free(item)); - smartlist_free(items); - return -1; -} - -/** If we want to manage the accounting system and potentially - * hibernate, return 1, else return 0. - */ -int -accounting_is_enabled(const or_options_t *options) -{ - if (options->AccountingMax) - return 1; - return 0; -} - -/** If accounting is enabled, return how long (in seconds) this - * interval lasts. */ -int -accounting_get_interval_length(void) -{ - return (int)(interval_end_time - interval_start_time); -} - -/** Return the time at which the current accounting interval will end. */ -time_t -accounting_get_end_time(void) -{ - return interval_end_time; -} - -/** Called from onion_main.c to tell us that seconds seconds have - * passed, n_read bytes have been read, and n_written - * bytes have been written. */ -void -accounting_add_bytes(size_t n_read, size_t n_written, int seconds) -{ - n_bytes_read_in_interval += n_read; - n_bytes_written_in_interval += n_written; - /* If we haven't been called in 10 seconds, we're probably jumping - * around in time. */ - n_seconds_active_in_interval += (seconds < 10) ? seconds : 0; -} - -/** If get_end, return the end of the accounting period that contains - * the time now. Else, return the start of the accounting - * period that contains the time now */ -static time_t -edge_of_accounting_period_containing(time_t now, int get_end) -{ - int before; - struct tm tm; - tor_localtime_r(&now, &tm); - - /* Set 'before' to true iff the current time is before the hh:mm - * changeover time for today. */ - before = tm.tm_hour < cfg_start_hour || - (tm.tm_hour == cfg_start_hour && tm.tm_min < cfg_start_min); - - /* Dispatch by unit. First, find the start day of the given period; - * then, if get_end is true, increment to the end day. */ - switch (cfg_unit) - { - case UNIT_MONTH: { - /* If this is before the Nth, we want the Nth of last month. */ - if (tm.tm_mday < cfg_start_day || - (tm.tm_mday < cfg_start_day && before)) { - --tm.tm_mon; - } - /* Otherwise, the month is correct. */ - tm.tm_mday = cfg_start_day; - if (get_end) - ++tm.tm_mon; - break; - } - case UNIT_WEEK: { - /* What is the 'target' day of the week in struct tm format? (We - say Sunday==7; struct tm says Sunday==0.) */ - int wday = cfg_start_day % 7; - /* How many days do we subtract from today to get to the right day? */ - int delta = (7+tm.tm_wday-wday)%7; - /* If we are on the right day, but the changeover hasn't happened yet, - * then subtract a whole week. */ - if (delta == 0 && before) - delta = 7; - tm.tm_mday -= delta; - if (get_end) - tm.tm_mday += 7; - break; - } - case UNIT_DAY: - if (before) - --tm.tm_mday; - if (get_end) - ++tm.tm_mday; - break; - default: - tor_assert(0); - } - - tm.tm_hour = cfg_start_hour; - tm.tm_min = cfg_start_min; - tm.tm_sec = 0; - tm.tm_isdst = -1; /* Autodetect DST */ - return mktime(&tm); -} - -/** Return the start of the accounting period containing the time - * now. */ -static time_t -start_of_accounting_period_containing(time_t now) -{ - return edge_of_accounting_period_containing(now, 0); -} - -/** Return the start of the accounting period that comes after the one - * containing the time now. */ -static time_t -start_of_accounting_period_after(time_t now) -{ - return edge_of_accounting_period_containing(now, 1); -} - -/** Return the length of the accounting period containing the time - * now. */ -static long -length_of_accounting_period_containing(time_t now) -{ - return edge_of_accounting_period_containing(now, 1) - - edge_of_accounting_period_containing(now, 0); -} - -/** Initialize the accounting subsystem. */ -void -configure_accounting(time_t now) -{ - time_t s_now; - /* Try to remember our recorded usage. */ - if (!interval_start_time) - read_bandwidth_usage(); /* If we fail, we'll leave values at zero, and - * reset below.*/ - - s_now = start_of_accounting_period_containing(now); - - if (!interval_start_time) { - /* We didn't have recorded usage; Start a new interval. */ - log_info(LD_ACCT, "Starting new accounting interval."); - reset_accounting(now); - } else if (s_now == interval_start_time) { - log_info(LD_ACCT, "Continuing accounting interval."); - /* We are in the interval we thought we were in. Do nothing.*/ - interval_end_time = start_of_accounting_period_after(interval_start_time); - } else { - long duration = - length_of_accounting_period_containing(interval_start_time); - double delta = ((double)(s_now - interval_start_time)) / duration; - if (-0.50 <= delta && delta <= 0.50) { - /* The start of the period is now a little later or earlier than we - * remembered. That's fine; we might lose some bytes we could otherwise - * have written, but better to err on the side of obeying people's - * accounting settings. */ - log_info(LD_ACCT, "Accounting interval moved by %.02f%%; " - "that's fine.", delta*100); - interval_end_time = start_of_accounting_period_after(now); - } else if (delta >= 0.99) { - /* This is the regular time-moved-forward case; don't be too noisy - * about it or people will complain */ - log_info(LD_ACCT, "Accounting interval elapsed; starting a new one"); - reset_accounting(now); - } else { - log_warn(LD_ACCT, - "Mismatched accounting interval: moved by %.02f%%. " - "Starting a fresh one.", delta*100); - reset_accounting(now); - } - } - accounting_set_wakeup_time(); -} - -/** Set expected_bandwidth_usage based on how much we sent/received - * per minute last interval (if we were up for at least 30 minutes), - * or based on our declared bandwidth otherwise. */ -static void -update_expected_bandwidth(void) -{ - uint64_t expected; - const or_options_t *options= get_options(); - uint64_t max_configured = (options->RelayBandwidthRate > 0 ? - options->RelayBandwidthRate : - options->BandwidthRate) * 60; - -#define MIN_TIME_FOR_MEASUREMENT (1800) - - if (soft_limit_hit_at > interval_start_time && n_bytes_at_soft_limit && - (soft_limit_hit_at - interval_start_time) > MIN_TIME_FOR_MEASUREMENT) { - /* If we hit our soft limit last time, only count the bytes up to that - * time. This is a better predictor of our actual bandwidth than - * considering the entirety of the last interval, since we likely started - * using bytes very slowly once we hit our soft limit. */ - expected = n_bytes_at_soft_limit / - (soft_limit_hit_at - interval_start_time); - expected /= 60; - } else if (n_seconds_active_in_interval >= MIN_TIME_FOR_MEASUREMENT) { - /* Otherwise, we either measured enough time in the last interval but - * never hit our soft limit, or we're using a state file from a Tor that - * doesn't know to store soft-limit info. Just take rate at which - * we were reading/writing in the last interval as our expected rate. - */ - uint64_t used = MAX(n_bytes_written_in_interval, - n_bytes_read_in_interval); - expected = used / (n_seconds_active_in_interval / 60); - } else { - /* If we haven't gotten enough data last interval, set 'expected' - * to 0. This will set our wakeup to the start of the interval. - * Next interval, we'll choose our starting time based on how much - * we sent this interval. - */ - expected = 0; - } - if (expected > max_configured) - expected = max_configured; - expected_bandwidth_usage = expected; -} - -/** Called at the start of a new accounting interval: reset our - * expected bandwidth usage based on what happened last time, set up - * the start and end of the interval, and clear byte/time totals. - */ -static void -reset_accounting(time_t now) -{ - log_info(LD_ACCT, "Starting new accounting interval."); - update_expected_bandwidth(); - interval_start_time = start_of_accounting_period_containing(now); - interval_end_time = start_of_accounting_period_after(interval_start_time); - n_bytes_read_in_interval = 0; - n_bytes_written_in_interval = 0; - n_seconds_active_in_interval = 0; - n_bytes_at_soft_limit = 0; - soft_limit_hit_at = 0; - n_seconds_to_hit_soft_limit = 0; -} - -/** Return true iff we should save our bandwidth usage to disk. */ -static INLINE int -time_to_record_bandwidth_usage(time_t now) -{ - /* Note every 600 sec */ -#define NOTE_INTERVAL (600) - /* Or every 20 megabytes */ -#define NOTE_BYTES 20*(1024*1024) - static uint64_t last_read_bytes_noted = 0; - static uint64_t last_written_bytes_noted = 0; - static time_t last_time_noted = 0; - - if (last_time_noted + NOTE_INTERVAL <= now || - last_read_bytes_noted + NOTE_BYTES <= n_bytes_read_in_interval || - last_written_bytes_noted + NOTE_BYTES <= n_bytes_written_in_interval || - (interval_end_time && interval_end_time <= now)) { - last_time_noted = now; - last_read_bytes_noted = n_bytes_read_in_interval; - last_written_bytes_noted = n_bytes_written_in_interval; - return 1; - } - return 0; -} - -/** Invoked once per second. Checks whether it is time to hibernate, - * record bandwidth used, etc. */ -void -accounting_run_housekeeping(time_t now) -{ - if (now >= interval_end_time) { - configure_accounting(now); - } - if (time_to_record_bandwidth_usage(now)) { - if (accounting_record_bandwidth_usage(now, get_or_state())) { - log_warn(LD_FS, "Couldn't record bandwidth usage to disk."); - } - } -} - -/** Based on our interval and our estimated bandwidth, choose a - * deterministic (but random-ish) time to wake up. */ -static void -accounting_set_wakeup_time(void) -{ - char digest[DIGEST_LEN]; - crypto_digest_t *d_env; - uint64_t time_to_exhaust_bw; - int time_to_consider; - - if (! server_identity_key_is_set()) { - if (init_keys() < 0) { - log_err(LD_BUG, "Error initializing keys"); - tor_assert(0); - } - } - - if (server_identity_key_is_set()) { - char buf[ISO_TIME_LEN+1]; - format_iso_time(buf, interval_start_time); - - crypto_pk_get_digest(get_server_identity_key(), digest); - - d_env = crypto_digest_new(); - crypto_digest_add_bytes(d_env, buf, ISO_TIME_LEN); - crypto_digest_add_bytes(d_env, digest, DIGEST_LEN); - crypto_digest_get_digest(d_env, digest, DIGEST_LEN); - crypto_digest_free(d_env); - } else { - crypto_rand(digest, DIGEST_LEN); - } - - if (!expected_bandwidth_usage) { - char buf1[ISO_TIME_LEN+1]; - char buf2[ISO_TIME_LEN+1]; - format_local_iso_time(buf1, interval_start_time); - format_local_iso_time(buf2, interval_end_time); - interval_wakeup_time = interval_start_time; - - log_notice(LD_ACCT, - "Configured hibernation. This interval begins at %s " - "and ends at %s. We have no prior estimate for bandwidth, so " - "we will start out awake and hibernate when we exhaust our quota.", - buf1, buf2); - return; - } - - time_to_exhaust_bw = - (get_options()->AccountingMax/expected_bandwidth_usage)*60; - if (time_to_exhaust_bw > INT_MAX) { - time_to_exhaust_bw = INT_MAX; - time_to_consider = 0; - } else { - time_to_consider = accounting_get_interval_length() - - (int)time_to_exhaust_bw; - } - - if (time_to_consider<=0) { - interval_wakeup_time = interval_start_time; - } else { - /* XXX can we simplify this just by picking a random (non-deterministic) - * time to be up? If we go down and come up, then we pick a new one. Is - * that good enough? -RD */ - - /* This is not a perfectly unbiased conversion, but it is good enough: - * in the worst case, the first half of the day is 0.06 percent likelier - * to be chosen than the last half. */ - interval_wakeup_time = interval_start_time + - (get_uint32(digest) % time_to_consider); - } - - { - char buf1[ISO_TIME_LEN+1]; - char buf2[ISO_TIME_LEN+1]; - char buf3[ISO_TIME_LEN+1]; - char buf4[ISO_TIME_LEN+1]; - time_t down_time; - if (interval_wakeup_time+time_to_exhaust_bw > TIME_MAX) - down_time = TIME_MAX; - else - down_time = (time_t)(interval_wakeup_time+time_to_exhaust_bw); - if (down_time>interval_end_time) - down_time = interval_end_time; - format_local_iso_time(buf1, interval_start_time); - format_local_iso_time(buf2, interval_wakeup_time); - format_local_iso_time(buf3, down_time); - format_local_iso_time(buf4, interval_end_time); - - log_notice(LD_ACCT, - "Configured hibernation. This interval began at %s; " - "the scheduled wake-up time %s %s; " - "we expect%s to exhaust our quota for this interval around %s; " - "the next interval begins at %s (all times local)", - buf1, - time(NULL)AccountingIntervalStart = interval_start_time; - state->AccountingBytesReadInInterval = ROUND_UP(n_bytes_read_in_interval); - state->AccountingBytesWrittenInInterval = - ROUND_UP(n_bytes_written_in_interval); - state->AccountingSecondsActive = n_seconds_active_in_interval; - state->AccountingExpectedUsage = expected_bandwidth_usage; - - state->AccountingSecondsToReachSoftLimit = n_seconds_to_hit_soft_limit; - state->AccountingSoftLimitHitAt = soft_limit_hit_at; - state->AccountingBytesAtSoftLimit = n_bytes_at_soft_limit; - - or_state_mark_dirty(state, - now+(get_options()->AvoidDiskWrites ? 7200 : 60)); - - return 0; -} -#undef ROUND_UP - -/** Read stored accounting information from disk. Return 0 on success; - * return -1 and change nothing on failure. */ -static int -read_bandwidth_usage(void) -{ - or_state_t *state = get_or_state(); - - { - char *fname = get_datadir_fname("bw_accounting"); - unlink(fname); - tor_free(fname); - } - - if (!state) - return -1; - - log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); - n_bytes_read_in_interval = state->AccountingBytesReadInInterval; - n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; - n_seconds_active_in_interval = state->AccountingSecondsActive; - interval_start_time = state->AccountingIntervalStart; - expected_bandwidth_usage = state->AccountingExpectedUsage; - - /* Older versions of Tor (before 0.2.2.17-alpha or so) didn't generate these - * fields. If you switch back and forth, you might get an - * AccountingSoftLimitHitAt value from long before the most recent - * interval_start_time. If that's so, then ignore the softlimit-related - * values. */ - if (state->AccountingSoftLimitHitAt > interval_start_time) { - soft_limit_hit_at = state->AccountingSoftLimitHitAt; - n_bytes_at_soft_limit = state->AccountingBytesAtSoftLimit; - n_seconds_to_hit_soft_limit = state->AccountingSecondsToReachSoftLimit; - } else { - soft_limit_hit_at = 0; - n_bytes_at_soft_limit = 0; - n_seconds_to_hit_soft_limit = 0; - } - - { - char tbuf1[ISO_TIME_LEN+1]; - char tbuf2[ISO_TIME_LEN+1]; - format_iso_time(tbuf1, state->LastWritten); - format_iso_time(tbuf2, state->AccountingIntervalStart); - - log_info(LD_ACCT, - "Successfully read bandwidth accounting info from state written at %s " - "for interval starting at %s. We have been active for %lu seconds in " - "this interval. At the start of the interval, we expected to use " - "about %lu KB per second. ("U64_FORMAT" bytes read so far, " - U64_FORMAT" bytes written so far)", - tbuf1, tbuf2, - (unsigned long)n_seconds_active_in_interval, - (unsigned long)(expected_bandwidth_usage*1024/60), - U64_PRINTF_ARG(n_bytes_read_in_interval), - U64_PRINTF_ARG(n_bytes_written_in_interval)); - } - - return 0; -} - -/** Return true iff we have sent/received all the bytes we are willing - * to send/receive this interval. */ -static int -hibernate_hard_limit_reached(void) -{ - uint64_t hard_limit = get_options()->AccountingMax; - if (!hard_limit) - return 0; - return n_bytes_read_in_interval >= hard_limit - || n_bytes_written_in_interval >= hard_limit; -} - -/** Return true iff we have sent/received almost all the bytes we are willing - * to send/receive this interval. */ -static int -hibernate_soft_limit_reached(void) -{ - const uint64_t acct_max = get_options()->AccountingMax; -#define SOFT_LIM_PCT (.95) -#define SOFT_LIM_BYTES (500*1024*1024) -#define SOFT_LIM_MINUTES (3*60) - /* The 'soft limit' is a fair bit more complicated now than once it was. - * We want to stop accepting connections when ALL of the following are true: - * - We expect to use up the remaining bytes in under 3 hours - * - We have used up 95% of our bytes. - * - We have less than 500MB of bytes left. - */ - uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(acct_max) * SOFT_LIM_PCT); - if (acct_max > SOFT_LIM_BYTES && acct_max - SOFT_LIM_BYTES > soft_limit) { - soft_limit = acct_max - SOFT_LIM_BYTES; - } - if (expected_bandwidth_usage) { - const uint64_t expected_usage = - expected_bandwidth_usage * SOFT_LIM_MINUTES; - if (acct_max > expected_usage && acct_max - expected_usage > soft_limit) - soft_limit = acct_max - expected_usage; - } - - if (!soft_limit) - return 0; - return n_bytes_read_in_interval >= soft_limit - || n_bytes_written_in_interval >= soft_limit; -} - -/** Called when we get a SIGINT, or when bandwidth soft limit is - * reached. Puts us into "loose hibernation": we don't accept new - * connections, but we continue handling old ones. */ -static void -hibernate_begin(hibernate_state_t new_state, time_t now) -{ - const or_options_t *options = get_options(); - - if (new_state == HIBERNATE_STATE_EXITING && - hibernate_state != HIBERNATE_STATE_LIVE) { - log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", - hibernate_state == HIBERNATE_STATE_EXITING ? - "a second time" : "while hibernating"); - tor_cleanup(); - exit(0); - } - - if (new_state == HIBERNATE_STATE_LOWBANDWIDTH && - hibernate_state == HIBERNATE_STATE_LIVE) { - soft_limit_hit_at = now; - n_seconds_to_hit_soft_limit = n_seconds_active_in_interval; - n_bytes_at_soft_limit = MAX(n_bytes_read_in_interval, - n_bytes_written_in_interval); - } - - /* close listeners. leave control listener(s). */ - connection_mark_all_noncontrol_listeners(); - - /* XXX kill intro point circs */ - /* XXX upload rendezvous service descriptors with no intro points */ - - if (new_state == HIBERNATE_STATE_EXITING) { - log_notice(LD_GENERAL,"Interrupt: we have stopped accepting new " - "connections, and will shut down in %d seconds. Interrupt " - "again to exit now.", options->ShutdownWaitLength); - shutdown_time = time(NULL) + options->ShutdownWaitLength; - } else { /* soft limit reached */ - hibernate_end_time = interval_end_time; - } - - hibernate_state = new_state; - accounting_record_bandwidth_usage(now, get_or_state()); - - or_state_mark_dirty(get_or_state(), - get_options()->AvoidDiskWrites ? now+600 : 0); -} - -/** Called when we've been hibernating and our timeout is reached. */ -static void -hibernate_end(hibernate_state_t new_state) -{ - tor_assert(hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH || - hibernate_state == HIBERNATE_STATE_DORMANT || - hibernate_state == HIBERNATE_STATE_INITIAL); - - /* listeners will be relaunched in run_scheduled_events() in onion_main.c */ - if (hibernate_state != HIBERNATE_STATE_INITIAL) - log_notice(LD_ACCT,"Hibernation period ended. Resuming normal activity."); - - hibernate_state = new_state; - hibernate_end_time = 0; /* no longer hibernating */ - stats_n_seconds_working = 0; /* reset published uptime */ -} - -/** A wrapper around hibernate_begin, for when we get SIGINT. */ -void -hibernate_begin_shutdown(void) -{ - hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL)); -} - -/** Return true iff we are currently hibernating. */ -int -we_are_hibernating(void) -{ - return hibernate_state != HIBERNATE_STATE_LIVE; -} - -/** If we aren't currently dormant, close all connections and become - * dormant. */ -static void -hibernate_go_dormant(time_t now) -{ - connection_t *conn; - - if (hibernate_state == HIBERNATE_STATE_DORMANT) - return; - else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) - hibernate_state = HIBERNATE_STATE_DORMANT; - else - hibernate_begin(HIBERNATE_STATE_DORMANT, now); - - log_notice(LD_ACCT,"Going dormant. Blowing away remaining connections."); - - /* Close all OR/AP/exit conns. Leave dir conns because we still want - * to be able to upload server descriptors so people know we're still - * running, and download directories so we can detect if we're obsolete. - * Leave control conns because we still want to be controllable. - */ - while ((conn = connection_get_by_type(CONN_TYPE_OR)) || - (conn = connection_get_by_type(CONN_TYPE_AP)) || - (conn = connection_get_by_type(CONN_TYPE_EXIT))) { - if (CONN_IS_EDGE(conn)) - connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); - log_info(LD_NET,"Closing conn type %d", conn->type); - if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ - connection_mark_unattached_ap(TO_ENTRY_CONN(conn), - END_STREAM_REASON_HIBERNATING); - else if (conn->type == CONN_TYPE_OR) { - if (TO_OR_CONN(conn)->chan) { - channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); - } else { - connection_mark_for_close(conn); - } - } else - connection_mark_for_close(conn); - } - - if (now < interval_wakeup_time) - hibernate_end_time = interval_wakeup_time; - else - hibernate_end_time = interval_end_time; - - accounting_record_bandwidth_usage(now, get_or_state()); - - or_state_mark_dirty(get_or_state(), - get_options()->AvoidDiskWrites ? now+600 : 0); -} - -/** Called when hibernate_end_time has arrived. */ -static void -hibernate_end_time_elapsed(time_t now) -{ - char buf[ISO_TIME_LEN+1]; - - /* The interval has ended, or it is wakeup time. Find out which. */ - accounting_run_housekeeping(now); - if (interval_wakeup_time <= now) { - /* The interval hasn't changed, but interval_wakeup_time has passed. - * It's time to wake up and start being a server. */ - hibernate_end(HIBERNATE_STATE_LIVE); - return; - } else { - /* The interval has changed, and it isn't time to wake up yet. */ - hibernate_end_time = interval_wakeup_time; - format_iso_time(buf,interval_wakeup_time); - if (hibernate_state != HIBERNATE_STATE_DORMANT) { - /* We weren't sleeping before; we should sleep now. */ - log_notice(LD_ACCT, - "Accounting period ended. Commencing hibernation until " - "%s UTC", buf); - hibernate_go_dormant(now); - } else { - log_notice(LD_ACCT, - "Accounting period ended. This period, we will hibernate" - " until %s UTC",buf); - } - } -} - -/** Consider our environment and decide if it's time - * to start/stop hibernating. - */ -void -consider_hibernation(time_t now) -{ - int accounting_enabled = get_options()->AccountingMax != 0; - char buf[ISO_TIME_LEN+1]; - - /* If we're in 'exiting' mode, then we just shut down after the interval - * elapses. */ - if (hibernate_state == HIBERNATE_STATE_EXITING) { - tor_assert(shutdown_time); - if (shutdown_time <= now) { - log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); - tor_cleanup(); - exit(0); - } - return; /* if exiting soon, don't worry about bandwidth limits */ - } - - if (hibernate_state == HIBERNATE_STATE_DORMANT) { - /* We've been hibernating because of bandwidth accounting. */ - tor_assert(hibernate_end_time); - if (hibernate_end_time > now && accounting_enabled) { - /* If we're hibernating, don't wake up until it's time, regardless of - * whether we're in a new interval. */ - return ; - } else { - hibernate_end_time_elapsed(now); - } - } - - /* Else, we aren't hibernating. See if it's time to start hibernating, or to - * go dormant. */ - if (hibernate_state == HIBERNATE_STATE_LIVE || - hibernate_state == HIBERNATE_STATE_INITIAL) { - if (hibernate_soft_limit_reached()) { - log_notice(LD_ACCT, - "Bandwidth soft limit reached; commencing hibernation. " - "No new connections will be accepted"); - hibernate_begin(HIBERNATE_STATE_LOWBANDWIDTH, now); - } else if (accounting_enabled && now < interval_wakeup_time) { - format_local_iso_time(buf,interval_wakeup_time); - log_notice(LD_ACCT, - "Commencing hibernation. We will wake up at %s local time.", - buf); - hibernate_go_dormant(now); - } else if (hibernate_state == HIBERNATE_STATE_INITIAL) { - hibernate_end(HIBERNATE_STATE_LIVE); - } - } - - if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) { - if (!accounting_enabled) { - hibernate_end_time_elapsed(now); - } else if (hibernate_hard_limit_reached()) { - hibernate_go_dormant(now); - } else if (hibernate_end_time <= now) { - /* The hibernation period ended while we were still in lowbandwidth.*/ - hibernate_end_time_elapsed(now); - } - } -} - -/** Helper function: called when we get a GETINFO request for an - * accounting-related key on the control connection conn. If we can - * answer the request for question, then set *answer to a newly - * allocated string holding the result. Otherwise, set *answer to - * NULL. */ -int -getinfo_helper_accounting(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg) -{ - (void) conn; - (void) errmsg; - if (!strcmp(question, "accounting/enabled")) { - *answer = tor_strdup(accounting_is_enabled(get_options()) ? "1" : "0"); - } else if (!strcmp(question, "accounting/hibernating")) { - if (hibernate_state == HIBERNATE_STATE_DORMANT) - *answer = tor_strdup("hard"); - else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) - *answer = tor_strdup("soft"); - else - *answer = tor_strdup("awake"); - } else if (!strcmp(question, "accounting/bytes")) { - tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, - U64_PRINTF_ARG(n_bytes_read_in_interval), - U64_PRINTF_ARG(n_bytes_written_in_interval)); - } else if (!strcmp(question, "accounting/bytes-left")) { - uint64_t limit = get_options()->AccountingMax; - uint64_t read_left = 0, write_left = 0; - if (n_bytes_read_in_interval < limit) - read_left = limit - n_bytes_read_in_interval; - if (n_bytes_written_in_interval < limit) - write_left = limit - n_bytes_written_in_interval; - tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, - U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(write_left)); - } else if (!strcmp(question, "accounting/interval-start")) { - *answer = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(*answer, interval_start_time); - } else if (!strcmp(question, "accounting/interval-wake")) { - *answer = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(*answer, interval_wakeup_time); - } else if (!strcmp(question, "accounting/interval-end")) { - *answer = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(*answer, interval_end_time); - } else { - *answer = NULL; - } - return 0; -} - -#ifdef TOR_UNIT_TESTS -/** - * Manually change the hibernation state. Private; used only by the unit - * tests. - */ -void -hibernate_set_state_for_testing_(hibernate_state_t newstate) -{ - hibernate_state = newstate; -} -#endif - diff --git a/src/tor/hibernate.h b/src/tor/hibernate.h deleted file mode 100644 index 4f7331c..0000000 --- a/src/tor/hibernate.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file hibernate.h - * \brief Header file for hibernate.c. - **/ - -#ifndef TOR_HIBERNATE_H -#define TOR_HIBERNATE_H - -int accounting_parse_options(const or_options_t *options, int validate_only); -int accounting_is_enabled(const or_options_t *options); -int accounting_get_interval_length(void); -time_t accounting_get_end_time(void); -void configure_accounting(time_t now); -void accounting_run_housekeeping(time_t now); -void accounting_add_bytes(size_t n_read, size_t n_written, int seconds); -int accounting_record_bandwidth_usage(time_t now, or_state_t *state); -void hibernate_begin_shutdown(void); -int we_are_hibernating(void); -void consider_hibernation(time_t now); -int getinfo_helper_accounting(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg); - -#ifdef HIBERNATE_PRIVATE -/** Possible values of hibernate_state */ -typedef enum { - /** We are running normally. */ - HIBERNATE_STATE_LIVE=1, - /** We're trying to shut down cleanly, and we'll kill all active connections - * at shutdown_time. */ - HIBERNATE_STATE_EXITING=2, - /** We're running low on allocated bandwidth for this period, so we won't - * accept any new connections. */ - HIBERNATE_STATE_LOWBANDWIDTH=3, - /** We are hibernating, and we won't wake up till there's more bandwidth to - * use. */ - HIBERNATE_STATE_DORMANT=4, - /** We start out in state default, which means we havent decided which state - * we're in. */ - HIBERNATE_STATE_INITIAL=5 -} hibernate_state_t; - -#ifdef TOR_UNIT_TESTS -void hibernate_set_state_for_testing_(hibernate_state_t newstate); -#endif -#endif - -#endif - diff --git a/src/tor/ht.h b/src/tor/ht.h deleted file mode 100644 index 669a2ed..0000000 --- a/src/tor/ht.h +++ /dev/null @@ -1,490 +0,0 @@ -/* Copyright (c) 2002, Christopher Clark. - * Copyright (c) 2005-2006, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See license at end. */ - -/* Based on ideas by Christopher Clark and interfaces from Niels Provos. */ - -#ifndef HT_H_INCLUDED_ -#define HT_H_INCLUDED_ - -#define HT_HEAD(name, type) \ - struct name { \ - /* The hash table itself. */ \ - struct type **hth_table; \ - /* How long is the hash table? */ \ - unsigned hth_table_length; \ - /* How many elements does the table contain? */ \ - unsigned hth_n_entries; \ - /* How many elements will we allow in the table before resizing it? */ \ - unsigned hth_load_limit; \ - /* Position of hth_table_length in the primes table. */ \ - int hth_prime_idx; \ - } - -#define HT_INITIALIZER() \ - { NULL, 0, 0, 0, -1 } - -#ifdef HT_NO_CACHE_HASH_VALUES -#define HT_ENTRY(type) \ - struct { \ - struct type *hte_next; \ - } -#else -#define HT_ENTRY(type) \ - struct { \ - struct type *hte_next; \ - unsigned hte_hash; \ - } -#endif - -#define HT_EMPTY(head) \ - ((head)->hth_n_entries == 0) - -/* How many elements in 'head'? */ -#define HT_SIZE(head) \ - ((head)->hth_n_entries) - -/* Return memory usage for a hashtable (not counting the entries themselves) */ -#define HT_MEM_USAGE(head) \ - (sizeof(*head) + (head)->hth_table_length * sizeof(void*)) - -#define HT_FIND(name, head, elm) name##_HT_FIND((head), (elm)) -#define HT_INSERT(name, head, elm) name##_HT_INSERT((head), (elm)) -#define HT_REPLACE(name, head, elm) name##_HT_REPLACE((head), (elm)) -#define HT_REMOVE(name, head, elm) name##_HT_REMOVE((head), (elm)) -#define HT_START(name, head) name##_HT_START(head) -#define HT_NEXT(name, head, elm) name##_HT_NEXT((head), (elm)) -#define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm)) -#define HT_CLEAR(name, head) name##_HT_CLEAR(head) -#define HT_INIT(name, head) name##_HT_INIT(head) -/* Helper: */ -static INLINE unsigned -ht_improve_hash(unsigned h) -{ - /* Aim to protect against poor hash functions by adding logic here - * - logic taken from java 1.4 hashtable source */ - h += ~(h << 9); - h ^= ((h >> 14) | (h << 18)); /* >>> */ - h += (h << 4); - h ^= ((h >> 10) | (h << 22)); /* >>> */ - return h; -} - -#if 0 -/** Basic string hash function, from Java standard String.hashCode(). */ -static INLINE unsigned -ht_string_hash(const char *s) -{ - unsigned h = 0; - int m = 1; - while (*s) { - h += ((signed char)*s++)*m; - m = (m<<5)-1; /* m *= 31 */ - } - return h; -} -#endif - -/** Basic string hash function, from Python's str.__hash__() */ -static INLINE unsigned -ht_string_hash(const char *s) -{ - unsigned h; - const unsigned char *cp = (const unsigned char *)s; - h = *cp << 7; - while (*cp) { - h = (1000003*h) ^ *cp++; - } - /* This conversion truncates the length of the string, but that's ok. */ - h ^= (unsigned)(cp-(const unsigned char*)s); - return h; -} - -#ifndef HT_NO_CACHE_HASH_VALUES -#define HT_SET_HASH_(elm, field, hashfn) \ - do { (elm)->field.hte_hash = hashfn(elm); } while (0) -#define HT_SET_HASHVAL_(elm, field, val) \ - do { (elm)->field.hte_hash = (val); } while (0) -#define HT_ELT_HASH_(elm, field, hashfn) \ - ((elm)->field.hte_hash) -#else -#define HT_SET_HASH_(elm, field, hashfn) \ - ((void)0) -#define HT_ELT_HASH_(elm, field, hashfn) \ - (hashfn(elm)) -#define HT_SET_HASHVAL_(elm, field, val) \ - ((void)0) -#endif - -/* Helper: alias for the bucket containing 'elm'. */ -#define HT_BUCKET_(head, field, elm, hashfn) \ - ((head)->hth_table[HT_ELT_HASH_(elm,field,hashfn) \ - % head->hth_table_length]) - -#define HT_FOREACH(x, name, head) \ - for ((x) = HT_START(name, head); \ - (x) != NULL; \ - (x) = HT_NEXT(name, head, x)) - -#define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \ - int name##_HT_GROW(struct name *ht, unsigned min_capacity); \ - void name##_HT_CLEAR(struct name *ht); \ - int name##_HT_REP_IS_BAD_(const struct name *ht); \ - static INLINE void \ - name##_HT_INIT(struct name *head) { \ - head->hth_table_length = 0; \ - head->hth_table = NULL; \ - head->hth_n_entries = 0; \ - head->hth_load_limit = 0; \ - head->hth_prime_idx = -1; \ - } \ - /* Helper: returns a pointer to the right location in the table \ - * 'head' to find or insert the element 'elm'. */ \ - static INLINE struct type ** \ - name##_HT_FIND_P_(struct name *head, struct type *elm) \ - { \ - struct type **p; \ - if (!head->hth_table) \ - return NULL; \ - p = &HT_BUCKET_(head, field, elm, hashfn); \ - while (*p) { \ - if (eqfn(*p, elm)) \ - return p; \ - p = &(*p)->field.hte_next; \ - } \ - return p; \ - } \ - /* Return a pointer to the element in the table 'head' matching 'elm', \ - * or NULL if no such element exists */ \ - static INLINE struct type * \ - name##_HT_FIND(const struct name *head, struct type *elm) \ - { \ - struct type **p; \ - struct name *h = (struct name *) head; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(h, elm); \ - return p ? *p : NULL; \ - } \ - /* Insert the element 'elm' into the table 'head'. Do not call this \ - * function if the table might already contain a matching element. */ \ - static INLINE void \ - name##_HT_INSERT(struct name *head, struct type *elm) \ - { \ - struct type **p; \ - if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ - name##_HT_GROW(head, head->hth_n_entries+1); \ - ++head->hth_n_entries; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = &HT_BUCKET_(head, field, elm, hashfn); \ - elm->field.hte_next = *p; \ - *p = elm; \ - } \ - /* Insert the element 'elm' into the table 'head'. If there already \ - * a matching element in the table, replace that element and return \ - * it. */ \ - static INLINE struct type * \ - name##_HT_REPLACE(struct name *head, struct type *elm) \ - { \ - struct type **p, *r; \ - if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ - name##_HT_GROW(head, head->hth_n_entries+1); \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(head, elm); \ - r = *p; \ - *p = elm; \ - if (r && (r!=elm)) { \ - elm->field.hte_next = r->field.hte_next; \ - r->field.hte_next = NULL; \ - return r; \ - } else { \ - ++head->hth_n_entries; \ - return NULL; \ - } \ - } \ - /* Remove any element matching 'elm' from the table 'head'. If such \ - * an element is found, return it; otherwise return NULL. */ \ - static INLINE struct type * \ - name##_HT_REMOVE(struct name *head, struct type *elm) \ - { \ - struct type **p, *r; \ - HT_SET_HASH_(elm, field, hashfn); \ - p = name##_HT_FIND_P_(head,elm); \ - if (!p || !*p) \ - return NULL; \ - r = *p; \ - *p = r->field.hte_next; \ - r->field.hte_next = NULL; \ - --head->hth_n_entries; \ - return r; \ - } \ - /* Invoke the function 'fn' on every element of the table 'head', \ - * using 'data' as its second argument. If the function returns \ - * nonzero, remove the most recently examined element before invoking \ - * the function again. */ \ - static INLINE void \ - name##_HT_FOREACH_FN(struct name *head, \ - int (*fn)(struct type *, void *), \ - void *data) \ - { \ - unsigned idx; \ - struct type **p, **nextp, *next; \ - if (!head->hth_table) \ - return; \ - for (idx=0; idx < head->hth_table_length; ++idx) { \ - p = &head->hth_table[idx]; \ - while (*p) { \ - nextp = &(*p)->field.hte_next; \ - next = *nextp; \ - if (fn(*p, data)) { \ - --head->hth_n_entries; \ - *p = next; \ - } else { \ - p = nextp; \ - } \ - } \ - } \ - } \ - /* Return a pointer to the first element in the table 'head', under \ - * an arbitrary order. This order is stable under remove operations, \ - * but not under others. If the table is empty, return NULL. */ \ - static INLINE struct type ** \ - name##_HT_START(struct name *head) \ - { \ - unsigned b = 0; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - /* Return the next element in 'head' after 'elm', under the arbitrary \ - * order used by HT_START. If there are no more elements, return \ - * NULL. If 'elm' is to be removed from the table, you must call \ - * this function for the next value before you remove it. \ - */ \ - static INLINE struct type ** \ - name##_HT_NEXT(struct name *head, struct type **elm) \ - { \ - if ((*elm)->field.hte_next) { \ - return &(*elm)->field.hte_next; \ - } else { \ - unsigned b = (HT_ELT_HASH_(*elm, field, hashfn) \ - % head->hth_table_length)+1; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - } \ - static INLINE struct type ** \ - name##_HT_NEXT_RMV(struct name *head, struct type **elm) \ - { \ - unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \ - *elm = (*elm)->field.hte_next; \ - --head->hth_n_entries; \ - if (*elm) { \ - return elm; \ - } else { \ - unsigned b = (h % head->hth_table_length)+1; \ - while (b < head->hth_table_length) { \ - if (head->hth_table[b]) \ - return &head->hth_table[b]; \ - ++b; \ - } \ - return NULL; \ - } \ - } - -#define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \ - reallocfn, freefn) \ - static unsigned name##_PRIMES[] = { \ - 53, 97, 193, 389, \ - 769, 1543, 3079, 6151, \ - 12289, 24593, 49157, 98317, \ - 196613, 393241, 786433, 1572869, \ - 3145739, 6291469, 12582917, 25165843, \ - 50331653, 100663319, 201326611, 402653189, \ - 805306457, 1610612741 \ - }; \ - static unsigned name##_N_PRIMES = \ - (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \ - /* Expand the internal table of 'head' until it is large enough to \ - * hold 'size' elements. Return 0 on success, -1 on allocation \ - * failure. */ \ - int \ - name##_HT_GROW(struct name *head, unsigned size) \ - { \ - unsigned new_len, new_load_limit; \ - int prime_idx; \ - struct type **new_table; \ - if (head->hth_prime_idx == (int)name##_N_PRIMES - 1) \ - return 0; \ - if (head->hth_load_limit > size) \ - return 0; \ - prime_idx = head->hth_prime_idx; \ - do { \ - new_len = name##_PRIMES[++prime_idx]; \ - new_load_limit = (unsigned)(load*new_len); \ - } while (new_load_limit <= size && \ - prime_idx < (int)name##_N_PRIMES); \ - if ((new_table = mallocfn(new_len*sizeof(struct type*)))) { \ - unsigned b; \ - memset(new_table, 0, new_len*sizeof(struct type*)); \ - for (b = 0; b < head->hth_table_length; ++b) { \ - struct type *elm, *next; \ - unsigned b2; \ - elm = head->hth_table[b]; \ - while (elm) { \ - next = elm->field.hte_next; \ - b2 = HT_ELT_HASH_(elm, field, hashfn) % new_len; \ - elm->field.hte_next = new_table[b2]; \ - new_table[b2] = elm; \ - elm = next; \ - } \ - } \ - if (head->hth_table) \ - freefn(head->hth_table); \ - head->hth_table = new_table; \ - } else { \ - unsigned b, b2; \ - new_table = reallocfn(head->hth_table, new_len*sizeof(struct type*)); \ - if (!new_table) return -1; \ - memset(new_table + head->hth_table_length, 0, \ - (new_len - head->hth_table_length)*sizeof(struct type*)); \ - for (b=0; b < head->hth_table_length; ++b) { \ - struct type *e, **pE; \ - for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \ - b2 = HT_ELT_HASH_(e, field, hashfn) % new_len; \ - if (b2 == b) { \ - pE = &e->field.hte_next; \ - } else { \ - *pE = e->field.hte_next; \ - e->field.hte_next = new_table[b2]; \ - new_table[b2] = e; \ - } \ - } \ - } \ - head->hth_table = new_table; \ - } \ - head->hth_table_length = new_len; \ - head->hth_prime_idx = prime_idx; \ - head->hth_load_limit = new_load_limit; \ - return 0; \ - } \ - /* Free all storage held by 'head'. Does not free 'head' itself, or \ - * individual elements. */ \ - void \ - name##_HT_CLEAR(struct name *head) \ - { \ - if (head->hth_table) \ - freefn(head->hth_table); \ - head->hth_table_length = 0; \ - name##_HT_INIT(head); \ - } \ - /* Debugging helper: return false iff the representation of 'head' is \ - * internally consistent. */ \ - int \ - name##_HT_REP_IS_BAD_(const struct name *head) \ - { \ - unsigned n, i; \ - struct type *elm; \ - if (!head->hth_table_length) { \ - if (!head->hth_table && !head->hth_n_entries && \ - !head->hth_load_limit && head->hth_prime_idx == -1) \ - return 0; \ - else \ - return 1; \ - } \ - if (!head->hth_table || head->hth_prime_idx < 0 || \ - !head->hth_load_limit) \ - return 2; \ - if (head->hth_n_entries > head->hth_load_limit) \ - return 3; \ - if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \ - return 4; \ - if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \ - return 5; \ - for (n = i = 0; i < head->hth_table_length; ++i) { \ - for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \ - if (HT_ELT_HASH_(elm, field, hashfn) != hashfn(elm)) \ - return 1000 + i; \ - if ((HT_ELT_HASH_(elm, field, hashfn) % head->hth_table_length) != i) \ - return 10000 + i; \ - ++n; \ - } \ - } \ - if (n != head->hth_n_entries) \ - return 6; \ - return 0; \ - } - -/** Implements an over-optimized "find and insert if absent" block; - * not meant for direct usage by typical code, or usage outside the critical - * path.*/ -#define HT_FIND_OR_INSERT_(name, field, hashfn, head, eltype, elm, var, y, n) \ - { \ - struct name *var##_head_ = head; \ - struct eltype **var; \ - if (!var##_head_->hth_table || \ - var##_head_->hth_n_entries >= var##_head_->hth_load_limit) \ - name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \ - HT_SET_HASH_((elm), field, hashfn); \ - var = name##_HT_FIND_P_(var##_head_, (elm)); \ - if (*var) { \ - y; \ - } else { \ - n; \ - } \ - } -#define HT_FOI_INSERT_(field, head, elm, newent, var) \ - { \ - HT_SET_HASHVAL_(newent, field, (elm)->field.hte_hash); \ - newent->field.hte_next = NULL; \ - *var = newent; \ - ++((head)->hth_n_entries); \ - } - -/* - * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code - * by Christopher Clark, retrofit to allow drop-in memory management, and to - * use the same interface as Niels Provos's tree.h. This is probably still - * a derived work, so the original license below still applies. - * - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#endif - diff --git a/src/tor/log.c b/src/tor/log.c deleted file mode 100644 index 6e3e95f..0000000 --- a/src/tor/log.c +++ /dev/null @@ -1,1316 +0,0 @@ -/* Copyright (c) 2001, Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file log.c - * \brief Functions to send messages to log files or the console. - **/ - -#include "orconfig.h" -#include -#include -// #include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_TIME_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#include "tor_compat.h" -#include "tor_util.h" -#define LOG_PRIVATE -#include "torlog.h" -#include "container.h" - -/** Given a severity, yields an index into log_severity_list_t.masks to use - * for that severity. */ -#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR) - -/** @{ */ -/** The string we stick at the end of a log message when it is too long, - * and its length. */ -#define TRUNCATED_STR "[...truncated]" -#define TRUNCATED_STR_LEN 14 -/** @} */ - -/** Information for a single logfile; only used in log.c */ -typedef struct logfile_t { - struct logfile_t *next; /**< Next logfile_t in the linked list. */ - char *filename; /**< Filename to open. */ - int fd; /**< fd to receive log messages, or -1 for none. */ - int seems_dead; /**< Boolean: true if the stream seems to be kaput. */ - int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */ - int is_temporary; /**< Boolean: close after initializing logging subsystem.*/ - int is_syslog; /**< Boolean: send messages to syslog. */ - log_callback callback; /**< If not NULL, send messages to this function. */ - log_severity_list_t *severities; /**< Which severity of messages should we - * log for each log domain? */ -} logfile_t; - -static void log_free(logfile_t *victim); - -/** Helper: map a log severity to descriptive string. */ -static INLINE const char * -sev_to_string(int severity) -{ - switch (severity) { - case LOG_DEBUG: return "debug"; - case LOG_INFO: return "info"; - case LOG_NOTICE: return "notice"; - case LOG_WARN: return "warn"; - case LOG_ERR: return "err"; - default: /* Call assert, not tor_assert, since tor_assert - * calls log on failure. */ - assert(0); return "UNKNOWN"; - } -} - -/** Helper: decide whether to include the function name in the log message. */ -static INLINE int -should_log_function_name(log_domain_mask_t domain, int severity) -{ - switch (severity) { - case LOG_DEBUG: - case LOG_INFO: - /* All debugging messages occur in interesting places. */ - return (domain & LD_NOFUNCNAME) == 0; - case LOG_NOTICE: - case LOG_WARN: - case LOG_ERR: - /* We care about places where bugs occur. */ - return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG; - default: - /* Call assert, not tor_assert, since tor_assert calls log on failure. */ - assert(0); return 0; - } -} - -/** A mutex to guard changes to logfiles and logging. */ -static tor_mutex_t log_mutex; -/** True iff we have initialized log_mutex */ -static int log_mutex_initialized = 0; - -/** Linked list of logfile_t. */ -static logfile_t *logfiles = NULL; -/** Boolean: do we report logging domains? */ -static int log_domains_are_logged = 0; - -#ifdef HAVE_SYSLOG_H -/** The number of open syslog log handlers that we have. When this reaches 0, - * we can close our connection to the syslog facility. */ -static int syslog_count = 0; -#endif - -/** Represents a log message that we are going to send to callback-driven - * loggers once we can do so in a non-reentrant way. */ -typedef struct pending_cb_message_t { - int severity; /**< The severity of the message */ - log_domain_mask_t domain; /**< The domain of the message */ - char *msg; /**< The content of the message */ -} pending_cb_message_t; - -/** Log messages waiting to be replayed onto callback-based logs */ -static smartlist_t *pending_cb_messages = NULL; - -/** Lock the log_mutex to prevent others from changing the logfile_t list */ -#define LOCK_LOGS() STMT_BEGIN \ - tor_mutex_acquire(&log_mutex); \ - STMT_END -/** Unlock the log_mutex */ -#define UNLOCK_LOGS() STMT_BEGIN tor_mutex_release(&log_mutex); STMT_END - -/** What's the lowest log level anybody cares about? Checking this lets us - * bail out early from log_debug if we aren't debugging. */ -int log_global_min_severity_ = LOG_NOTICE; - -static void delete_log(logfile_t *victim); -static void close_log(logfile_t *victim); - -static char *domain_to_string(log_domain_mask_t domain, - char *buf, size_t buflen); -static INLINE char *format_msg(char *buf, size_t buf_len, - log_domain_mask_t domain, int severity, const char *funcname, - const char *suffix, - const char *format, va_list ap, size_t *msg_len_out) - CHECK_PRINTF(7,0); -static void logv(int severity, log_domain_mask_t domain, const char *funcname, - const char *suffix, const char *format, va_list ap) - CHECK_PRINTF(5,0); - -/** Name of the application: used to generate the message we write at the - * start of each new log. */ -static char *appname = NULL; - -/** Set the "application name" for the logs to name: we'll use this - * name in the message we write when starting up, and at the start of each new - * log. - * - * Tor uses this string to write the version number to the log file. */ -void -log_set_application_name(const char *name) -{ - tor_free(appname); - appname = name ? tor_strdup(name) : NULL; -} - -/** Log time granularity in milliseconds. */ -static int log_time_granularity = 1; - -/** Define log time granularity for all logs to be granularity_msec - * milliseconds. */ -void -set_log_time_granularity(int granularity_msec) -{ - log_time_granularity = granularity_msec; -} - -/** Helper: Write the standard prefix for log lines to a - * buf_len character buffer in buf. - */ -static INLINE size_t -log_prefix_(char *buf, size_t buf_len, int severity) -{ - time_t t; - struct timeval now; - struct tm tm; - size_t n; - int r, ms; - - tor_gettimeofday(&now); - t = (time_t)now.tv_sec; - ms = (int)now.tv_usec / 1000; - if (log_time_granularity >= 1000) { - t -= t % (log_time_granularity / 1000); - ms = 0; - } else { - ms -= ((int)now.tv_usec / 1000) % log_time_granularity; - } - - n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm)); - r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", ms, - sev_to_string(severity)); - - if (r<0) - return buf_len-1; - else - return n+r; -} - -/** If lf refers to an actual file that we have just opened, and the file - * contains no data, log an "opening new logfile" message at the top. - * - * Return -1 if the log is broken and needs to be deleted, else return 0. - */ -static int -log_tor_version(logfile_t *lf, int reset) -{ - char buf[256]; - size_t n; - int is_new; - - if (!lf->needs_close) - /* If it doesn't get closed, it isn't really a file. */ - return 0; - if (lf->is_temporary) - /* If it's temporary, it isn't really a file. */ - return 0; - - is_new = lf->fd >= 0 && tor_fd_getpos(lf->fd) == 0; - - if (reset && !is_new) - /* We are resetting, but we aren't at the start of the file; no - * need to log again. */ - return 0; - n = log_prefix_(buf, sizeof(buf), LOG_NOTICE); - if (appname) { - tor_snprintf(buf+n, sizeof(buf)-n, - "%s opening %slog file.\n", appname, is_new?"new ":""); - } else { - tor_snprintf(buf+n, sizeof(buf)-n, - "Tor %s opening %slog file.\n", VERSION, is_new?"new ":""); - } - if (write_all(lf->fd, buf, strlen(buf), 0) < 0) /* error */ - return -1; /* failed */ - return 0; -} - -/** Helper: Format a log message into a fixed-sized buffer. (This is - * factored out of logv so that we never format a message more - * than once.) Return a pointer to the first character of the message - * portion of the formatted string. - */ -static INLINE char * -format_msg(char *buf, size_t buf_len, - log_domain_mask_t domain, int severity, const char *funcname, - const char *suffix, - const char *format, va_list ap, size_t *msg_len_out) -{ - size_t n; - int r; - char *end_of_prefix; - char *buf_end; - - assert(buf_len >= 16); /* prevent integer underflow and general stupidity */ - buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ - buf_end = buf+buf_len; /* point *after* the last char we can write to */ - - n = log_prefix_(buf, buf_len, severity); - end_of_prefix = buf+n; - - if (log_domains_are_logged) { - char *cp = buf+n; - if (cp == buf_end) goto format_msg_no_room_for_domains; - *cp++ = '{'; - if (cp == buf_end) goto format_msg_no_room_for_domains; - cp = domain_to_string(domain, cp, (buf+buf_len-cp)); - if (cp == buf_end) goto format_msg_no_room_for_domains; - *cp++ = '}'; - if (cp == buf_end) goto format_msg_no_room_for_domains; - *cp++ = ' '; - if (cp == buf_end) goto format_msg_no_room_for_domains; - end_of_prefix = cp; - n = cp-buf; - format_msg_no_room_for_domains: - /* This will leave end_of_prefix and n unchanged, and thus cause - * whatever log domain string we had written to be clobbered. */ - ; - } - - if (funcname && should_log_function_name(domain, severity)) { - r = tor_snprintf(buf+n, buf_len-n, "%s(): ", funcname); - if (r<0) - n = strlen(buf); - else - n += r; - } - - if (domain == LD_BUG && buf_len-n > 6) { - memcpy(buf+n, "Bug: ", 6); - n += 5; - } - - r = tor_vsnprintf(buf+n,buf_len-n,format,ap); - if (r < 0) { - /* The message was too long; overwrite the end of the buffer with - * "[...truncated]" */ - if (buf_len >= TRUNCATED_STR_LEN) { - size_t offset = buf_len-TRUNCATED_STR_LEN; - /* We have an extra 2 characters after buf_len to hold the \n\0, - * so it's safe to add 1 to the size here. */ - strlcpy(buf+offset, TRUNCATED_STR, buf_len-offset+1); - } - /* Set 'n' to the end of the buffer, where we'll be writing \n\0. - * Since we already subtracted 2 from buf_len, this is safe.*/ - n = buf_len; - } else { - n += r; - if (suffix) { - size_t suffix_len = strlen(suffix); - if (buf_len-n >= suffix_len) { - memcpy(buf+n, suffix, suffix_len); - n += suffix_len; - } - } - } - buf[n]='\n'; - buf[n+1]='\0'; - *msg_len_out = n+1; - return end_of_prefix; -} - -/** Helper: sends a message to the appropriate logfiles, at loglevel - * severity. If provided, funcname is prepended to the - * message. The actual message is derived as from tor_snprintf(format,ap). - */ -static void -logv(int severity, log_domain_mask_t domain, const char *funcname, - const char *suffix, const char *format, va_list ap) -{ - char buf[10024]; - size_t msg_len = 0; - int formatted = 0; - logfile_t *lf; - char *end_of_prefix=NULL; - int callbacks_deferred = 0; - - /* Call assert, not tor_assert, since tor_assert calls log on failure. */ - assert(format); - /* check that severity is sane. Overrunning the masks array leads to - * interesting and hard to diagnose effects */ - assert(severity >= LOG_ERR && severity <= LOG_DEBUG); - LOCK_LOGS(); - - if ((! (domain & LD_NOCB)) && smartlist_len(pending_cb_messages)) - flush_pending_log_callbacks(); - - lf = logfiles; - while (lf) { - if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) { - lf = lf->next; - continue; - } - if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) { - lf = lf->next; - continue; - } - if (lf->seems_dead) { - lf = lf->next; - continue; - } - - if (!formatted) { - end_of_prefix = - format_msg(buf, sizeof(buf), domain, severity, funcname, suffix, - format, ap, &msg_len); - formatted = 1; - } - - if (lf->is_syslog) { -#ifdef HAVE_SYSLOG_H - char *m = end_of_prefix; -#ifdef MAXLINE - /* Some syslog implementations have limits on the length of what you can - * pass them, and some very old ones do not detect overflow so well. - * Regrettably, they call their maximum line length MAXLINE. */ -#if MAXLINE < 64 -#warn "MAXLINE is a very low number; it might not be from syslog.h after all" -#endif - if (msg_len >= MAXLINE) - m = tor_strndup(end_of_prefix, MAXLINE-1); -#endif - syslog(severity, "%s", m); -#ifdef MAXLINE - if (m != end_of_prefix) { - tor_free(m); - } -#endif -#endif - lf = lf->next; - continue; - } else if (lf->callback) { - if (domain & LD_NOCB) { - if (!callbacks_deferred && pending_cb_messages) { - pending_cb_message_t *msg = tor_malloc(sizeof(pending_cb_message_t)); - msg->severity = severity; - msg->domain = domain; - msg->msg = tor_strdup(end_of_prefix); - smartlist_add(pending_cb_messages, msg); - - callbacks_deferred = 1; - } - } else { - lf->callback(severity, domain, end_of_prefix); - } - lf = lf->next; - continue; - } - if (write_all(lf->fd, buf, msg_len, 0) < 0) { /* error */ - /* don't log the error! mark this log entry to be blown away, and - * continue. */ - lf->seems_dead = 1; - } - lf = lf->next; - } - UNLOCK_LOGS(); -} - -/** Output a message to the log. It gets logged to all logfiles that - * care about messages with severity in domain. The content - * is formatted printf-style based on format and extra arguments. - * */ -void -tor_log(int severity, log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (severity > log_global_min_severity_) - return; - va_start(ap,format); - logv(severity, domain, NULL, NULL, format, ap); - va_end(ap); -} - -/** Maximum number of fds that will get notifications if we crash */ -#define MAX_SIGSAFE_FDS 8 -/** Array of fds to log crash-style warnings to. */ -static int sigsafe_log_fds[MAX_SIGSAFE_FDS] = { STDERR_FILENO }; -/** The number of elements used in sigsafe_log_fds */ -static int n_sigsafe_log_fds = 1; - -/** Write s to each element of sigsafe_log_fds. Return 0 on success, -1 - * on failure. */ -static int -tor_log_err_sigsafe_write(const char *s) -{ - int i; - ssize_t r; - size_t len = strlen(s); - int err = 0; - for (i=0; i < n_sigsafe_log_fds; ++i) { - r = write(sigsafe_log_fds[i], s, len); - err += (r != (ssize_t)len); - } - return err ? -1 : 0; -} - -/** Given a list of string arguments ending with a NULL, writes them - * to our logs and to stderr (if possible). This function is safe to call - * from within a signal handler. */ -void -tor_log_err_sigsafe(const char *m, ...) -{ - va_list ap; - const char *x; - char timebuf[33]; - time_t now = time(NULL); - - if (!m) - return; - if (log_time_granularity >= 2000) { - int g = log_time_granularity / 1000; - now -= now % g; - } - timebuf[0] = now < 0 ? '-' : ' '; - if (now < 0) now = -now; - timebuf[1] = '\0'; - format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1); - tor_log_err_sigsafe_write("\n==========================================" - "================== T="); - tor_log_err_sigsafe_write(timebuf); - tor_log_err_sigsafe_write("\n"); - tor_log_err_sigsafe_write(m); - va_start(ap, m); - while ((x = va_arg(ap, const char*))) { - tor_log_err_sigsafe_write(x); - } - va_end(ap); -} - -/** Set *out to a pointer to an array of the fds to log errors to from - * inside a signal handler. Return the number of elements in the array. */ -int -tor_log_get_sigsafe_err_fds(const int **out) -{ - *out = sigsafe_log_fds; - return n_sigsafe_log_fds; -} - -/** Helper function; return true iff the n-element array array - * contains item. */ -static int -int_array_contains(const int *array, int n, int item) -{ - int j; - for (j = 0; j < n; ++j) { - if (array[j] == item) - return 1; - } - return 0; -} - -/** Function to call whenever the list of logs changes to get ready to log - * from signal handlers. */ -void -tor_log_update_sigsafe_err_fds(void) -{ - const logfile_t *lf; - int found_real_stderr = 0; - - LOCK_LOGS(); - /* Reserve the first one for stderr. This is safe because when we daemonize, - * we dup2 /dev/null to stderr, */ - sigsafe_log_fds[0] = STDERR_FILENO; - n_sigsafe_log_fds = 1; - - for (lf = logfiles; lf; lf = lf->next) { - /* Don't try callback to the control port, or syslogs: We can't - * do them from a signal handler. Don't try stdout: we always do stderr. - */ - if (lf->is_temporary || lf->is_syslog || - lf->callback || lf->seems_dead || lf->fd < 0) - continue; - if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] & - (LD_BUG|LD_GENERAL)) { - if (lf->fd == STDERR_FILENO) - found_real_stderr = 1; - /* Avoid duplicates */ - if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd)) - continue; - sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd; - if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS) - break; - } - } - - if (!found_real_stderr && - int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) { - /* Don't use a virtual stderr when we're also logging to stdout. */ - assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/ - sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds]; - } - - UNLOCK_LOGS(); -} - -/** Output a message to the log, prefixed with a function name fn. */ -#ifdef __GNUC__ -/** GCC-based implementation of the log_fn backend, used when we have - * variadic macros. All arguments are as for log_fn, except for - * fn, which is the name of the calling functions. */ -void -log_fn_(int severity, log_domain_mask_t domain, const char *fn, - const char *format, ...) -{ - va_list ap; - if (severity > log_global_min_severity_) - return; - va_start(ap,format); - logv(severity, domain, fn, NULL, format, ap); - va_end(ap); -} -void -log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, - const char *fn, const char *format, ...) -{ - va_list ap; - char *m; - if (severity > log_global_min_severity_) - return; - m = rate_limit_log(ratelim, approx_time()); - if (m == NULL) - return; - va_start(ap, format); - logv(severity, domain, fn, m, format, ap); - va_end(ap); - tor_free(m); -} -#else -/** @{ */ -/** Variant implementation of log_fn, log_debug, log_info,... for C compilers - * without variadic macros. In this case, the calling function sets - * log_fn_function_name_ to the name of the function, then invokes the - * appropriate log_fn_, log_debug_, etc. */ -const char *log_fn_function_name_=NULL; -void -log_fn_(int severity, log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (severity > log_global_min_severity_) - return; - va_start(ap,format); - logv(severity, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -void -log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, - const char *format, ...) -{ - va_list ap; - char *m; - if (severity > log_global_min_severity_) - return; - m = rate_limit_log(ratelim, approx_time()); - if (m == NULL) - return; - va_start(ap, format); - logv(severity, domain, log_fn_function_name_, m, format, ap); - va_end(ap); - tor_free(m); -} -void -log_debug_(log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - /* For GCC we do this check in the macro. */ - if (PREDICT_LIKELY(LOG_DEBUG > log_global_min_severity_)) - return; - va_start(ap,format); - logv(LOG_DEBUG, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -void -log_info_(log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (LOG_INFO > log_global_min_severity_) - return; - va_start(ap,format); - logv(LOG_INFO, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -void -log_notice_(log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (LOG_NOTICE > log_global_min_severity_) - return; - va_start(ap,format); - logv(LOG_NOTICE, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -void -log_warn_(log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (LOG_WARN > log_global_min_severity_) - return; - va_start(ap,format); - logv(LOG_WARN, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -void -log_err_(log_domain_mask_t domain, const char *format, ...) -{ - va_list ap; - if (LOG_ERR > log_global_min_severity_) - return; - va_start(ap,format); - logv(LOG_ERR, domain, log_fn_function_name_, NULL, format, ap); - va_end(ap); - log_fn_function_name_ = NULL; -} -/** @} */ -#endif - -/** Free all storage held by victim. */ -static void -log_free(logfile_t *victim) -{ - if (!victim) - return; - tor_free(victim->severities); - tor_free(victim->filename); - tor_free(victim); -} - -/** Close all open log files, and free other static memory. */ -void -logs_free_all(void) -{ - logfile_t *victim, *next; - smartlist_t *messages; - LOCK_LOGS(); - next = logfiles; - logfiles = NULL; - messages = pending_cb_messages; - pending_cb_messages = NULL; - UNLOCK_LOGS(); - while (next) { - victim = next; - next = next->next; - close_log(victim); - log_free(victim); - } - tor_free(appname); - - SMARTLIST_FOREACH(messages, pending_cb_message_t *, msg, { - tor_free(msg->msg); - tor_free(msg); - }); - smartlist_free(messages); - - /* We _could_ destroy the log mutex here, but that would screw up any logs - * that happened between here and the end of execution. */ -} - -/** Remove and free the log entry victim from the linked-list - * logfiles (it is probably present, but it might not be due to thread - * racing issues). After this function is called, the caller shouldn't - * refer to victim anymore. - * - * Long-term, we need to do something about races in the log subsystem - * in general. See bug 222 for more details. - */ -static void -delete_log(logfile_t *victim) -{ - logfile_t *tmpl; - if (victim == logfiles) - logfiles = victim->next; - else { - for (tmpl = logfiles; tmpl && tmpl->next != victim; tmpl=tmpl->next) ; -// tor_assert(tmpl); -// tor_assert(tmpl->next == victim); - if (!tmpl) - return; - tmpl->next = victim->next; - } - log_free(victim); -} - -/** Helper: release system resources (but not memory) held by a single - * logfile_t. */ -static void -close_log(logfile_t *victim) -{ - if (victim->needs_close && victim->fd >= 0) { - close(victim->fd); - victim->fd = -1; - } else if (victim->is_syslog) { -#ifdef HAVE_SYSLOG_H - if (--syslog_count == 0) { - /* There are no other syslogs; close the logging facility. */ - closelog(); - } -#endif - } -} - -/** Adjust a log severity configuration in severity_out to contain - * every domain between loglevelMin and loglevelMax, inclusive. - */ -void -set_log_severity_config(int loglevelMin, int loglevelMax, - log_severity_list_t *severity_out) -{ - int i; - tor_assert(loglevelMin >= loglevelMax); - tor_assert(loglevelMin >= LOG_ERR && loglevelMin <= LOG_DEBUG); - tor_assert(loglevelMax >= LOG_ERR && loglevelMax <= LOG_DEBUG); - memset(severity_out, 0, sizeof(log_severity_list_t)); - for (i = loglevelMin; i >= loglevelMax; --i) { - severity_out->masks[SEVERITY_MASK_IDX(i)] = ~0u; - } -} - -/** Add a log handler named name to send all messages in severity - * to fd. Copies severity. Helper: does no locking. */ -static void -add_stream_log_impl(const log_severity_list_t *severity, - const char *name, int fd) -{ - logfile_t *lf; - lf = tor_malloc_zero(sizeof(logfile_t)); - lf->fd = fd; - lf->filename = tor_strdup(name); - lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); - lf->next = logfiles; - - logfiles = lf; - log_global_min_severity_ = get_min_log_level(); -} - -/** Add a log handler named name to send all messages in severity - * to fd. Steals a reference to severity; the caller must - * not use it after calling this function. */ -void -add_stream_log(const log_severity_list_t *severity, const char *name, int fd) -{ - LOCK_LOGS(); - add_stream_log_impl(severity, name, fd); - UNLOCK_LOGS(); -} - -/** Initialize the global logging facility */ -void -init_logging(void) -{ - if (!log_mutex_initialized) { - tor_mutex_init(&log_mutex); - log_mutex_initialized = 1; - } - if (pending_cb_messages == NULL) - pending_cb_messages = smartlist_new(); -} - -/** Set whether we report logging domains as a part of our log messages. - */ -void -logs_set_domain_logging(int enabled) -{ - LOCK_LOGS(); - log_domains_are_logged = enabled; - UNLOCK_LOGS(); -} - -/** Add a log handler to receive messages during startup (before the real - * logs are initialized). - */ -void -add_temp_log(int min_severity) -{ - log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t)); - set_log_severity_config(min_severity, LOG_ERR, s); - LOCK_LOGS(); - add_stream_log_impl(s, "", fileno(stdout)); - tor_free(s); - logfiles->is_temporary = 1; - UNLOCK_LOGS(); -} - -/** - * Add a log handler to send messages in severity - * to the function cb. - */ -int -add_callback_log(const log_severity_list_t *severity, log_callback cb) -{ - logfile_t *lf; - lf = tor_malloc_zero(sizeof(logfile_t)); - lf->fd = -1; - lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); - lf->filename = tor_strdup(""); - lf->callback = cb; - lf->next = logfiles; - - LOCK_LOGS(); - logfiles = lf; - log_global_min_severity_ = get_min_log_level(); - UNLOCK_LOGS(); - return 0; -} - -/** Adjust the configured severity of any logs whose callback function is - * cb. */ -void -change_callback_log_severity(int loglevelMin, int loglevelMax, - log_callback cb) -{ - logfile_t *lf; - log_severity_list_t severities; - set_log_severity_config(loglevelMin, loglevelMax, &severities); - LOCK_LOGS(); - for (lf = logfiles; lf; lf = lf->next) { - if (lf->callback == cb) { - memcpy(lf->severities, &severities, sizeof(severities)); - } - } - log_global_min_severity_ = get_min_log_level(); - UNLOCK_LOGS(); -} - -/** If there are any log messages that were generated with LD_NOCB waiting to - * be sent to callback-based loggers, send them now. */ -void -flush_pending_log_callbacks(void) -{ - logfile_t *lf; - smartlist_t *messages, *messages_tmp; - - LOCK_LOGS(); - if (0 == smartlist_len(pending_cb_messages)) { - UNLOCK_LOGS(); - return; - } - - messages = pending_cb_messages; - pending_cb_messages = smartlist_new(); - do { - SMARTLIST_FOREACH_BEGIN(messages, pending_cb_message_t *, msg) { - const int severity = msg->severity; - const int domain = msg->domain; - for (lf = logfiles; lf; lf = lf->next) { - if (! lf->callback || lf->seems_dead || - ! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) { - continue; - } - lf->callback(severity, domain, msg->msg); - } - tor_free(msg->msg); - tor_free(msg); - } SMARTLIST_FOREACH_END(msg); - smartlist_clear(messages); - - messages_tmp = pending_cb_messages; - pending_cb_messages = messages; - messages = messages_tmp; - } while (smartlist_len(messages)); - - smartlist_free(messages); - - UNLOCK_LOGS(); -} - -/** Close any log handlers added by add_temp_log() or marked by - * mark_logs_temp(). */ -void -close_temp_logs(void) -{ - logfile_t *lf, **p; - - LOCK_LOGS(); - for (p = &logfiles; *p; ) { - if ((*p)->is_temporary) { - lf = *p; - /* we use *p here to handle the edge case of the head of the list */ - *p = (*p)->next; - close_log(lf); - log_free(lf); - } else { - p = &((*p)->next); - } - } - - log_global_min_severity_ = get_min_log_level(); - UNLOCK_LOGS(); -} - -/** Make all currently temporary logs (set to be closed by close_temp_logs) - * live again, and close all non-temporary logs. */ -void -rollback_log_changes(void) -{ - logfile_t *lf; - LOCK_LOGS(); - for (lf = logfiles; lf; lf = lf->next) - lf->is_temporary = ! lf->is_temporary; - UNLOCK_LOGS(); - close_temp_logs(); -} - -/** Configure all log handles to be closed by close_temp_logs(). */ -void -mark_logs_temp(void) -{ - logfile_t *lf; - LOCK_LOGS(); - for (lf = logfiles; lf; lf = lf->next) - lf->is_temporary = 1; - UNLOCK_LOGS(); -} - -/** - * Add a log handler to send messages to filename. If opening the - * logfile fails, -1 is returned and errno is set appropriately (by open(2)). - */ -int -add_file_log(const log_severity_list_t *severity, const char *filename) -{ - int fd; - logfile_t *lf; - - fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644); - if (fd<0) - return -1; - if (tor_fd_seekend(fd)<0) { - close(fd); - return -1; - } - - LOCK_LOGS(); - add_stream_log_impl(severity, filename, fd); - logfiles->needs_close = 1; - lf = logfiles; - log_global_min_severity_ = get_min_log_level(); - - if (log_tor_version(lf, 0) < 0) { - delete_log(lf); - } - UNLOCK_LOGS(); - - return 0; -} - -#ifdef HAVE_SYSLOG_H -/** - * Add a log handler to send messages to they system log facility. - */ -int -add_syslog_log(const log_severity_list_t *severity) -{ - logfile_t *lf; - if (syslog_count++ == 0) - /* This is the first syslog. */ - openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY); - - lf = tor_malloc_zero(sizeof(logfile_t)); - lf->fd = -1; - lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); - lf->filename = tor_strdup(""); - lf->is_syslog = 1; - - LOCK_LOGS(); - lf->next = logfiles; - logfiles = lf; - log_global_min_severity_ = get_min_log_level(); - UNLOCK_LOGS(); - return 0; -} -#endif - -/** If level is a valid log severity, return the corresponding - * numeric value. Otherwise, return -1. */ -int -parse_log_level(const char *level) -{ - if (!strcasecmp(level, "err")) - return LOG_ERR; - if (!strcasecmp(level, "warn")) - return LOG_WARN; - if (!strcasecmp(level, "notice")) - return LOG_NOTICE; - if (!strcasecmp(level, "info")) - return LOG_INFO; - if (!strcasecmp(level, "debug")) - return LOG_DEBUG; - return -1; -} - -/** Return the string equivalent of a given log level. */ -const char * -log_level_to_string(int level) -{ - return sev_to_string(level); -} - -/** NULL-terminated array of names for log domains such that domain_list[dom] - * is a description of dom. */ -static const char *domain_list[] = { - "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", - "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV", - "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL", NULL -}; - -/** Return a bitmask for the log domain for which domain is the name, - * or 0 if there is no such name. */ -static log_domain_mask_t -parse_log_domain(const char *domain) -{ - int i; - for (i=0; domain_list[i]; ++i) { - if (!strcasecmp(domain, domain_list[i])) - return (1u<= N_LOGGING_DOMAINS) { - tor_snprintf(buf, buflen, "", (long)domain); - return buf+strlen(buf); - } - d = domain_list[bit]; - n = strlcpy(cp, d, eos-cp); - if (n >= buflen) { - tor_snprintf(buf, buflen, "", (long)domain); - return buf+strlen(buf); - } - cp += n; - domain &= ~(1<cfg_ptr. Advance cfg_ptr after - * the end of the severityPattern. Set the value of severity_out to - * the parsed pattern. Return 0 on success, -1 on failure. - * - * The syntax for a SeverityPattern is: - *
- *   SeverityPattern = *(DomainSeverity SP)* DomainSeverity
- *   DomainSeverity = (DomainList SP)? SeverityRange
- *   SeverityRange = MinSeverity ("-" MaxSeverity )?
- *   DomainList = "[" (SP? DomainSpec SP? ",") SP? DomainSpec "]"
- *   DomainSpec = "*" | Domain | "~" Domain
- * 
- * A missing MaxSeverity defaults to ERR. Severities and domains are - * case-insensitive. "~" indicates negation for a domain; negation happens - * last inside a DomainList. Only one SeverityRange without a DomainList is - * allowed per line. - */ -int -parse_log_severity_config(const char **cfg_ptr, - log_severity_list_t *severity_out) -{ - const char *cfg = *cfg_ptr; - int got_anything = 0; - int got_an_unqualified_range = 0; - memset(severity_out, 0, sizeof(*severity_out)); - - cfg = eat_whitespace(cfg); - while (*cfg) { - const char *dash, *space; - char *sev_lo, *sev_hi; - int low, high, i; - log_domain_mask_t domains = ~0u; - - if (*cfg == '[') { - int err = 0; - char *domains_str; - smartlist_t *domains_list; - log_domain_mask_t neg_domains = 0; - const char *closebracket = strchr(cfg, ']'); - if (!closebracket) - return -1; - domains = 0; - domains_str = tor_strndup(cfg+1, closebracket-cfg-1); - domains_list = smartlist_new(); - smartlist_split_string(domains_list, domains_str, ",", SPLIT_SKIP_SPACE, - -1); - tor_free(domains_str); - SMARTLIST_FOREACH_BEGIN(domains_list, const char *, domain) { - if (!strcmp(domain, "*")) { - domains = ~0u; - } else { - int d; - int negate=0; - if (*domain == '~') { - negate = 1; - ++domain; - } - d = parse_log_domain(domain); - if (!d) { - log_warn(LD_CONFIG, "No such logging domain as %s", domain); - err = 1; - } else { - if (negate) - neg_domains |= d; - else - domains |= d; - } - } - } SMARTLIST_FOREACH_END(domain); - SMARTLIST_FOREACH(domains_list, char *, d, tor_free(d)); - smartlist_free(domains_list); - if (err) - return -1; - if (domains == 0 && neg_domains) - domains = ~neg_domains; - else - domains &= ~neg_domains; - cfg = eat_whitespace(closebracket+1); - } else { - ++got_an_unqualified_range; - } - if (!strcasecmpstart(cfg, "file") || - !strcasecmpstart(cfg, "stderr") || - !strcasecmpstart(cfg, "stdout") || - !strcasecmpstart(cfg, "syslog")) { - goto done; - } - if (got_an_unqualified_range > 1) - return -1; - - space = strchr(cfg, ' '); - dash = strchr(cfg, '-'); - if (!space) - space = strchr(cfg, '\0'); - if (dash && dash < space) { - sev_lo = tor_strndup(cfg, dash-cfg); - sev_hi = tor_strndup(dash+1, space-(dash+1)); - } else { - sev_lo = tor_strndup(cfg, space-cfg); - sev_hi = tor_strdup("ERR"); - } - low = parse_log_level(sev_lo); - high = parse_log_level(sev_hi); - tor_free(sev_lo); - tor_free(sev_hi); - if (low == -1) - return -1; - if (high == -1) - return -1; - - got_anything = 1; - for (i=low; i >= high; --i) - severity_out->masks[SEVERITY_MASK_IDX(i)] |= domains; - - cfg = eat_whitespace(space); - } - - done: - *cfg_ptr = cfg; - return got_anything ? 0 : -1; -} - -/** Return the least severe log level that any current log is interested in. */ -int -get_min_log_level(void) -{ - logfile_t *lf; - int i; - int min = LOG_ERR; - for (lf = logfiles; lf; lf = lf->next) { - for (i = LOG_DEBUG; i > min; --i) - if (lf->severities->masks[SEVERITY_MASK_IDX(i)]) - min = i; - } - return min; -} - -/** Switch all logs to output at most verbose level. */ -void -switch_logs_debug(void) -{ - logfile_t *lf; - int i; - LOCK_LOGS(); - for (lf = logfiles; lf; lf=lf->next) { - for (i = LOG_DEBUG; i >= LOG_ERR; --i) - lf->severities->masks[SEVERITY_MASK_IDX(i)] = ~0u; - } - log_global_min_severity_ = get_min_log_level(); - UNLOCK_LOGS(); -} - -#if 0 -static void -dump_log_info(logfile_t *lf) -{ - const char *tp; - - if (lf->filename) { - printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename, - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } else if (lf->is_syslog) { - printf("=== syslog (%s-%s) (%stemporary)\n", - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } else { - printf("=== log (%s-%s) (%stemporary)\n", - sev_to_string(lf->min_loglevel), - sev_to_string(lf->max_loglevel), - lf->is_temporary?"":"not "); - } -} - -void -describe_logs(void) -{ - logfile_t *lf; - printf("==== BEGIN LOGS ====\n"); - for (lf = logfiles; lf; lf = lf->next) - dump_log_info(lf); - printf("==== END LOGS ====\n"); -} -#endif - diff --git a/src/tor/memarea.c b/src/tor/memarea.c deleted file mode 100644 index 96f95bf..0000000 --- a/src/tor/memarea.c +++ /dev/null @@ -1,328 +0,0 @@ -/* Copyright (c) 2008-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** \file memarea.c - * \brief Implementation for memarea_t, an allocator for allocating lots of - * small objects that will be freed all at once. - */ - -#include "orconfig.h" -#include -#include "memarea.h" -#include "tor_util.h" -#include "tor_compat.h" -#include "torlog.h" - -/** If true, we try to detect any attempts to write beyond the length of a - * memarea. */ -#define USE_SENTINELS - -/** All returned pointers should be aligned to the nearest multiple of this - * value. */ -#define MEMAREA_ALIGN SIZEOF_VOID_P - -#if MEMAREA_ALIGN == 4 -#define MEMAREA_ALIGN_MASK 3lu -#elif MEMAREA_ALIGN == 8 -#define MEMAREA_ALIGN_MASK 7lu -#else -#error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff." -#endif - -#ifdef USE_SENTINELS -/** Magic value that we stick at the end of a memarea so we can make sure - * there are no run-off-the-end bugs. */ -#define SENTINEL_VAL 0x90806622u -/** How many bytes per area do we devote to the sentinel? */ -#define SENTINEL_LEN sizeof(uint32_t) -/** Given a mem_area_chunk_t with SENTINEL_LEN extra bytes allocated at the - * end, set those bytes. */ -#define SET_SENTINEL(chunk) \ - STMT_BEGIN \ - set_uint32( &(chunk)->u.mem[chunk->mem_size], SENTINEL_VAL ); \ - STMT_END -/** Assert that the sentinel on a memarea is set correctly. */ -#define CHECK_SENTINEL(chunk) \ - STMT_BEGIN \ - uint32_t sent_val = get_uint32(&(chunk)->u.mem[chunk->mem_size]); \ - tor_assert(sent_val == SENTINEL_VAL); \ - STMT_END -#else -#define SENTINEL_LEN 0 -#define SET_SENTINEL(chunk) STMT_NIL -#define CHECK_SENTINEL(chunk) STMT_NIL -#endif - -/** Increment ptr until it is aligned to MEMAREA_ALIGN. */ -static INLINE void * -realign_pointer(void *ptr) -{ - uintptr_t x = (uintptr_t)ptr; - x = (x+MEMAREA_ALIGN_MASK) & ~MEMAREA_ALIGN_MASK; - /* Reinstate this if bug 930 ever reappears - tor_assert(((void*)x) >= ptr); - */ - return (void*)x; -} - -/** Implements part of a memarea. New memory is carved off from chunk->mem in - * increasing order until a request is too big, at which point a new chunk is - * allocated. */ -typedef struct memarea_chunk_t { - /** Next chunk in this area. Only kept around so we can free it. */ - struct memarea_chunk_t *next_chunk; - size_t mem_size; /**< How much RAM is available in u.mem, total? */ - char *next_mem; /**< Next position in u.mem to allocate data at. If it's - * greater than or equal to mem+mem_size, this chunk is - * full. */ - union { - char mem[1]; /**< Memory space in this chunk. */ - void *void_for_alignment_; /**< Dummy; used to make sure mem is aligned. */ - } u; -} memarea_chunk_t; - -/** How many bytes are needed for overhead before we get to the memory part - * of a chunk? */ -#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, u) - -/** What's the smallest that we'll allocate a chunk? */ -#define CHUNK_SIZE 4096 - -/** A memarea_t is an allocation region for a set of small memory requests - * that will all be freed at once. */ -struct memarea_t { - memarea_chunk_t *first; /**< Top of the chunk stack: never NULL. */ -}; - -/** How many chunks will we put into the freelist before freeing them? */ -#define MAX_FREELIST_LEN 4 -/** The number of memarea chunks currently in our freelist. */ -static int freelist_len=0; -/** A linked list of unused memory area chunks. Used to prevent us from - * spinning in malloc/free loops. */ -static memarea_chunk_t *freelist = NULL; - -/** Helper: allocate a new memarea chunk of around chunk_size bytes. */ -static memarea_chunk_t * -alloc_chunk(size_t sz, int freelist_ok) -{ - tor_assert(sz < SIZE_T_CEILING); - if (freelist && freelist_ok) { - memarea_chunk_t *res = freelist; - freelist = res->next_chunk; - res->next_chunk = NULL; - --freelist_len; - CHECK_SENTINEL(res); - return res; - } else { - size_t chunk_size = freelist_ok ? CHUNK_SIZE : sz; - memarea_chunk_t *res; - chunk_size += SENTINEL_LEN; - 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; - tor_assert(res->next_mem+res->mem_size+SENTINEL_LEN == - ((char*)res)+chunk_size); - tor_assert(realign_pointer(res->next_mem) == res->next_mem); - SET_SENTINEL(res); - return res; - } -} - -/** Release chunk from a memarea, either by adding it to the freelist - * or by freeing it if the freelist is already too big. */ -static void -chunk_free_unchecked(memarea_chunk_t *chunk) -{ - CHECK_SENTINEL(chunk); - if (freelist_len < MAX_FREELIST_LEN) { - ++freelist_len; - chunk->next_chunk = freelist; - freelist = chunk; - chunk->next_mem = chunk->u.mem; - } else { - tor_free(chunk); - } -} - -/** Allocate and return new memarea. */ -memarea_t * -memarea_new(void) -{ - memarea_t *head = tor_malloc(sizeof(memarea_t)); - head->first = alloc_chunk(CHUNK_SIZE, 1); - return head; -} - -/** Free area, invalidating all pointers returned from memarea_alloc() - * and friends for this area */ -void -memarea_drop_all(memarea_t *area) -{ - memarea_chunk_t *chunk, *next; - for (chunk = area->first; chunk; chunk = next) { - next = chunk->next_chunk; - chunk_free_unchecked(chunk); - } - area->first = NULL; /*fail fast on */ - tor_free(area); -} - -/** Forget about having allocated anything in area, and free some of - * the backing storage associated with it, as appropriate. Invalidates all - * pointers returned from memarea_alloc() for this area. */ -void -memarea_clear(memarea_t *area) -{ - memarea_chunk_t *chunk, *next; - if (area->first->next_chunk) { - for (chunk = area->first->next_chunk; chunk; chunk = next) { - next = chunk->next_chunk; - chunk_free_unchecked(chunk); - } - area->first->next_chunk = NULL; - } - area->first->next_mem = area->first->u.mem; -} - -/** Remove all unused memarea chunks from the internal freelist. */ -void -memarea_clear_freelist(void) -{ - memarea_chunk_t *chunk, *next; - freelist_len = 0; - for (chunk = freelist; chunk; chunk = next) { - next = chunk->next_chunk; - tor_free(chunk); - } - freelist = NULL; -} - -/** Return true iff p is in a range that has been returned by an - * allocation from area. */ -int -memarea_owns_ptr(const memarea_t *area, const void *p) -{ - memarea_chunk_t *chunk; - const char *ptr = p; - for (chunk = area->first; chunk; chunk = chunk->next_chunk) { - if (ptr >= chunk->u.mem && ptr < chunk->next_mem) - return 1; - } - return 0; -} - -/** Return a pointer to a chunk of memory in area of at least sz - * bytes. sz should be significantly smaller than the area's chunk - * size, though we can deal if it isn't. */ -void * -memarea_alloc(memarea_t *area, size_t sz) -{ - memarea_chunk_t *chunk = area->first; - char *result; - tor_assert(chunk); - CHECK_SENTINEL(chunk); - tor_assert(sz < SIZE_T_CEILING); - if (sz == 0) - sz = 1; - if (chunk->next_mem+sz > chunk->u.mem+chunk->mem_size) { - if (sz+CHUNK_HEADER_SIZE >= CHUNK_SIZE) { - /* This allocation is too big. Stick it in a special chunk, and put - * that chunk second in the list. */ - memarea_chunk_t *new_chunk = alloc_chunk(sz+CHUNK_HEADER_SIZE, 0); - new_chunk->next_chunk = chunk->next_chunk; - chunk->next_chunk = new_chunk; - chunk = new_chunk; - } else { - memarea_chunk_t *new_chunk = alloc_chunk(CHUNK_SIZE, 1); - new_chunk->next_chunk = chunk; - area->first = chunk = new_chunk; - } - tor_assert(chunk->mem_size >= sz); - } - result = chunk->next_mem; - chunk->next_mem = chunk->next_mem + sz; - /* Reinstate these if bug 930 ever comes back - tor_assert(chunk->next_mem >= chunk->u.mem); - tor_assert(chunk->next_mem <= chunk->u.mem+chunk->mem_size); - */ - chunk->next_mem = realign_pointer(chunk->next_mem); - return result; -} - -/** As memarea_alloc(), but clears the memory it returns. */ -void * -memarea_alloc_zero(memarea_t *area, size_t sz) -{ - void *result = memarea_alloc(area, sz); - memset(result, 0, sz); - return result; -} - -/** As memdup, but returns the memory from area. */ -void * -memarea_memdup(memarea_t *area, const void *s, size_t n) -{ - char *result = memarea_alloc(area, n); - memcpy(result, s, n); - return result; -} - -/** As strdup, but returns the memory from area. */ -char * -memarea_strdup(memarea_t *area, const char *s) -{ - return memarea_memdup(area, s, strlen(s)+1); -} - -/** As strndup, but returns the memory from area. */ -char * -memarea_strndup(memarea_t *area, const char *s, size_t n) -{ - size_t ln; - char *result; - const char *cp, *end = s+n; - tor_assert(n < SIZE_T_CEILING); - for (cp = s; cp < end && *cp; ++cp) - ; - /* cp now points to s+n, or to the 0 in the string. */ - ln = cp-s; - result = memarea_alloc(area, ln+1); - memcpy(result, s, ln); - result[ln]='\0'; - return result; -} - -/** Set allocated_out to the number of bytes allocated in area, - * and used_out to the number of bytes currently used. */ -void -memarea_get_stats(memarea_t *area, size_t *allocated_out, size_t *used_out) -{ - size_t a = 0, u = 0; - memarea_chunk_t *chunk; - for (chunk = area->first; chunk; chunk = chunk->next_chunk) { - CHECK_SENTINEL(chunk); - a += CHUNK_HEADER_SIZE + chunk->mem_size; - tor_assert(chunk->next_mem >= chunk->u.mem); - u += CHUNK_HEADER_SIZE + (chunk->next_mem - chunk->u.mem); - } - *allocated_out = a; - *used_out = u; -} - -/** Assert that area is okay. */ -void -memarea_assert_ok(memarea_t *area) -{ - memarea_chunk_t *chunk; - tor_assert(area->first); - - for (chunk = area->first; chunk; chunk = chunk->next_chunk) { - CHECK_SENTINEL(chunk); - tor_assert(chunk->next_mem >= chunk->u.mem); - tor_assert(chunk->next_mem <= - (char*) realign_pointer(chunk->u.mem+chunk->mem_size)); - } -} - diff --git a/src/tor/memarea.h b/src/tor/memarea.h deleted file mode 100644 index 8b88585..0000000 --- a/src/tor/memarea.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (c) 2008-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ -/* Tor dependencies */ - -#ifndef TOR_MEMAREA_H -#define TOR_MEMAREA_H - -typedef struct memarea_t memarea_t; - -memarea_t *memarea_new(void); -void memarea_drop_all(memarea_t *area); -void memarea_clear(memarea_t *area); -int memarea_owns_ptr(const memarea_t *area, const void *ptr); -void *memarea_alloc(memarea_t *area, size_t sz); -void *memarea_alloc_zero(memarea_t *area, size_t sz); -void *memarea_memdup(memarea_t *area, const void *s, size_t n); -char *memarea_strdup(memarea_t *area, const char *s); -char *memarea_strndup(memarea_t *area, const char *s, size_t n); -void memarea_get_stats(memarea_t *area, - size_t *allocated_out, size_t *used_out); -void memarea_clear_freelist(void); -void memarea_assert_ok(memarea_t *area); - -#endif - diff --git a/src/tor/mempool.c b/src/tor/mempool.c deleted file mode 100644 index 4102aa4..0000000 --- a/src/tor/mempool.c +++ /dev/null @@ -1,628 +0,0 @@ -/* Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ -#if 1 -/* Tor dependencies */ -#include "orconfig.h" -#endif - -#include -#include -#include "torint.h" -#include "crypto.h" -#define MEMPOOL_PRIVATE -#include "mempool.h" - -/* OVERVIEW: - * - * This is an implementation of memory pools for Tor cells. It may be - * useful for you too. - * - * Generally, a memory pool is an allocation strategy optimized for large - * numbers of identically-sized objects. Rather than the elaborate arena - * and coalescing strategies you need to get good performance for a - * general-purpose malloc(), pools use a series of large memory "chunks", - * each of which is carved into a bunch of smaller "items" or - * "allocations". - * - * To get decent performance, you need to: - * - Minimize the number of times you hit the underlying allocator. - * - Try to keep accesses as local in memory as possible. - * - Try to keep the common case fast. - * - * Our implementation uses three lists of chunks per pool. Each chunk can - * be either "full" (no more room for items); "empty" (no items); or - * "used" (not full, not empty). There are independent doubly-linked - * lists for each state. - * - * CREDIT: - * - * I wrote this after looking at 3 or 4 other pooling allocators, but - * without copying. The strategy this most resembles (which is funny, - * since that's the one I looked at longest ago) is the pool allocator - * underlying Python's obmalloc code. Major differences from obmalloc's - * pools are: - * - We don't even try to be threadsafe. - * - We only handle objects of one size. - * - Our list of empty chunks is doubly-linked, not singly-linked. - * (This could change pretty easily; it's only doubly-linked for - * consistency.) - * - We keep a list of full chunks (so we can have a "nuke everything" - * function). Obmalloc's pools leave full chunks to float unanchored. - * - * LIMITATIONS: - * - Not even slightly threadsafe. - * - Likes to have lots of items per chunks. - * - One pointer overhead per allocated thing. (The alternative is - * something like glib's use of an RB-tree to keep track of what - * chunk any given piece of memory is in.) - * - Only aligns allocated things to void* level: redefine ALIGNMENT_TYPE - * if you need doubles. - * - Could probably be optimized a bit; the representation contains - * a bit more info than it really needs to have. - */ - -#if 1 -/* Tor dependencies */ -#include "tor_util.h" -#include "tor_compat.h" -#include "torlog.h" -#define ALLOC(x) tor_malloc(x) -#define FREE(x) tor_free(x) -#define ASSERT(x) tor_assert(x) -#undef ALLOC_CAN_RETURN_NULL -#define TOR -/* End Tor dependencies */ -#else -/* If you're not building this as part of Tor, you'll want to define the - * following macros. For now, these should do as defaults. - */ -#include -#define PREDICT_UNLIKELY(x) (x) -#define PREDICT_LIKELY(x) (x) -#define ALLOC(x) malloc(x) -#define FREE(x) free(x) -#define STRUCT_OFFSET(tp, member) \ - ((off_t) (((char*)&((tp*)0)->member)-(char*)0)) -#define ASSERT(x) assert(x) -#define ALLOC_CAN_RETURN_NULL -#endif - -/* Tuning parameters */ -/** Largest type that we need to ensure returned memory items are aligned to. - * Change this to "double" if we need to be safe for structs with doubles. */ -#define ALIGNMENT_TYPE void * -/** Increment that we need to align allocated. */ -#define ALIGNMENT sizeof(ALIGNMENT_TYPE) -/** Largest memory chunk that we should allocate. */ -#define MAX_CHUNK (8*(1L<<20)) -/** Smallest memory chunk size that we should allocate. */ -#define MIN_CHUNK 4096 - -typedef struct mp_allocated_t mp_allocated_t; -typedef struct mp_chunk_t mp_chunk_t; - -/** Holds a single allocated item, allocated as part of a chunk. */ -struct mp_allocated_t { - /** The chunk that this item is allocated in. This adds overhead to each - * allocated item, thus making this implementation inappropriate for - * very small items. */ - mp_chunk_t *in_chunk; - union { - /** If this item is free, the next item on the free list. */ - mp_allocated_t *next_free; - /** If this item is not free, the actual memory contents of this item. - * (Not actual size.) */ - char mem[1]; - /** An extra element to the union to insure correct alignment. */ - ALIGNMENT_TYPE dummy_; - } u; -}; - -/** 'Magic' value used to detect memory corruption. */ -#define MP_CHUNK_MAGIC 0x09870123 - -/** A chunk of memory. Chunks come from malloc; we use them */ -struct mp_chunk_t { - unsigned long magic; /**< Must be MP_CHUNK_MAGIC if this chunk is valid. */ - mp_chunk_t *next; /**< The next free, used, or full chunk in sequence. */ - mp_chunk_t *prev; /**< The previous free, used, or full chunk in sequence. */ - mp_pool_t *pool; /**< The pool that this chunk is part of. */ - /** First free item in the freelist for this chunk. Note that this may be - * NULL even if this chunk is not at capacity: if so, the free memory at - * next_mem has not yet been carved into items. - */ - mp_allocated_t *first_free; - int n_allocated; /**< Number of currently allocated items in this chunk. */ - int capacity; /**< Number of items that can be fit into this chunk. */ - size_t mem_size; /**< Number of usable bytes in mem. */ - char *next_mem; /**< Pointer into part of mem not yet carved up. */ - char mem[FLEXIBLE_ARRAY_MEMBER]; /**< Storage for this chunk. */ -}; - -/** Number of extra bytes needed beyond mem_size to allocate a chunk. */ -#define CHUNK_OVERHEAD STRUCT_OFFSET(mp_chunk_t, mem[0]) - -/** Given a pointer to a mp_allocated_t, return a pointer to the memory - * item it holds. */ -#define A2M(a) (&(a)->u.mem) -/** Given a pointer to a memory_item_t, return a pointer to its enclosing - * mp_allocated_t. */ -#define M2A(p) ( ((char*)p) - STRUCT_OFFSET(mp_allocated_t, u.mem) ) - -#ifdef ALLOC_CAN_RETURN_NULL -/** If our ALLOC() macro can return NULL, check whether x is NULL, - * and if so, return NULL. */ -#define CHECK_ALLOC(x) \ - if (PREDICT_UNLIKELY(!x)) { return NULL; } -#else -/** If our ALLOC() macro can't return NULL, do nothing. */ -#define CHECK_ALLOC(x) -#endif - -/** Helper: Allocate and return a new memory chunk for pool. Does not - * link the chunk into any list. */ -static mp_chunk_t * -mp_chunk_new(mp_pool_t *pool) -{ - size_t sz = pool->new_chunk_capacity * pool->item_alloc_size; - mp_chunk_t *chunk = ALLOC(CHUNK_OVERHEAD + sz); - -#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; - chunk->capacity = pool->new_chunk_capacity; - chunk->mem_size = sz; - chunk->next_mem = chunk->mem; - chunk->pool = pool; - return chunk; -} - -/** Take a chunk that has just been allocated or removed from - * pool's empty chunk list, and add it to the head of the used chunk - * list. */ -static INLINE void -add_newly_used_chunk_to_used_list(mp_pool_t *pool, mp_chunk_t *chunk) -{ - chunk->next = pool->used_chunks; - if (chunk->next) - chunk->next->prev = chunk; - pool->used_chunks = chunk; - ASSERT(!chunk->prev); -} - -/** Return a newly allocated item from pool. */ -void * -mp_pool_get(mp_pool_t *pool) -{ - mp_chunk_t *chunk; - mp_allocated_t *allocated; - - if (PREDICT_LIKELY(pool->used_chunks != NULL)) { - /* Common case: there is some chunk that is neither full nor empty. Use - * that one. (We can't use the full ones, obviously, and we should fill - * up the used ones before we start on any empty ones. */ - chunk = pool->used_chunks; - - } else if (pool->empty_chunks) { - /* We have no used chunks, but we have an empty chunk that we haven't - * freed yet: use that. (We pull from the front of the list, which should - * get us the most recently emptied chunk.) */ - chunk = pool->empty_chunks; - - /* Remove the chunk from the empty list. */ - pool->empty_chunks = chunk->next; - if (chunk->next) - chunk->next->prev = NULL; - - /* Put the chunk on the 'used' list*/ - add_newly_used_chunk_to_used_list(pool, chunk); - - ASSERT(!chunk->prev); - --pool->n_empty_chunks; - if (pool->n_empty_chunks < pool->min_empty_chunks) - pool->min_empty_chunks = pool->n_empty_chunks; - } else { - /* We have no used or empty chunks: allocate a new chunk. */ - chunk = mp_chunk_new(pool); - CHECK_ALLOC(chunk); - - /* Add the new chunk to the used list. */ - add_newly_used_chunk_to_used_list(pool, chunk); - } - - ASSERT(chunk->n_allocated < chunk->capacity); - - if (chunk->first_free) { - /* If there's anything on the chunk's freelist, unlink it and use it. */ - allocated = chunk->first_free; - chunk->first_free = allocated->u.next_free; - allocated->u.next_free = NULL; /* For debugging; not really needed. */ - ASSERT(allocated->in_chunk == chunk); - } else { - /* Otherwise, the chunk had better have some free space left on it. */ - ASSERT(chunk->next_mem + pool->item_alloc_size <= - chunk->mem + chunk->mem_size); - - /* Good, it did. Let's carve off a bit of that free space, and use - * that. */ - allocated = (void*)chunk->next_mem; - chunk->next_mem += pool->item_alloc_size; - allocated->in_chunk = chunk; - allocated->u.next_free = NULL; /* For debugging; not really needed. */ - } - - ++chunk->n_allocated; -#ifdef MEMPOOL_STATS - ++pool->total_items_allocated; -#endif - - if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) { - /* This chunk just became full. */ - ASSERT(chunk == pool->used_chunks); - ASSERT(chunk->prev == NULL); - - /* Take it off the used list. */ - pool->used_chunks = chunk->next; - if (chunk->next) - chunk->next->prev = NULL; - - /* Put it on the full list. */ - chunk->next = pool->full_chunks; - if (chunk->next) - chunk->next->prev = chunk; - pool->full_chunks = chunk; - } - /* And return the memory portion of the mp_allocated_t. */ - return A2M(allocated); -} - -/** Return an allocated memory item to its memory pool. */ -void -mp_pool_release(void *item) -{ - mp_allocated_t *allocated = (void*) M2A(item); - mp_chunk_t *chunk = allocated->in_chunk; - - ASSERT(chunk); - ASSERT(chunk->magic == MP_CHUNK_MAGIC); - ASSERT(chunk->n_allocated > 0); - - allocated->u.next_free = chunk->first_free; - chunk->first_free = allocated; - - if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) { - /* This chunk was full and is about to be used. */ - mp_pool_t *pool = chunk->pool; - /* unlink from the full list */ - if (chunk->prev) - chunk->prev->next = chunk->next; - if (chunk->next) - chunk->next->prev = chunk->prev; - if (chunk == pool->full_chunks) - pool->full_chunks = chunk->next; - - /* link to the used list. */ - chunk->next = pool->used_chunks; - chunk->prev = NULL; - if (chunk->next) - chunk->next->prev = chunk; - pool->used_chunks = chunk; - } else if (PREDICT_UNLIKELY(chunk->n_allocated == 1)) { - /* This was used and is about to be empty. */ - mp_pool_t *pool = chunk->pool; - - /* Unlink from the used list */ - if (chunk->prev) - chunk->prev->next = chunk->next; - if (chunk->next) - chunk->next->prev = chunk->prev; - if (chunk == pool->used_chunks) - pool->used_chunks = chunk->next; - - /* Link to the empty list */ - chunk->next = pool->empty_chunks; - chunk->prev = NULL; - if (chunk->next) - chunk->next->prev = chunk; - pool->empty_chunks = chunk; - - /* Reset the guts of this chunk to defragment it, in case it gets - * used again. */ - chunk->first_free = NULL; - chunk->next_mem = chunk->mem; - - ++pool->n_empty_chunks; - } - --chunk->n_allocated; -} - -/** Allocate a new memory pool to hold items of size item_size. We'll - * try to fit about chunk_capacity bytes in each chunk. */ -mp_pool_t * -mp_pool_new(size_t item_size, size_t chunk_capacity) -{ - mp_pool_t *pool; - size_t alloc_size, new_chunk_cap; - - tor_assert(item_size < SIZE_T_CEILING); - tor_assert(chunk_capacity < SIZE_T_CEILING); - tor_assert(SIZE_T_CEILING / item_size > chunk_capacity); - - pool = ALLOC(sizeof(mp_pool_t)); - CHECK_ALLOC(pool); - memset(pool, 0, sizeof(mp_pool_t)); - - /* First, we figure out how much space to allow per item. We'll want to - * use make sure we have enough for the overhead plus the item size. */ - alloc_size = (size_t)(STRUCT_OFFSET(mp_allocated_t, u.mem) + item_size); - /* If the item_size is less than sizeof(next_free), we need to make - * the allocation bigger. */ - if (alloc_size < sizeof(mp_allocated_t)) - alloc_size = sizeof(mp_allocated_t); - - /* If we're not an even multiple of ALIGNMENT, round up. */ - if (alloc_size % ALIGNMENT) { - alloc_size = alloc_size + ALIGNMENT - (alloc_size % ALIGNMENT); - } - if (alloc_size < ALIGNMENT) - alloc_size = ALIGNMENT; - ASSERT((alloc_size % ALIGNMENT) == 0); - - /* Now we figure out how many items fit in each chunk. We need to fit at - * least 2 items per chunk. No chunk can be more than MAX_CHUNK bytes long, - * or less than MIN_CHUNK. */ - if (chunk_capacity > MAX_CHUNK) - chunk_capacity = MAX_CHUNK; - /* Try to be around a power of 2 in size, since that's what allocators like - * handing out. 512K-1 byte is a lot better than 512K+1 byte. */ - chunk_capacity = (size_t) round_to_power_of_2(chunk_capacity); - while (chunk_capacity < alloc_size * 2 + CHUNK_OVERHEAD) - chunk_capacity *= 2; - if (chunk_capacity < MIN_CHUNK) - chunk_capacity = MIN_CHUNK; - - new_chunk_cap = (chunk_capacity-CHUNK_OVERHEAD) / alloc_size; - tor_assert(new_chunk_cap < INT_MAX); - pool->new_chunk_capacity = (int)new_chunk_cap; - - pool->item_alloc_size = alloc_size; - - log_debug(LD_MM, "Capacity is %lu, item size is %lu, alloc size is %lu", - (unsigned long)pool->new_chunk_capacity, - (unsigned long)pool->item_alloc_size, - (unsigned long)(pool->new_chunk_capacity*pool->item_alloc_size)); - - return pool; -} - -/** Helper function for qsort: used to sort pointers to mp_chunk_t into - * descending order of fullness. */ -static int -mp_pool_sort_used_chunks_helper(const void *_a, const void *_b) -{ - mp_chunk_t *a = *(mp_chunk_t**)_a; - mp_chunk_t *b = *(mp_chunk_t**)_b; - return b->n_allocated - a->n_allocated; -} - -/** Sort the used chunks in pool into descending order of fullness, - * so that we preferentially fill up mostly full chunks before we make - * nearly empty chunks less nearly empty. */ -static void -mp_pool_sort_used_chunks(mp_pool_t *pool) -{ - int i, n=0, inverted=0; - mp_chunk_t **chunks, *chunk; - for (chunk = pool->used_chunks; chunk; chunk = chunk->next) { - ++n; - if (chunk->next && chunk->next->n_allocated > chunk->n_allocated) - ++inverted; - } - if (!inverted) - return; - //printf("Sort %d/%d\n",inverted,n); - chunks = ALLOC(sizeof(mp_chunk_t *)*n); -#ifdef ALLOC_CAN_RETURN_NULL - if (PREDICT_UNLIKELY(!chunks)) return; -#endif - for (i=0,chunk = pool->used_chunks; chunk; chunk = chunk->next) - chunks[i++] = chunk; - qsort(chunks, n, sizeof(mp_chunk_t *), mp_pool_sort_used_chunks_helper); - pool->used_chunks = chunks[0]; - chunks[0]->prev = NULL; - for (i=1;inext = chunks[i]; - chunks[i]->prev = chunks[i-1]; - } - chunks[n-1]->next = NULL; - FREE(chunks); - mp_pool_assert_ok(pool); -} - -/** If there are more than n empty chunks in pool, free the - * excess ones that have been empty for the longest. If - * keep_recently_used is true, do not free chunks unless they have been - * empty since the last call to this function. - **/ -void -mp_pool_clean(mp_pool_t *pool, int n_to_keep, int keep_recently_used) -{ - mp_chunk_t *chunk, **first_to_free; - - mp_pool_sort_used_chunks(pool); - ASSERT(n_to_keep >= 0); - - if (keep_recently_used) { - int n_recently_used = pool->n_empty_chunks - pool->min_empty_chunks; - if (n_to_keep < n_recently_used) - n_to_keep = n_recently_used; - } - - ASSERT(n_to_keep >= 0); - - first_to_free = &pool->empty_chunks; - while (*first_to_free && n_to_keep > 0) { - first_to_free = &(*first_to_free)->next; - --n_to_keep; - } - if (!*first_to_free) { - pool->min_empty_chunks = pool->n_empty_chunks; - return; - } - - chunk = *first_to_free; - while (chunk) { - mp_chunk_t *next = chunk->next; - chunk->magic = 0xdeadbeef; - FREE(chunk); -#ifdef MEMPOOL_STATS - ++pool->total_chunks_freed; -#endif - --pool->n_empty_chunks; - chunk = next; - } - - pool->min_empty_chunks = pool->n_empty_chunks; - *first_to_free = NULL; -} - -/** Helper: Given a list of chunks, free all the chunks in the list. */ -static void -destroy_chunks(mp_chunk_t *chunk) -{ - mp_chunk_t *next; - while (chunk) { - chunk->magic = 0xd3adb33f; - next = chunk->next; - FREE(chunk); - chunk = next; - } -} - -/** Free all space held in pool This makes all pointers returned from - * mp_pool_get(pool) invalid. */ -void -mp_pool_destroy(mp_pool_t *pool) -{ - destroy_chunks(pool->empty_chunks); - destroy_chunks(pool->used_chunks); - destroy_chunks(pool->full_chunks); - memwipe(pool, 0xe0, sizeof(mp_pool_t)); - FREE(pool); -} - -/** Helper: make sure that a given chunk list is not corrupt. */ -static int -assert_chunks_ok(mp_pool_t *pool, mp_chunk_t *chunk, int empty, int full) -{ - mp_allocated_t *allocated; - int n = 0; - if (chunk) - ASSERT(chunk->prev == NULL); - - while (chunk) { - n++; - ASSERT(chunk->magic == MP_CHUNK_MAGIC); - ASSERT(chunk->pool == pool); - for (allocated = chunk->first_free; allocated; - allocated = allocated->u.next_free) { - ASSERT(allocated->in_chunk == chunk); - } - if (empty) - ASSERT(chunk->n_allocated == 0); - else if (full) - ASSERT(chunk->n_allocated == chunk->capacity); - else - ASSERT(chunk->n_allocated > 0 && chunk->n_allocated < chunk->capacity); - - ASSERT(chunk->capacity == pool->new_chunk_capacity); - - ASSERT(chunk->mem_size == - pool->new_chunk_capacity * pool->item_alloc_size); - - ASSERT(chunk->next_mem >= chunk->mem && - chunk->next_mem <= chunk->mem + chunk->mem_size); - - if (chunk->next) - ASSERT(chunk->next->prev == chunk); - - chunk = chunk->next; - } - return n; -} - -/** Fail with an assertion if pool is not internally consistent. */ -void -mp_pool_assert_ok(mp_pool_t *pool) -{ - int n_empty; - - n_empty = assert_chunks_ok(pool, pool->empty_chunks, 1, 0); - assert_chunks_ok(pool, pool->full_chunks, 0, 1); - assert_chunks_ok(pool, pool->used_chunks, 0, 0); - - ASSERT(pool->n_empty_chunks == n_empty); -} - -#ifdef TOR -/** Dump information about pool's memory usage to the Tor log at level - * severity. */ -/*FFFF uses Tor logging functions. */ -void -mp_pool_log_status(mp_pool_t *pool, int severity) -{ - uint64_t bytes_used = 0; - uint64_t bytes_allocated = 0; - uint64_t bu = 0, ba = 0; - mp_chunk_t *chunk; - int n_full = 0, n_used = 0; - - ASSERT(pool); - - for (chunk = pool->empty_chunks; chunk; chunk = chunk->next) { - bytes_allocated += chunk->mem_size; - } - log_fn(severity, LD_MM, U64_FORMAT" bytes in %d empty chunks", - U64_PRINTF_ARG(bytes_allocated), pool->n_empty_chunks); - for (chunk = pool->used_chunks; chunk; chunk = chunk->next) { - ++n_used; - bu += chunk->n_allocated * pool->item_alloc_size; - ba += chunk->mem_size; - log_fn(severity, LD_MM, " used chunk: %d items allocated", - chunk->n_allocated); - } - log_fn(severity, LD_MM, U64_FORMAT"/"U64_FORMAT - " bytes in %d partially full chunks", - U64_PRINTF_ARG(bu), U64_PRINTF_ARG(ba), n_used); - bytes_used += bu; - bytes_allocated += ba; - bu = ba = 0; - for (chunk = pool->full_chunks; chunk; chunk = chunk->next) { - ++n_full; - bu += chunk->n_allocated * pool->item_alloc_size; - ba += chunk->mem_size; - } - log_fn(severity, LD_MM, U64_FORMAT"/"U64_FORMAT - " bytes in %d full chunks", - U64_PRINTF_ARG(bu), U64_PRINTF_ARG(ba), n_full); - bytes_used += bu; - bytes_allocated += ba; - - log_fn(severity, LD_MM, "Total: "U64_FORMAT"/"U64_FORMAT" bytes allocated " - "for cell pools are full.", - U64_PRINTF_ARG(bytes_used), U64_PRINTF_ARG(bytes_allocated)); - -#ifdef MEMPOOL_STATS - log_fn(severity, LD_MM, U64_FORMAT" cell allocations ever; " - U64_FORMAT" chunk allocations ever; " - U64_FORMAT" chunk frees ever.", - U64_PRINTF_ARG(pool->total_items_allocated), - U64_PRINTF_ARG(pool->total_chunks_allocated), - U64_PRINTF_ARG(pool->total_chunks_freed)); -#endif -} -#endif - diff --git a/src/tor/mempool.h b/src/tor/mempool.h deleted file mode 100644 index 0fc1e4c..0000000 --- a/src/tor/mempool.h +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file mempool.h - * \brief Headers for mempool.c - **/ - -#ifndef TOR_MEMPOOL_H -#define TOR_MEMPOOL_H - -/** A memory pool is a context in which a large number of fixed-sized -* objects can be allocated efficiently. See mempool.c for implementation -* details. */ -typedef struct mp_pool_t mp_pool_t; - -void *mp_pool_get(mp_pool_t *pool); -void mp_pool_release(void *item); -mp_pool_t *mp_pool_new(size_t item_size, size_t chunk_capacity); -void mp_pool_clean(mp_pool_t *pool, int n_to_keep, int keep_recently_used); -void mp_pool_destroy(mp_pool_t *pool); -void mp_pool_assert_ok(mp_pool_t *pool); -void mp_pool_log_status(mp_pool_t *pool, int severity); - -#define MP_POOL_ITEM_OVERHEAD (sizeof(void*)) - -#define MEMPOOL_STATS - -#ifdef MEMPOOL_PRIVATE -/* These declarations are only used by mempool.c and test.c */ - -struct mp_pool_t { - /** Doubly-linked list of chunks in which no items have been allocated. - * The front of the list is the most recently emptied chunk. */ - struct mp_chunk_t *empty_chunks; - /** Doubly-linked list of chunks in which some items have been allocated, - * but which are not yet full. The front of the list is the chunk that has - * most recently been modified. */ - struct mp_chunk_t *used_chunks; - /** Doubly-linked list of chunks in which no more items can be allocated. - * The front of the list is the chunk that has most recently become full. */ - struct mp_chunk_t *full_chunks; - /** Length of empty_chunks. */ - int n_empty_chunks; - /** Lowest value of empty_chunks since last call to - * mp_pool_clean(-1). */ - int min_empty_chunks; - /** Size of each chunk (in items). */ - int new_chunk_capacity; - /** Size to allocate for each item, including overhead and alignment - * padding. */ - size_t item_alloc_size; -#ifdef MEMPOOL_STATS - /** Total number of items allocated ever. */ - uint64_t total_items_allocated; - /** Total number of chunks allocated ever. */ - uint64_t total_chunks_allocated; - /** Total number of chunks freed ever. */ - uint64_t total_chunks_freed; -#endif -}; -#endif - -#endif - diff --git a/src/tor/microdesc.c b/src/tor/microdesc.c deleted file mode 100644 index 1124991..0000000 --- a/src/tor/microdesc.c +++ /dev/null @@ -1,833 +0,0 @@ -/* Copyright (c) 2009-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "circuitbuild.h" -#include "config.h" -#include "directory.h" -#include "dirserv.h" -#include "entrynodes.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" - -/** A data structure to hold a bunch of cached microdescriptors. There are - * two active files in the cache: a "cache file" that we mmap, and a "journal - * file" that we append to. Periodically, we rebuild the cache file to hold - * only the microdescriptors that we want to keep */ -struct microdesc_cache_t { - /** Map from sha256-digest to microdesc_t for every microdesc_t in the - * cache. */ - HT_HEAD(microdesc_map, microdesc_t) map; - - /** Name of the cache file. */ - char *cache_fname; - /** Name of the journal file. */ - char *journal_fname; - /** Mmap'd contents of the cache file, or NULL if there is none. */ - tor_mmap_t *cache_content; - /** Number of bytes used in the journal file. */ - size_t journal_len; - /** Number of bytes in descriptors removed as too old. */ - size_t bytes_dropped; - - /** Total bytes of microdescriptor bodies we have added to this cache */ - uint64_t total_len_seen; - /** Total number of microdescriptors we have added to this cache */ - unsigned n_seen; -}; - -/** Helper: computes a hash of md to place it in a hash table. */ -static INLINE unsigned int -microdesc_hash_(microdesc_t *md) -{ - unsigned *d = (unsigned*)md->digest; -#if SIZEOF_INT == 4 - return d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6] ^ d[7]; -#else - return d[0] ^ d[1] ^ d[2] ^ d[3]; -#endif -} - -/** Helper: compares a and for equality for hash-table purposes. */ -static INLINE int -microdesc_eq_(microdesc_t *a, microdesc_t *b) -{ - return tor_memeq(a->digest, b->digest, DIGEST256_LEN); -} - -HT_PROTOTYPE(microdesc_map, microdesc_t, node, - microdesc_hash_, microdesc_eq_); -HT_GENERATE(microdesc_map, microdesc_t, node, - microdesc_hash_, microdesc_eq_, 0.6, - malloc, realloc, free); - -/** Write the body of md into f, with appropriate annotations. - * On success, return the total number of bytes written, and set - * *annotation_len_out to the number of bytes written as - * annotations. */ -static ssize_t -dump_microdescriptor(int fd, microdesc_t *md, size_t *annotation_len_out) -{ - ssize_t r = 0; - ssize_t written; - if (md->body == NULL) { - *annotation_len_out = 0; - return 0; - } - /* XXXX drops unknown annotations. */ - if (md->last_listed) { - char buf[ISO_TIME_LEN+1]; - char annotation[ISO_TIME_LEN+32]; - format_iso_time(buf, md->last_listed); - tor_snprintf(annotation, sizeof(annotation), "@last-listed %s\n", buf); - if (write_all(fd, annotation, strlen(annotation), 0) < 0) { - log_warn(LD_DIR, - "Couldn't write microdescriptor annotation: %s", - strerror(errno)); - return -1; - } - r += strlen(annotation); - *annotation_len_out = r; - } else { - *annotation_len_out = 0; - } - - md->off = tor_fd_getpos(fd); - written = write_all(fd, md->body, md->bodylen, 0); - if (written != (ssize_t)md->bodylen) { - log_warn(LD_DIR, - "Couldn't dump microdescriptor (wrote %ld out of %lu): %s", - (long)written, (unsigned long)md->bodylen, - strerror(errno)); - return -1; - } - r += md->bodylen; - return r; -} - -/** Holds a pointer to the current microdesc_cache_t object, or NULL if no - * such object has been allocated. */ -static microdesc_cache_t *the_microdesc_cache = NULL; - -/** Return a pointer to the microdescriptor cache, loading it if necessary. */ -microdesc_cache_t * -get_microdesc_cache(void) -{ - if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { - microdesc_cache_t *cache = tor_malloc_zero(sizeof(microdesc_cache_t)); - HT_INIT(microdesc_map, &cache->map); - cache->cache_fname = get_datadir_fname("cached-microdescs"); - cache->journal_fname = get_datadir_fname("cached-microdescs.new"); - microdesc_cache_reload(cache); - the_microdesc_cache = cache; - } - return the_microdesc_cache; -} - -/* There are three sources of microdescriptors: - 1) Generated by us while acting as a directory authority. - 2) Loaded from the cache on disk. - 3) Downloaded. -*/ - -/** Decode the microdescriptors from the string starting at s and - * ending at eos, and store them in cache. If no_save, - * mark them as non-writable to disk. If where is SAVED_IN_CACHE, - * leave their bodies as pointers to the mmap'd cache. If where is - * SAVED_NOWHERE, do not allow annotations. If listed_at is not -1, - * set the last_listed field of every microdesc to listed_at. If - * requested_digests is non-null, then it contains a list of digests we mean - * to allow, so we should reject any non-requested microdesc with a different - * digest, and alter the list to contain only the digests of those microdescs - * we didn't find. - * Return a newly allocated list of the added microdescriptors, or NULL */ -smartlist_t * -microdescs_add_to_cache(microdesc_cache_t *cache, - const char *s, const char *eos, saved_location_t where, - int no_save, time_t listed_at, - smartlist_t *requested_digests256) -{ - smartlist_t *descriptors, *added; - const int allow_annotations = (where != SAVED_NOWHERE); - - descriptors = microdescs_parse_from_string(s, eos, - allow_annotations, - where); - if (listed_at != (time_t)-1) { - SMARTLIST_FOREACH(descriptors, microdesc_t *, md, - md->last_listed = listed_at); - } - if (requested_digests256) { - digestmap_t *requested; /* XXXX actually we should just use a - digest256map */ - requested = digestmap_new(); - SMARTLIST_FOREACH(requested_digests256, const char *, cp, - digestmap_set(requested, cp, (void*)1)); - SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) { - if (digestmap_get(requested, md->digest)) { - digestmap_set(requested, md->digest, (void*)2); - } else { - log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microdesc"); - microdesc_free(md); - SMARTLIST_DEL_CURRENT(descriptors, md); - } - } SMARTLIST_FOREACH_END(md); - SMARTLIST_FOREACH_BEGIN(requested_digests256, char *, cp) { - if (digestmap_get(requested, cp) == (void*)2) { - tor_free(cp); - SMARTLIST_DEL_CURRENT(requested_digests256, cp); - } - } SMARTLIST_FOREACH_END(cp); - digestmap_free(requested, NULL); - } - - added = microdescs_add_list_to_cache(cache, descriptors, where, no_save); - smartlist_free(descriptors); - return added; -} - -/** As microdescs_add_to_cache, but takes a list of microdescriptors instead of - * a string to decode. Frees any members of descriptors that it does - * not add. */ -smartlist_t * -microdescs_add_list_to_cache(microdesc_cache_t *cache, - smartlist_t *descriptors, saved_location_t where, - int no_save) -{ - smartlist_t *added; - open_file_t *open_file = NULL; - int fd = -1; - // int n_added = 0; - ssize_t size = 0; - - if (where == SAVED_NOWHERE && !no_save) { - fd = start_writing_to_file(cache->journal_fname, - OPEN_FLAGS_APPEND|O_BINARY, - 0600, &open_file); - if (fd < 0) { - log_warn(LD_DIR, "Couldn't append to journal in %s: %s", - cache->journal_fname, strerror(errno)); - } - } - - added = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) { - microdesc_t *md2; - md2 = HT_FIND(microdesc_map, &cache->map, md); - if (md2) { - /* We already had this one. */ - if (md2->last_listed < md->last_listed) - md2->last_listed = md->last_listed; - microdesc_free(md); - if (where != SAVED_NOWHERE) - cache->bytes_dropped += size; - continue; - } - - /* Okay, it's a new one. */ - if (fd >= 0) { - size_t annotation_len; - size = dump_microdescriptor(fd, md, &annotation_len); - if (size < 0) { - /* we already warned in dump_microdescriptor */ - abort_writing_to_file(open_file); - fd = -1; - } else { - md->saved_location = SAVED_IN_JOURNAL; - cache->journal_len += size; - } - } else { - md->saved_location = where; - } - - md->no_save = no_save; - - HT_INSERT(microdesc_map, &cache->map, md); - md->held_in_map = 1; - smartlist_add(added, md); - ++cache->n_seen; - cache->total_len_seen += md->bodylen; - } SMARTLIST_FOREACH_END(md); - - if (fd >= 0) { - if (finish_writing_to_file(open_file) < 0) { - log_warn(LD_DIR, "Error appending to microdescriptor file: %s", - strerror(errno)); - smartlist_clear(added); - return added; - } - } - - { - networkstatus_t *ns = networkstatus_get_latest_consensus(); - if (ns && ns->flavor == FLAV_MICRODESC) - SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md)); - } - - if (smartlist_len(added)) - router_dir_info_changed(); - - return added; -} - -/** Remove every microdescriptor in cache. */ -void -microdesc_cache_clear(microdesc_cache_t *cache) -{ - microdesc_t **entry, **next; - for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) { - microdesc_t *md = *entry; - next = HT_NEXT_RMV(microdesc_map, &cache->map, entry); - md->held_in_map = 0; - microdesc_free(md); - } - HT_CLEAR(microdesc_map, &cache->map); - if (cache->cache_content) { - tor_munmap_file(cache->cache_content); - cache->cache_content = NULL; - } - cache->total_len_seen = 0; - cache->n_seen = 0; - cache->bytes_dropped = 0; -} - -/** Reload the contents of cache from disk. If it is empty, load it - * for the first time. Return 0 on success, -1 on failure. */ -int -microdesc_cache_reload(microdesc_cache_t *cache) -{ - struct stat st; - char *journal_content; - smartlist_t *added; - tor_mmap_t *mm; - int total = 0; - - microdesc_cache_clear(cache); - - mm = cache->cache_content = tor_mmap_file(cache->cache_fname); - if (mm) { - added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size, - SAVED_IN_CACHE, 0, -1, NULL); - if (added) { - total += smartlist_len(added); - smartlist_free(added); - } - } - - journal_content = read_file_to_str(cache->journal_fname, - RFTS_IGNORE_MISSING, &st); - if (journal_content) { - cache->journal_len = (size_t) st.st_size; - added = microdescs_add_to_cache(cache, journal_content, - journal_content+st.st_size, - SAVED_IN_JOURNAL, 0, -1, NULL); - if (added) { - total += smartlist_len(added); - smartlist_free(added); - } - tor_free(journal_content); - } - log_info(LD_DIR, "Reloaded microdescriptor cache. Found %d descriptors.", - total); - - microdesc_cache_rebuild(cache, 0 /* don't force */); - - return 0; -} - -/** By default, we remove any microdescriptors that have gone at least this - * long without appearing in a current consensus. */ -#define TOLERATE_MICRODESC_AGE (7*24*60*60) - -/** Remove all microdescriptors from cache that haven't been listed for - * a long time. Does not rebuild the cache on disk. If cutoff is - * positive, specifically remove microdescriptors that have been unlisted - * since cutoff. If force is true, remove microdescriptors even - * if we have no current live microdescriptor consensus. - */ -void -microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force) -{ - microdesc_t **mdp, *victim; - int dropped=0, kept=0; - size_t bytes_dropped = 0; - time_t now = time(NULL); - - /* If we don't know a live consensus, don't believe last_listed values: we - * might be starting up after being down for a while. */ - if (! force && - ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC)) - return; - - if (cutoff <= 0) - cutoff = now - TOLERATE_MICRODESC_AGE; - - for (mdp = HT_START(microdesc_map, &cache->map); mdp != NULL; ) { - if ((*mdp)->last_listed < cutoff) { - ++dropped; - victim = *mdp; - mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp); - victim->held_in_map = 0; - bytes_dropped += victim->bodylen; - microdesc_free(victim); - } else { - ++kept; - mdp = HT_NEXT(microdesc_map, &cache->map, mdp); - } - } - - if (dropped) { - log_info(LD_DIR, "Removed %d/%d microdescriptors as old.", - dropped,dropped+kept); - cache->bytes_dropped += bytes_dropped; - } -} - -static int -should_rebuild_md_cache(microdesc_cache_t *cache) -{ - const size_t old_len = - cache->cache_content ? cache->cache_content->size : 0; - const size_t journal_len = cache->journal_len; - const size_t dropped = cache->bytes_dropped; - - if (journal_len < 16384) - return 0; /* Don't bother, not enough has happened yet. */ - if (dropped > (journal_len + old_len) / 3) - return 1; /* We could save 1/3 or more of the currently used space. */ - if (journal_len > old_len / 2) - return 1; /* We should append to the regular file */ - - return 0; -} - -/** - * Mark md as having no body, and release any storage previously held - * by its body. - */ -static void -microdesc_wipe_body(microdesc_t *md) -{ - if (!md) - return; - - if (md->saved_location != SAVED_IN_CACHE) - tor_free(md->body); - - md->off = 0; - md->saved_location = SAVED_NOWHERE; - md->body = NULL; - md->bodylen = 0; - md->no_save = 1; -} - -/** Regenerate the main cache file for cache, clear the journal file, - * and update every microdesc_t in the cache with pointers to its new - * location. If force is true, do this unconditionally. If - * force is false, do it only if we expect to save space on disk. */ -int -microdesc_cache_rebuild(microdesc_cache_t *cache, int force) -{ - open_file_t *open_file; - int fd = -1; - microdesc_t **mdp; - smartlist_t *wrote; - ssize_t size; - off_t off = 0, off_real; - int orig_size, new_size; - - if (cache == NULL) { - cache = the_microdesc_cache; - if (cache == NULL) - return 0; - } - - /* Remove dead descriptors */ - microdesc_cache_clean(cache, 0/*cutoff*/, 0/*force*/); - - if (!force && !should_rebuild_md_cache(cache)) - return 0; - - log_info(LD_DIR, "Rebuilding the microdescriptor cache..."); - - orig_size = (int)(cache->cache_content ? cache->cache_content->size : 0); - orig_size += (int)cache->journal_len; - - fd = start_writing_to_file(cache->cache_fname, - OPEN_FLAGS_REPLACE|O_BINARY, - 0600, &open_file); - if (fd < 0) - return -1; - - wrote = smartlist_new(); - - HT_FOREACH(mdp, microdesc_map, &cache->map) { - microdesc_t *md = *mdp; - size_t annotation_len; - if (md->no_save || !md->body) - continue; - - size = dump_microdescriptor(fd, md, &annotation_len); - if (size < 0) { - microdesc_wipe_body(md); - - /* rewind, in case it was a partial write. */ - tor_fd_setpos(fd, off); - continue; - } - tor_assert(((size_t)size) == annotation_len + md->bodylen); - md->off = off + annotation_len; - off += size; - off_real = tor_fd_getpos(fd); - if (off_real != off) { - log_warn(LD_BUG, "Discontinuity in position in microdescriptor cache." - "By my count, I'm at "I64_FORMAT - ", but I should be at "I64_FORMAT, - I64_PRINTF_ARG(off), I64_PRINTF_ARG(off_real)); - if (off_real >= 0) - off = off_real; - } - if (md->saved_location != SAVED_IN_CACHE) { - tor_free(md->body); - md->saved_location = SAVED_IN_CACHE; - } - smartlist_add(wrote, md); - } - - /* We must do this unmap _before_ we call finish_writing_to_file(), or - * windows will not actually replace the file. */ - if (cache->cache_content) - tor_munmap_file(cache->cache_content); - - if (finish_writing_to_file(open_file) < 0) { - log_warn(LD_DIR, "Error rebuilding microdescriptor cache: %s", - strerror(errno)); - /* Okay. Let's prevent from making things worse elsewhere. */ - cache->cache_content = NULL; - HT_FOREACH(mdp, microdesc_map, &cache->map) { - microdesc_t *md = *mdp; - if (md->saved_location == SAVED_IN_CACHE) { - microdesc_wipe_body(md); - } - } - return -1; - } - - cache->cache_content = tor_mmap_file(cache->cache_fname); - - if (!cache->cache_content && smartlist_len(wrote)) { - log_err(LD_DIR, "Couldn't map file that we just wrote to %s!", - cache->cache_fname); - smartlist_free(wrote); - return -1; - } - SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) { - tor_assert(md->saved_location == SAVED_IN_CACHE); - md->body = (char*)cache->cache_content->data + md->off; - if (PREDICT_UNLIKELY( - md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) { - /* XXXX once bug 2022 is solved, we can kill this block and turn it - * into just the tor_assert(fast_memeq) */ - off_t avail = cache->cache_content->size - md->off; - char *bad_str; - tor_assert(avail >= 0); - bad_str = tor_strndup(md->body, MIN(128, (size_t)avail)); - log_err(LD_BUG, "After rebuilding microdesc cache, offsets seem wrong. " - " At offset %d, I expected to find a microdescriptor starting " - " with \"onion-key\". Instead I got %s.", - (int)md->off, escaped(bad_str)); - tor_free(bad_str); - tor_assert(fast_memeq(md->body, "onion-key", 9)); - } - } SMARTLIST_FOREACH_END(md); - - smartlist_free(wrote); - - write_str_to_file(cache->journal_fname, "", 1); - cache->journal_len = 0; - cache->bytes_dropped = 0; - - new_size = cache->cache_content ? (int)cache->cache_content->size : 0; - log_info(LD_DIR, "Done rebuilding microdesc cache. " - "Saved %d bytes; %d still used.", - orig_size-new_size, new_size); - - return 0; -} - -/** Make sure that the reference count of every microdescriptor in cache is - * accurate. */ -void -microdesc_check_counts(void) -{ - microdesc_t **mdp; - if (!the_microdesc_cache) - return; - - HT_FOREACH(mdp, microdesc_map, &the_microdesc_cache->map) { - microdesc_t *md = *mdp; - unsigned int found=0; - const smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, node_t *, node, { - if (node->md == md) { - ++found; - } - }); - tor_assert(found == md->held_by_nodes); - } -} - -/** Deallocate a single microdescriptor. Note: the microdescriptor MUST have - * previously been removed from the cache if it had ever been inserted. */ -void -microdesc_free_(microdesc_t *md, const char *fname, int lineno) -{ - if (!md) - return; - - /* Make sure that the microdesc was really removed from the appropriate data - structures. */ - if (md->held_in_map) { - microdesc_cache_t *cache = get_microdesc_cache(); - microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md); - if (md2 == md) { - log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still " - "in microdesc_map", fname, lineno); - HT_REMOVE(microdesc_map, &cache->map, md); - } else { - log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_in_map " - "set, but microdesc was not in the map.", fname, lineno); - } - tor_fragile_assert(); - } - if (md->held_by_nodes) { - int found=0; - const smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, node_t *, node, { - if (node->md == md) { - ++found; - node->md = NULL; - } - }); - if (found) { - log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still " - "referenced %d node(s); held_by_nodes == %u", - fname, lineno, found, md->held_by_nodes); - } else { - log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_by_nodes " - "set to %u, but md was not referenced by any nodes", - fname, lineno, md->held_by_nodes); - } - tor_fragile_assert(); - } - //tor_assert(md->held_in_map == 0); - //tor_assert(md->held_by_nodes == 0); - - if (md->onion_pkey) - crypto_pk_free(md->onion_pkey); - tor_free(md->onion_curve25519_pkey); - if (md->body && md->saved_location != SAVED_IN_CACHE) - tor_free(md->body); - - if (md->family) { - SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp)); - smartlist_free(md->family); - } - short_policy_free(md->exit_policy); - short_policy_free(md->ipv6_exit_policy); - - tor_free(md); -} - -/** Free all storage held in the microdesc.c module. */ -void -microdesc_free_all(void) -{ - if (the_microdesc_cache) { - microdesc_cache_clear(the_microdesc_cache); - tor_free(the_microdesc_cache->cache_fname); - tor_free(the_microdesc_cache->journal_fname); - tor_free(the_microdesc_cache); - } -} - -/** If there is a microdescriptor in cache whose sha256 digest is - * d, return it. Otherwise return NULL. */ -microdesc_t * -microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache, const char *d) -{ - microdesc_t *md, search; - if (!cache) - cache = get_microdesc_cache(); - memcpy(search.digest, d, DIGEST256_LEN); - md = HT_FIND(microdesc_map, &cache->map, &search); - return md; -} - -/** Return the mean size of decriptors added to cache since it was last - * cleared. Used to estimate the size of large downloads. */ -size_t -microdesc_average_size(microdesc_cache_t *cache) -{ - if (!cache) - cache = get_microdesc_cache(); - if (!cache->n_seen) - return 512; - return (size_t)(cache->total_len_seen / cache->n_seen); -} - -/** Return a smartlist of all the sha256 digest of the microdescriptors that - * are listed in ns but not present in cache. Returns pointers - * to internals of ns; you should not free the members of the resulting - * smartlist. Omit all microdescriptors whose digest appear in skip. */ -smartlist_t * -microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, - int downloadable_only, digestmap_t *skip) -{ - smartlist_t *result = smartlist_new(); - time_t now = time(NULL); - tor_assert(ns->flavor == FLAV_MICRODESC); - SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { - if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest)) - continue; - if (downloadable_only && - !download_status_is_ready(&rs->dl_status, now, - get_options()->TestingMicrodescMaxDownloadTries)) - continue; - if (skip && digestmap_get(skip, rs->descriptor_digest)) - continue; - 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 - */ - smartlist_add(result, rs->descriptor_digest); - } SMARTLIST_FOREACH_END(rs); - return result; -} - -/** 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 - * current microdesc consensus that we don't have, and either we never asked - * for them, or we failed to download them but we're willing to retry. - */ -void -update_microdesc_downloads(time_t now) -{ - const or_options_t *options = get_options(); - networkstatus_t *consensus; - smartlist_t *missing; - digestmap_t *pending; - - if (should_delay_dir_fetches(options)) - return; - if (directory_too_idle_to_fetch_descriptors(options, now)) - return; - - consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC); - if (!consensus) - return; - - if (!we_fetch_microdescriptors(options)) - return; - - pending = digestmap_new(); - list_pending_microdesc_downloads(pending); - - missing = microdesc_list_missing_digest256(consensus, - get_microdesc_cache(), - 1, - pending); - digestmap_free(pending, NULL); - - launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC, - missing, NULL, now); - - smartlist_free(missing); -} - -/** For every microdescriptor listed in the current microdecriptor consensus, - * update its last_listed field to be at least as recent as the publication - * time of the current microdescriptor consensus. - */ -void -update_microdescs_from_networkstatus(time_t now) -{ - microdesc_cache_t *cache = get_microdesc_cache(); - microdesc_t *md; - networkstatus_t *ns = - networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC); - - if (! ns) - return; - - tor_assert(ns->flavor == FLAV_MICRODESC); - - SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { - md = microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest); - if (md && ns->valid_after > md->last_listed) - md->last_listed = ns->valid_after; - } SMARTLIST_FOREACH_END(rs); -} - -/** Return true iff we should prefer to use microdescriptors rather than - * routerdescs for building circuits. */ -int -we_use_microdescriptors_for_circuits(const or_options_t *options) -{ - int ret = options->UseMicrodescriptors; - if (ret == -1) { - /* UseMicrodescriptors is "auto"; we need to decide: */ - /* If we are configured to use bridges and none of our bridges - * know what a microdescriptor is, the answer is no. */ - if (options->UseBridges && !any_bridge_supports_microdescriptors()) - return 0; - /* Otherwise, we decide that we'll use microdescriptors iff we are - * not a server, and we're not autofetching everything. */ - /* XXX023 what does not being a server have to do with it? also there's - * a partitioning issue here where bridges differ from clients. */ - ret = !server_mode(options) && !options->FetchUselessDescriptors; - } - return ret; -} - -/** Return true iff we should try to download microdescriptors at all. */ -int -we_fetch_microdescriptors(const or_options_t *options) -{ - if (directory_caches_dir_info(options)) - return 1; - if (options->FetchUselessDescriptors) - return 1; - return we_use_microdescriptors_for_circuits(options); -} - -/** Return true iff we should try to download router descriptors at all. */ -int -we_fetch_router_descriptors(const or_options_t *options) -{ - if (directory_caches_dir_info(options)) - return 1; - if (options->FetchUselessDescriptors) - return 1; - return ! we_use_microdescriptors_for_circuits(options); -} - -/** Return the consensus flavor we actually want to use to build circuits. */ -int -usable_consensus_flavor(void) -{ - if (we_use_microdescriptors_for_circuits(get_options())) { - return FLAV_MICRODESC; - } else { - return FLAV_NS; - } -} - diff --git a/src/tor/microdesc.h b/src/tor/microdesc.h deleted file mode 100644 index 7adb8c6..0000000 --- a/src/tor/microdesc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file microdesc.h - * \brief Header file for microdesc.c. - **/ - -#ifndef TOR_MICRODESC_H -#define TOR_MICRODESC_H - -microdesc_cache_t *get_microdesc_cache(void); - -void microdesc_check_counts(void); - -smartlist_t *microdescs_add_to_cache(microdesc_cache_t *cache, - const char *s, const char *eos, saved_location_t where, - int no_save, time_t listed_at, - smartlist_t *requested_digests256); -smartlist_t *microdescs_add_list_to_cache(microdesc_cache_t *cache, - smartlist_t *descriptors, saved_location_t where, - int no_save); - -void microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force); -int microdesc_cache_rebuild(microdesc_cache_t *cache, int force); -int microdesc_cache_reload(microdesc_cache_t *cache); -void microdesc_cache_clear(microdesc_cache_t *cache); - -microdesc_t *microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache, - const char *d); - -size_t microdesc_average_size(microdesc_cache_t *cache); - -smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns, - microdesc_cache_t *cache, - int downloadable_only, - digestmap_t *skip); - -void microdesc_free_(microdesc_t *md, const char *fname, int line); -#define microdesc_free(md) \ - microdesc_free_((md), __FILE__, __LINE__) -void microdesc_free_all(void); - -void update_microdesc_downloads(time_t now); -void update_microdescs_from_networkstatus(time_t now); - -int usable_consensus_flavor(void); -int we_fetch_microdescriptors(const or_options_t *options); -int we_fetch_router_descriptors(const or_options_t *options); -int we_use_microdescriptors_for_circuits(const or_options_t *options); - -#endif - diff --git a/src/tor/networkstatus.c b/src/tor/networkstatus.c deleted file mode 100644 index 9f3d881..0000000 --- a/src/tor/networkstatus.c +++ /dev/null @@ -1,1871 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file networkstatus.c - * \brief Functions and structures for handling network status documents as a - * client or cache. - */ - -#include "or.h" -#include "channel.h" -#include "circuitmux.h" -#include "circuitmux_ewma.h" -#include "circuitstats.h" -#include "config.h" -#include "connection.h" -#include "connection_or.h" -#include "control.h" -#include "directory.h" -#include "dirserv.h" -#include "dirvote.h" -#include "entrynodes.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "relay.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" - -/** Map from lowercase nickname to identity digest of named server, if any. */ -static strmap_t *named_server_map = NULL; -/** Map from lowercase nickname to (void*)1 for all names that are listed - * as unnamed for some server in the consensus. */ -static strmap_t *unnamed_server_map = NULL; - -/** Most recently received and validated v3 consensus network status, - * of whichever type we are using for our own circuits. This will be the same - * as one of current_ns_consensus or current_md_consensus. - */ -#define current_consensus \ - (we_use_microdescriptors_for_circuits(get_options()) ? \ - current_md_consensus : current_ns_consensus) - -/** Most recently received and validated v3 "ns"-flavored consensus network - * status. */ -static networkstatus_t *current_ns_consensus = NULL; - -/** Most recently received and validated v3 "microdec"-flavored consensus - * network status. */ -static networkstatus_t *current_md_consensus = NULL; - -/** A v3 consensus networkstatus that we've received, but which we don't - * have enough certificates to be happy about. */ -typedef struct consensus_waiting_for_certs_t { - /** The consensus itself. */ - networkstatus_t *consensus; - /** The encoded version of the consensus, nul-terminated. */ - char *body; - /** When did we set the current value of consensus_waiting_for_certs? If - * this is too recent, we shouldn't try to fetch a new consensus for a - * little while, to give ourselves time to get certificates for this one. */ - time_t set_at; - /** Set to 1 if we've been holding on to it for so long we should maybe - * treat it as being bad. */ - int dl_failed; -} consensus_waiting_for_certs_t; - -/** An array, for each flavor of consensus we might want, of consensuses that - * we have downloaded, but which we cannot verify due to having insufficient - * authority certificates. */ -static consensus_waiting_for_certs_t - consensus_waiting_for_certs[N_CONSENSUS_FLAVORS]; - -/** A time before which we shouldn't try to replace the current consensus: - * this will be at some point after the next consensus becomes valid, but - * before the current consensus becomes invalid. */ -static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS]; -/** Download status for the current consensus networkstatus. */ -static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS]; - -/** True iff we have logged a warning about this OR's version being older than - * listed by the authorities. */ -static int have_warned_about_old_version = 0; -/** True iff we have logged a warning about this OR's version being newer than - * listed by the authorities. */ -static int have_warned_about_new_version = 0; - -static void routerstatus_list_update_named_server_map(void); - -/** Forget that we've warned about anything networkstatus-related, so we will - * give fresh warnings if the same behavior happens again. */ -void -networkstatus_reset_warnings(void) -{ - if (current_consensus) { - SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, - node->name_lookup_warned = 0); - } - - have_warned_about_old_version = 0; - have_warned_about_new_version = 0; -} - -/** Reset the descriptor download failure count on all networkstatus docs, so - * that we can retry any long-failed documents immediately. - */ -void -networkstatus_reset_download_failures(void) -{ - int i; - - for (i=0; i < N_CONSENSUS_FLAVORS; ++i) - download_status_reset(&consensus_dl_status[i]); -} - -/** Read every cached v3 consensus networkstatus from the disk. */ -int -router_reload_consensus_networkstatus(void) -{ - char *filename; - char *s; - const unsigned int flags = NSSET_FROM_CACHE | NSSET_DONT_DOWNLOAD_CERTS; - int flav; - - /* FFFF Suppress warnings if cached consensus is bad? */ - for (flav = 0; flav < N_CONSENSUS_FLAVORS; ++flav) { - char buf[128]; - const char *flavor = networkstatus_get_flavor_name(flav); - if (flav == FLAV_NS) { - filename = get_datadir_fname("cached-consensus"); - } else { - tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor); - filename = get_datadir_fname(buf); - } - s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); - if (s) { - if (networkstatus_set_current_consensus(s, flavor, flags) < -1) { - log_warn(LD_FS, "Couldn't load consensus %s networkstatus from \"%s\"", - flavor, filename); - } - tor_free(s); - } - tor_free(filename); - - if (flav == FLAV_NS) { - filename = get_datadir_fname("unverified-consensus"); - } else { - tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor); - filename = get_datadir_fname(buf); - } - - s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); - if (s) { - if (networkstatus_set_current_consensus(s, flavor, - flags|NSSET_WAS_WAITING_FOR_CERTS)) { - log_info(LD_FS, "Couldn't load consensus %s networkstatus from \"%s\"", - flavor, filename); - } - tor_free(s); - } - tor_free(filename); - } - - if (!current_consensus) { - if (!named_server_map) - named_server_map = strmap_new(); - if (!unnamed_server_map) - unnamed_server_map = strmap_new(); - } - - update_certificate_downloads(time(NULL)); - - routers_update_all_from_networkstatus(time(NULL), 3); - update_microdescs_from_networkstatus(time(NULL)); - - return 0; -} - -/** Free all storage held by the vote_routerstatus object rs. */ -static void -vote_routerstatus_free(vote_routerstatus_t *rs) -{ - vote_microdesc_hash_t *h, *next; - if (!rs) - return; - tor_free(rs->version); - tor_free(rs->status.exitsummary); - for (h = rs->microdesc; h; h = next) { - tor_free(h->microdesc_hash_line); - next = h->next; - tor_free(h); - } - tor_free(rs); -} - -/** Free all storage held by the routerstatus object rs. */ -void -routerstatus_free(routerstatus_t *rs) -{ - if (!rs) - return; - tor_free(rs->exitsummary); - tor_free(rs); -} - -/** Free all storage held in sig */ -void -document_signature_free(document_signature_t *sig) -{ - tor_free(sig->signature); - tor_free(sig); -} - -/** Return a newly allocated copy of sig */ -document_signature_t * -document_signature_dup(const document_signature_t *sig) -{ - document_signature_t *r = tor_memdup(sig, sizeof(document_signature_t)); - if (r->signature) - r->signature = tor_memdup(sig->signature, sig->signature_len); - return r; -} - -/** Free all storage held in ns. */ -void -networkstatus_vote_free(networkstatus_t *ns) -{ - if (!ns) - return; - - tor_free(ns->client_versions); - tor_free(ns->server_versions); - if (ns->known_flags) { - SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); - smartlist_free(ns->known_flags); - } - if (ns->weight_params) { - SMARTLIST_FOREACH(ns->weight_params, char *, c, tor_free(c)); - smartlist_free(ns->weight_params); - } - if (ns->net_params) { - SMARTLIST_FOREACH(ns->net_params, char *, c, tor_free(c)); - smartlist_free(ns->net_params); - } - if (ns->supported_methods) { - SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c)); - smartlist_free(ns->supported_methods); - } - if (ns->voters) { - SMARTLIST_FOREACH_BEGIN(ns->voters, networkstatus_voter_info_t *, voter) { - tor_free(voter->nickname); - tor_free(voter->address); - tor_free(voter->contact); - if (voter->sigs) { - SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, - document_signature_free(sig)); - smartlist_free(voter->sigs); - } - tor_free(voter); - } SMARTLIST_FOREACH_END(voter); - smartlist_free(ns->voters); - } - authority_cert_free(ns->cert); - - if (ns->routerstatus_list) { - if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) { - SMARTLIST_FOREACH(ns->routerstatus_list, vote_routerstatus_t *, rs, - vote_routerstatus_free(rs)); - } else { - SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *, rs, - routerstatus_free(rs)); - } - - smartlist_free(ns->routerstatus_list); - } - - digestmap_free(ns->desc_digest_map, NULL); - - memwipe(ns, 11, sizeof(*ns)); - tor_free(ns); -} - -/** Return the voter info from vote for the voter whose identity digest - * is identity, or NULL if no such voter is associated with - * vote. */ -networkstatus_voter_info_t * -networkstatus_get_voter_by_id(networkstatus_t *vote, - const char *identity) -{ - if (!vote || !vote->voters) - return NULL; - SMARTLIST_FOREACH(vote->voters, networkstatus_voter_info_t *, voter, - if (fast_memeq(voter->identity_digest, identity, DIGEST_LEN)) - return voter); - return NULL; -} - -/** Check whether the signature sig is correctly signed with the - * signing key in cert. Return -1 if cert doesn't match the - * signing key; otherwise set the good_signature or bad_signature flag on - * voter, and return 0. */ -int -networkstatus_check_document_signature(const networkstatus_t *consensus, - document_signature_t *sig, - const authority_cert_t *cert) -{ - char key_digest[DIGEST_LEN]; - const int dlen = sig->alg == DIGEST_SHA1 ? DIGEST_LEN : DIGEST256_LEN; - char *signed_digest; - size_t signed_digest_len; - - if (crypto_pk_get_digest(cert->signing_key, key_digest)<0) - return -1; - if (tor_memneq(sig->signing_key_digest, key_digest, DIGEST_LEN) || - tor_memneq(sig->identity_digest, cert->cache_info.identity_digest, - DIGEST_LEN)) - return -1; - - signed_digest_len = crypto_pk_keysize(cert->signing_key); - signed_digest = tor_malloc(signed_digest_len); - if (crypto_pk_public_checksig(cert->signing_key, - signed_digest, - signed_digest_len, - sig->signature, - sig->signature_len) < dlen || - tor_memneq(signed_digest, consensus->digests.d[sig->alg], dlen)) { - log_warn(LD_DIR, "Got a bad signature on a networkstatus vote"); - sig->bad_signature = 1; - } else { - sig->good_signature = 1; - } - tor_free(signed_digest); - return 0; -} - -/** Given a v3 networkstatus consensus in consensus, check every - * as-yet-unchecked signature on consensus. Return 1 if there is a - * signature from every recognized authority on it, 0 if there are - * enough good signatures from recognized authorities on it, -1 if we might - * get enough good signatures by fetching missing certificates, and -2 - * otherwise. Log messages at INFO or WARN: if warn is over 1, warn - * about every problem; if warn is at least 1, warn only if we can't get - * enough signatures; if warn is negative, log nothing at all. */ -int -networkstatus_check_consensus_signature(networkstatus_t *consensus, - int warn) -{ - int n_good = 0; - int n_missing_key = 0, n_dl_failed_key = 0; - int n_bad = 0; - int n_unknown = 0; - int n_no_signature = 0; - int n_v3_authorities = get_n_authorities(V3_DIRINFO); - int n_required = n_v3_authorities/2 + 1; - smartlist_t *list_good = smartlist_new(); - smartlist_t *list_no_signature = smartlist_new(); - smartlist_t *need_certs_from = smartlist_new(); - smartlist_t *unrecognized = smartlist_new(); - smartlist_t *missing_authorities = smartlist_new(); - int severity; - time_t now = time(NULL); - - tor_assert(consensus->type == NS_TYPE_CONSENSUS); - - SMARTLIST_FOREACH_BEGIN(consensus->voters, networkstatus_voter_info_t *, - voter) { - int good_here = 0; - int bad_here = 0; - int unknown_here = 0; - int missing_key_here = 0, dl_failed_key_here = 0; - SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) { - if (!sig->good_signature && !sig->bad_signature && - sig->signature) { - /* we can try to check the signature. */ - int is_v3_auth = trusteddirserver_get_by_v3_auth_digest( - sig->identity_digest) != NULL; - authority_cert_t *cert = - authority_cert_get_by_digests(sig->identity_digest, - sig->signing_key_digest); - tor_assert(tor_memeq(sig->identity_digest, voter->identity_digest, - DIGEST_LEN)); - - if (!is_v3_auth) { - smartlist_add(unrecognized, voter); - ++unknown_here; - continue; - } else if (!cert || cert->expires < now) { - smartlist_add(need_certs_from, voter); - ++missing_key_here; - if (authority_cert_dl_looks_uncertain(sig->identity_digest)) - ++dl_failed_key_here; - continue; - } - if (networkstatus_check_document_signature(consensus, sig, cert) < 0) { - smartlist_add(need_certs_from, voter); - ++missing_key_here; - if (authority_cert_dl_looks_uncertain(sig->identity_digest)) - ++dl_failed_key_here; - continue; - } - } - if (sig->good_signature) - ++good_here; - else if (sig->bad_signature) - ++bad_here; - } SMARTLIST_FOREACH_END(sig); - - if (good_here) { - ++n_good; - smartlist_add(list_good, voter->nickname); - } else if (bad_here) { - ++n_bad; - } else if (missing_key_here) { - ++n_missing_key; - if (dl_failed_key_here) - ++n_dl_failed_key; - } else if (unknown_here) { - ++n_unknown; - } else { - ++n_no_signature; - smartlist_add(list_no_signature, voter->nickname); - } - } SMARTLIST_FOREACH_END(voter); - - /* Now see whether we're missing any voters entirely. */ - SMARTLIST_FOREACH(router_get_trusted_dir_servers(), - dir_server_t *, ds, - { - if ((ds->type & V3_DIRINFO) && - !networkstatus_get_voter_by_id(consensus, ds->v3_identity_digest)) - smartlist_add(missing_authorities, ds); - }); - - if (warn > 1 || (warn >= 0 && - (n_good + n_missing_key - n_dl_failed_key < n_required))) { - severity = LOG_WARN; - } else { - severity = LOG_INFO; - } - - if (warn >= 0) { - SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter, - { - tor_log(severity, LD_DIR, "Consensus includes unrecognized authority " - "'%s' at %s:%d (contact %s; identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, - voter->contact?voter->contact:"n/a", - hex_str(voter->identity_digest, DIGEST_LEN)); - }); - SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter, - { - tor_log(severity, LD_DIR, "Looks like we need to download a new " - "certificate from authority '%s' at %s:%d (contact %s; " - "identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, - voter->contact?voter->contact:"n/a", - hex_str(voter->identity_digest, DIGEST_LEN)); - }); - SMARTLIST_FOREACH(missing_authorities, dir_server_t *, ds, - { - tor_log(severity, LD_DIR, "Consensus does not include configured " - "authority '%s' at %s:%d (identity %s)", - ds->nickname, ds->address, (int)ds->dir_port, - hex_str(ds->v3_identity_digest, DIGEST_LEN)); - }); - { - char *joined; - smartlist_t *sl = smartlist_new(); - char *tmp = smartlist_join_strings(list_good, " ", 0, NULL); - smartlist_add_asprintf(sl, - "A consensus needs %d good signatures from recognized " - "authorities for us to accept it. This one has %d (%s).", - n_required, n_good, tmp); - tor_free(tmp); - if (n_no_signature) { - tmp = smartlist_join_strings(list_no_signature, " ", 0, NULL); - smartlist_add_asprintf(sl, - "%d (%s) of the authorities we know didn't sign it.", - n_no_signature, tmp); - tor_free(tmp); - } - if (n_unknown) { - smartlist_add_asprintf(sl, - "It has %d signatures from authorities we don't " - "recognize.", n_unknown); - } - if (n_bad) { - smartlist_add_asprintf(sl, "%d of the signatures on it didn't verify " - "correctly.", n_bad); - } - if (n_missing_key) { - smartlist_add_asprintf(sl, - "We were unable to check %d of the signatures, " - "because we were missing the keys.", n_missing_key); - } - joined = smartlist_join_strings(sl, " ", 0, NULL); - tor_log(severity, LD_DIR, "%s", joined); - tor_free(joined); - SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); - smartlist_free(sl); - } - } - - smartlist_free(list_good); - smartlist_free(list_no_signature); - smartlist_free(unrecognized); - smartlist_free(need_certs_from); - smartlist_free(missing_authorities); - - if (n_good == n_v3_authorities) - return 1; - else if (n_good >= n_required) - return 0; - else if (n_good + n_missing_key >= n_required) - return -1; - else - return -2; -} - -/** Helper: return a newly allocated string containing the name of the filename - * where we plan to cache the network status with the given identity digest. */ -char * -networkstatus_get_cache_filename(const char *identity_digest) -{ - char fp[HEX_DIGEST_LEN+1]; - base16_encode(fp, HEX_DIGEST_LEN+1, identity_digest, DIGEST_LEN); - return get_datadir_fname2("cached-status", fp); -} - -/** How far in the future do we allow a network-status to get before removing - * it? (seconds) */ -#define NETWORKSTATUS_ALLOW_SKEW (24*60*60) - -/** Helper for bsearching a list of routerstatus_t pointers: compare a - * digest in the key to the identity digest of a routerstatus_t. */ -int -compare_digest_to_routerstatus_entry(const void *_key, const void **_member) -{ - const char *key = _key; - const routerstatus_t *rs = *_member; - return tor_memcmp(key, rs->identity_digest, DIGEST_LEN); -} - -/** Helper for bsearching a list of routerstatus_t pointers: compare a - * digest in the key to the identity digest of a routerstatus_t. */ -int -compare_digest_to_vote_routerstatus_entry(const void *_key, - const void **_member) -{ - const char *key = _key; - const vote_routerstatus_t *vrs = *_member; - return tor_memcmp(key, vrs->status.identity_digest, DIGEST_LEN); -} - -/** As networkstatus_find_entry, but do not return a const pointer */ -routerstatus_t * -networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest) -{ - return smartlist_bsearch(ns->routerstatus_list, digest, - compare_digest_to_routerstatus_entry); -} - -/** Return the entry in ns for the identity digest digest, or - * NULL if none was found. */ -const routerstatus_t * -networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest) -{ - return networkstatus_vote_find_mutable_entry(ns, digest); -} - -/*XXXX MOVE make this static once functions are moved into this file. */ -/** Search the routerstatuses in ns for one whose identity digest is - * digest. Return value and set *found_out as for - * smartlist_bsearch_idx(). */ -int -networkstatus_vote_find_entry_idx(networkstatus_t *ns, - const char *digest, int *found_out) -{ - return smartlist_bsearch_idx(ns->routerstatus_list, digest, - compare_digest_to_routerstatus_entry, - found_out); -} - -/** As router_get_consensus_status_by_descriptor_digest, but does not return - * a const pointer. */ -routerstatus_t * -router_get_mutable_consensus_status_by_descriptor_digest( - networkstatus_t *consensus, - const char *digest) -{ - if (!consensus) - consensus = current_consensus; - if (!consensus) - return NULL; - if (!consensus->desc_digest_map) { - digestmap_t *m = consensus->desc_digest_map = digestmap_new(); - SMARTLIST_FOREACH(consensus->routerstatus_list, - routerstatus_t *, rs, - { - digestmap_set(m, rs->descriptor_digest, rs); - }); - } - return digestmap_get(consensus->desc_digest_map, digest); -} - -/** Return the consensus view of the status of the router whose current - * descriptor digest in consensus is digest, or NULL if - * no such router is known. */ -const routerstatus_t * -router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus, - const char *digest) -{ - return router_get_mutable_consensus_status_by_descriptor_digest( - consensus, digest); -} - -/** Given the digest of a router descriptor, return its current download - * status, or NULL if the digest is unrecognized. */ -download_status_t * -router_get_dl_status_by_descriptor_digest(const char *d) -{ - routerstatus_t *rs; - if (!current_ns_consensus) - return NULL; - if ((rs = router_get_mutable_consensus_status_by_descriptor_digest( - current_ns_consensus, d))) - return &rs->dl_status; - - return NULL; -} - -/** As router_get_consensus_status_by_id, but do not return a const pointer */ -routerstatus_t * -router_get_mutable_consensus_status_by_id(const char *digest) -{ - if (!current_consensus) - return NULL; - return smartlist_bsearch(current_consensus->routerstatus_list, digest, - compare_digest_to_routerstatus_entry); -} - -/** Return the consensus view of the status of the router whose identity - * digest is digest, or NULL if we don't know about any such router. */ -const routerstatus_t * -router_get_consensus_status_by_id(const char *digest) -{ - return router_get_mutable_consensus_status_by_id(digest); -} - -/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return - * the corresponding routerstatus_t, or NULL if none exists. Warn the - * user if warn_if_unnamed is set, and they have specified a router by - * nickname, but the Named flag isn't set for that router. */ -const routerstatus_t * -router_get_consensus_status_by_nickname(const char *nickname, - int warn_if_unnamed) -{ - const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed); - if (node) - return node->rs; - else - return NULL; -} - -/** Return the identity digest that's mapped to officially by - * nickname. */ -const char * -networkstatus_get_router_digest_by_nickname(const char *nickname) -{ - if (!named_server_map) - return NULL; - return strmap_get_lc(named_server_map, nickname); -} - -/** Return true iff nickname is disallowed from being the nickname - * of any server. */ -int -networkstatus_nickname_is_unnamed(const char *nickname) -{ - if (!unnamed_server_map) - return 0; - return strmap_get_lc(unnamed_server_map, nickname) != NULL; -} - -/** How frequently do directory authorities re-download fresh networkstatus - * documents? */ -#define AUTHORITY_NS_CACHE_INTERVAL (10*60) - -/** How frequently do non-authority directory caches re-download fresh - * networkstatus documents? */ -#define NONAUTHORITY_NS_CACHE_INTERVAL (60*60) - -/** Return true iff, given the options listed in options, flavor - * is the flavor of a consensus networkstatus that we would like to fetch. */ -static int -we_want_to_fetch_flavor(const or_options_t *options, int flavor) -{ - if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) { - /* This flavor is crazy; we don't want it */ - /*XXXX handle unrecognized flavors later */ - return 0; - } - if (authdir_mode_v3(options) || directory_caches_dir_info(options)) { - /* We want to serve all flavors to others, regardless if we would use - * it ourselves. */ - return 1; - } - if (options->FetchUselessDescriptors) { - /* In order to get all descriptors, we need to fetch all consensuses. */ - return 1; - } - /* Otherwise, we want the flavor only if we want to use it to build - * circuits. */ - return flavor == usable_consensus_flavor(); -} - -/** How long will we hang onto a possibly live consensus for which we're - * fetching certs before we check whether there is a better one? */ -#define DELAY_WHILE_FETCHING_CERTS (20*60) - -/** If we want to download a fresh consensus, launch a new download as - * appropriate. */ -static void -update_consensus_networkstatus_downloads(time_t now) -{ - int i; - const or_options_t *options = get_options(); - - for (i=0; i < N_CONSENSUS_FLAVORS; ++i) { - /* XXXX need some way to download unknown flavors if we are caching. */ - const char *resource; - consensus_waiting_for_certs_t *waiting; - networkstatus_t *c; - - if (! we_want_to_fetch_flavor(options, i)) - continue; - - c = networkstatus_get_latest_consensus_by_flavor(i); - if (! (c && c->valid_after <= now && now <= c->valid_until)) { - /* No live consensus? Get one now!*/ - time_to_download_next_consensus[i] = now; - } - - if (time_to_download_next_consensus[i] > now) - continue; /* Wait until the current consensus is older. */ - - resource = networkstatus_get_flavor_name(i); - - if (!download_status_is_ready(&consensus_dl_status[i], now, - options->TestingConsensusMaxDownloadTries)) - continue; /* We failed downloading a consensus too recently. */ - if (connection_dir_get_by_purpose_and_resource( - DIR_PURPOSE_FETCH_CONSENSUS, resource)) - continue; /* There's an in-progress download.*/ - - waiting = &consensus_waiting_for_certs[i]; - if (waiting->consensus) { - /* XXXX make sure this doesn't delay sane downloads. */ - if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) { - continue; /* We're still getting certs for this one. */ - } else { - if (!waiting->dl_failed) { - download_status_failed(&consensus_dl_status[i], 0); - waiting->dl_failed=1; - } - } - } - - log_info(LD_DIR, "Launching %s networkstatus consensus download.", - networkstatus_get_flavor_name(i)); - - directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS, - ROUTER_PURPOSE_GENERAL, resource, - PDS_RETRY_IF_NO_SERVERS); - } -} - -/** Called when an attempt to download a consensus fails: note that the - * failure occurred, and possibly retry. */ -void -networkstatus_consensus_download_failed(int status_code, const char *flavname) -{ - int flav = networkstatus_parse_flavor_name(flavname); - if (flav >= 0) { - tor_assert(flav < N_CONSENSUS_FLAVORS); - /* XXXX handle unrecognized flavors */ - download_status_failed(&consensus_dl_status[flav], status_code); - /* Retry immediately, if appropriate. */ - update_consensus_networkstatus_downloads(time(NULL)); - } -} - -/** How long do we (as a cache) wait after a consensus becomes non-fresh - * before trying to fetch another? */ -#define CONSENSUS_MIN_SECONDS_BEFORE_CACHING 120 - -/** Update the time at which we'll consider replacing the current - * consensus of flavor flav */ -static void -update_consensus_networkstatus_fetch_time_impl(time_t now, int flav) -{ - const or_options_t *options = get_options(); - networkstatus_t *c = networkstatus_get_latest_consensus_by_flavor(flav); - const char *flavor = networkstatus_get_flavor_name(flav); - if (! we_want_to_fetch_flavor(get_options(), flav)) - return; - - if (c && c->valid_after <= now && now <= c->valid_until) { - long dl_interval; - long interval = c->fresh_until - c->valid_after; - long min_sec_before_caching = CONSENSUS_MIN_SECONDS_BEFORE_CACHING; - time_t start; - - if (min_sec_before_caching > interval/16) { - /* Usually we allow 2-minutes slop factor in case clocks get - desynchronized a little. If we're on a private network with - a crazy-fast voting interval, though, 2 minutes may be too - much. */ - min_sec_before_caching = interval/16; - } - - if (directory_fetches_dir_info_early(options)) { - /* We want to cache the next one at some point after this one - * is no longer fresh... */ - start = c->fresh_until + min_sec_before_caching; - /* Some clients may need the consensus sooner than others. */ - if (options->FetchDirInfoExtraEarly || authdir_mode_v3(options)) { - dl_interval = 60; - if (min_sec_before_caching + dl_interval > interval) - dl_interval = interval/2; - } else { - /* But only in the first half-interval after that. */ - dl_interval = interval/2; - } - } else { - /* We're an ordinary client or a bridge. Give all the caches enough - * time to download the consensus. */ - start = c->fresh_until + (interval*3)/4; - /* But download the next one well before this one is expired. */ - dl_interval = ((c->valid_until - start) * 7 )/ 8; - - /* If we're a bridge user, make use of the numbers we just computed - * to choose the rest of the interval *after* them. */ - if (directory_fetches_dir_info_later(options)) { - /* Give all the *clients* enough time to download the consensus. */ - start = start + dl_interval + min_sec_before_caching; - /* But try to get it before ours actually expires. */ - dl_interval = (c->valid_until - start) - min_sec_before_caching; - } - } - if (dl_interval < 1) - dl_interval = 1; - /* We must not try to replace c while it's still fresh: */ - tor_assert(c->fresh_until < start); - /* We must download the next one before c is invalid: */ - tor_assert(start+dl_interval < c->valid_until); - time_to_download_next_consensus[flav] = - start + crypto_rand_int((int)dl_interval); - { - char tbuf1[ISO_TIME_LEN+1]; - char tbuf2[ISO_TIME_LEN+1]; - char tbuf3[ISO_TIME_LEN+1]; - format_local_iso_time(tbuf1, c->fresh_until); - format_local_iso_time(tbuf2, c->valid_until); - format_local_iso_time(tbuf3, time_to_download_next_consensus[flav]); - log_info(LD_DIR, "Live %s consensus %s the most recent until %s and " - "will expire at %s; fetching the next one at %s.", - flavor, (c->fresh_until > now) ? "will be" : "was", - tbuf1, tbuf2, tbuf3); - } - } else { - time_to_download_next_consensus[flav] = now; - log_info(LD_DIR, "No live %s consensus; we should fetch one immediately.", - flavor); - } -} - -/** Update the time at which we'll consider replacing the current - * consensus of flavor 'flavor' */ -void -update_consensus_networkstatus_fetch_time(time_t now) -{ - int i; - for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { - if (we_want_to_fetch_flavor(get_options(), i)) - update_consensus_networkstatus_fetch_time_impl(now, i); - } -} - -/** Return 1 if there's a reason we shouldn't try any directory - * fetches yet (e.g. we demand bridges and none are yet known). - * Else return 0. */ -int -should_delay_dir_fetches(const or_options_t *options) -{ - if (options->UseBridges && !any_bridge_descriptors_known()) { - log_info(LD_DIR, "delaying dir fetches (no running bridges known)"); - return 1; - } - return 0; -} - -/** Launch requests for networkstatus documents and authority certificates as - * appropriate. */ -void -update_networkstatus_downloads(time_t now) -{ - const or_options_t *options = get_options(); - if (should_delay_dir_fetches(options)) - return; - update_consensus_networkstatus_downloads(now); - update_certificate_downloads(now); -} - -/** Launch requests as appropriate for missing directory authority - * certificates. */ -void -update_certificate_downloads(time_t now) -{ - int i; - for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) { - if (consensus_waiting_for_certs[i].consensus) - authority_certs_fetch_missing(consensus_waiting_for_certs[i].consensus, - now); - } - - if (current_ns_consensus) - authority_certs_fetch_missing(current_ns_consensus, now); - if (current_md_consensus) - authority_certs_fetch_missing(current_md_consensus, now); -} - -/** Return 1 if we have a consensus but we don't have enough certificates - * to start using it yet. */ -int -consensus_is_waiting_for_certs(void) -{ - return consensus_waiting_for_certs[usable_consensus_flavor()].consensus - ? 1 : 0; -} - -/** Return the most recent consensus that we have downloaded, or NULL if we - * don't have one. */ -networkstatus_t * -networkstatus_get_latest_consensus(void) -{ - return current_consensus; -} - -/** Return the latest consensus we have whose flavor matches f, or NULL - * if we don't have one. */ -networkstatus_t * -networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) -{ - if (f == FLAV_NS) - return current_ns_consensus; - else if (f == FLAV_MICRODESC) - return current_md_consensus; - else { - tor_assert(0); - return NULL; - } -} - -/** Return the most recent consensus that we have downloaded, or NULL if it is - * no longer live. */ -networkstatus_t * -networkstatus_get_live_consensus(time_t now) -{ - if (current_consensus && - current_consensus->valid_after <= now && - now <= current_consensus->valid_until) - return current_consensus; - else - return NULL; -} - -/* XXXX remove this in favor of get_live_consensus. But actually, - * leave something like it for bridge users, who need to not totally - * lose if they spend a while fetching a new consensus. */ -/** As networkstatus_get_live_consensus(), but is way more tolerant of expired - * consensuses. */ -networkstatus_t * -networkstatus_get_reasonably_live_consensus(time_t now, int flavor) -{ -#define REASONABLY_LIVE_TIME (24*60*60) - networkstatus_t *consensus = - networkstatus_get_latest_consensus_by_flavor(flavor); - if (consensus && - consensus->valid_after <= now && - now <= consensus->valid_until+REASONABLY_LIVE_TIME) - return consensus; - else - return NULL; -} - -/** Given two router status entries for the same router identity, return 1 if - * if the contents have changed between them. Otherwise, return 0. */ -static int -routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) -{ - tor_assert(tor_memeq(a->identity_digest, b->identity_digest, DIGEST_LEN)); - - return strcmp(a->nickname, b->nickname) || - fast_memneq(a->descriptor_digest, b->descriptor_digest, DIGEST_LEN) || - a->addr != b->addr || - a->or_port != b->or_port || - a->dir_port != b->dir_port || - a->is_authority != b->is_authority || - a->is_exit != b->is_exit || - a->is_stable != b->is_stable || - a->is_fast != b->is_fast || - a->is_flagged_running != b->is_flagged_running || - a->is_named != b->is_named || - a->is_unnamed != b->is_unnamed || - a->is_valid != b->is_valid || - a->is_possible_guard != b->is_possible_guard || - 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; -} - -/** Notify controllers of any router status entries that changed between - * old_c and new_c. */ -static void -notify_control_networkstatus_changed(const networkstatus_t *old_c, - const networkstatus_t *new_c) -{ - smartlist_t *changed; - if (old_c == new_c) - return; - - /* tell the controller exactly which relays are still listed, as well - * as what they're listed as */ - control_event_newconsensus(new_c); - - if (!control_event_is_interesting(EVENT_NS)) - return; - - if (!old_c) { - control_event_networkstatus_changed(new_c->routerstatus_list); - return; - } - changed = smartlist_new(); - - SMARTLIST_FOREACH_JOIN( - old_c->routerstatus_list, const routerstatus_t *, rs_old, - new_c->routerstatus_list, const routerstatus_t *, rs_new, - tor_memcmp(rs_old->identity_digest, - rs_new->identity_digest, DIGEST_LEN), - smartlist_add(changed, (void*) rs_new)) { - if (routerstatus_has_changed(rs_old, rs_new)) - smartlist_add(changed, (void*)rs_new); - } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new); - - control_event_networkstatus_changed(changed); - smartlist_free(changed); -} - -/** Copy all the ancillary information (like router download status and so on) - * from old_c to new_c. */ -static void -networkstatus_copy_old_consensus_info(networkstatus_t *new_c, - const networkstatus_t *old_c) -{ - if (old_c == new_c) - return; - if (!old_c || !smartlist_len(old_c->routerstatus_list)) - return; - - SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old, - new_c->routerstatus_list, routerstatus_t *, rs_new, - tor_memcmp(rs_old->identity_digest, - rs_new->identity_digest, DIGEST_LEN), - STMT_NIL) { - /* Okay, so we're looking at the same identity. */ - rs_new->last_dir_503_at = rs_old->last_dir_503_at; - - if (tor_memeq(rs_old->descriptor_digest, rs_new->descriptor_digest, - DIGEST_LEN)) { - /* And the same descriptor too! */ - memcpy(&rs_new->dl_status, &rs_old->dl_status,sizeof(download_status_t)); - } - } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new); -} - -/** Try to replace the current cached v3 networkstatus with the one in - * consensus. If we don't have enough certificates to validate it, - * store it in consensus_waiting_for_certs and launch a certificate fetch. - * - * If flags & NSSET_FROM_CACHE, this networkstatus has come from the disk - * cache. If flags & NSSET_WAS_WAITING_FOR_CERTS, this networkstatus was - * already received, but we were waiting for certificates on it. If flags & - * NSSET_DONT_DOWNLOAD_CERTS, do not launch certificate downloads as needed. - * If flags & NSSET_ACCEPT_OBSOLETE, then we should be willing to take this - * consensus, even if it comes from many days in the past. - * - * Return 0 on success, <0 on failure. On failure, caller should increment - * the failure count as appropriate. - * - * We return -1 for mild failures that don't need to be reported to the - * user, and -2 for more serious problems. - */ -int -networkstatus_set_current_consensus(const char *consensus, - const char *flavor, - unsigned flags) -{ - networkstatus_t *c=NULL; - int r, result = -1; - time_t now = time(NULL); - const or_options_t *options = get_options(); - char *unverified_fname = NULL, *consensus_fname = NULL; - int flav = networkstatus_parse_flavor_name(flavor); - const unsigned from_cache = flags & NSSET_FROM_CACHE; - const unsigned was_waiting_for_certs = flags & NSSET_WAS_WAITING_FOR_CERTS; - const unsigned dl_certs = !(flags & NSSET_DONT_DOWNLOAD_CERTS); - const unsigned accept_obsolete = flags & NSSET_ACCEPT_OBSOLETE; - const unsigned require_flavor = flags & NSSET_REQUIRE_FLAVOR; - const digests_t *current_digests = NULL; - consensus_waiting_for_certs_t *waiting = NULL; - time_t current_valid_after = 0; - int free_consensus = 1; /* Free 'c' at the end of the function */ - int old_ewma_enabled; - - if (flav < 0) { - /* XXXX we don't handle unrecognized flavors yet. */ - log_warn(LD_BUG, "Unrecognized consensus flavor %s", flavor); - return -2; - } - - /* Make sure it's parseable. */ - c = networkstatus_parse_vote_from_string(consensus, NULL, NS_TYPE_CONSENSUS); - if (!c) { - log_warn(LD_DIR, "Unable to parse networkstatus consensus"); - result = -2; - goto done; - } - - if ((int)c->flavor != flav) { - /* This wasn't the flavor we thought we were getting. */ - if (require_flavor) { - log_warn(LD_DIR, "Got consensus with unexpected flavor %s (wanted %s)", - networkstatus_get_flavor_name(c->flavor), flavor); - goto done; - } - flav = c->flavor; - flavor = networkstatus_get_flavor_name(flav); - } - - if (flav != usable_consensus_flavor() && - !directory_caches_dir_info(options)) { - /* This consensus is totally boring to us: we won't use it, and we won't - * serve it. Drop it. */ - goto done; - } - - if (from_cache && !accept_obsolete && - c->valid_until < now-OLD_ROUTER_DESC_MAX_AGE) { - log_info(LD_DIR, "Loaded an expired consensus. Discarding."); - goto done; - } - - if (!strcmp(flavor, "ns")) { - consensus_fname = get_datadir_fname("cached-consensus"); - unverified_fname = get_datadir_fname("unverified-consensus"); - if (current_ns_consensus) { - current_digests = ¤t_ns_consensus->digests; - current_valid_after = current_ns_consensus->valid_after; - } - } else if (!strcmp(flavor, "microdesc")) { - consensus_fname = get_datadir_fname("cached-microdesc-consensus"); - unverified_fname = get_datadir_fname("unverified-microdesc-consensus"); - if (current_md_consensus) { - current_digests = ¤t_md_consensus->digests; - current_valid_after = current_md_consensus->valid_after; - } - } else { - cached_dir_t *cur; - char buf[128]; - tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor); - consensus_fname = get_datadir_fname(buf); - tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor); - unverified_fname = get_datadir_fname(buf); - cur = dirserv_get_consensus(flavor); - if (cur) { - current_digests = &cur->digests; - current_valid_after = cur->published; - } - } - - if (current_digests && - tor_memeq(&c->digests, current_digests, sizeof(c->digests))) { - /* We already have this one. That's a failure. */ - log_info(LD_DIR, "Got a %s consensus we already have", flavor); - goto done; - } - - if (current_valid_after && c->valid_after <= current_valid_after) { - /* We have a newer one. There's no point in accepting this one, - * even if it's great. */ - log_info(LD_DIR, "Got a %s consensus at least as old as the one we have", - flavor); - goto done; - } - - /* Make sure it's signed enough. */ - if ((r=networkstatus_check_consensus_signature(c, 1))<0) { - if (r == -1) { - /* Okay, so it _might_ be signed enough if we get more certificates. */ - if (!was_waiting_for_certs) { - log_info(LD_DIR, - "Not enough certificates to check networkstatus consensus"); - } - if (!current_valid_after || - c->valid_after > current_valid_after) { - waiting = &consensus_waiting_for_certs[flav]; - networkstatus_vote_free(waiting->consensus); - tor_free(waiting->body); - waiting->consensus = c; - free_consensus = 0; - waiting->body = tor_strdup(consensus); - waiting->set_at = now; - waiting->dl_failed = 0; - if (!from_cache) { - write_str_to_file(unverified_fname, consensus, 0); - } - if (dl_certs) - authority_certs_fetch_missing(c, now); - /* This case is not a success or a failure until we get the certs - * or fail to get the certs. */ - result = 0; - } else { - /* Even if we had enough signatures, we'd never use this as the - * latest consensus. */ - if (was_waiting_for_certs && from_cache) - unlink(unverified_fname); - } - goto done; - } else { - /* This can never be signed enough: Kill it. */ - if (!was_waiting_for_certs) { - log_warn(LD_DIR, "Not enough good signatures on networkstatus " - "consensus"); - result = -2; - } - if (was_waiting_for_certs && (r < -1) && from_cache) - unlink(unverified_fname); - goto done; - } - } - - if (!from_cache && flav == usable_consensus_flavor()) - control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED"); - - /* Are we missing any certificates at all? */ - if (r != 1 && dl_certs) - authority_certs_fetch_missing(c, now); - - if (flav == usable_consensus_flavor()) { - notify_control_networkstatus_changed(current_consensus, c); - } - if (flav == FLAV_NS) { - if (current_ns_consensus) { - networkstatus_copy_old_consensus_info(c, current_ns_consensus); - networkstatus_vote_free(current_ns_consensus); - /* Defensive programming : we should set current_consensus very soon, - * but we're about to call some stuff in the meantime, and leaving this - * dangling pointer around has proven to be trouble. */ - current_ns_consensus = NULL; - } - current_ns_consensus = c; - free_consensus = 0; /* avoid free */ - } else if (flav == FLAV_MICRODESC) { - if (current_md_consensus) { - networkstatus_copy_old_consensus_info(c, current_md_consensus); - networkstatus_vote_free(current_md_consensus); - /* more defensive programming */ - current_md_consensus = NULL; - } - current_md_consensus = c; - free_consensus = 0; /* avoid free */ - } - - waiting = &consensus_waiting_for_certs[flav]; - if (waiting->consensus && - waiting->consensus->valid_after <= c->valid_after) { - networkstatus_vote_free(waiting->consensus); - waiting->consensus = NULL; - if (consensus != waiting->body) - tor_free(waiting->body); - else - waiting->body = NULL; - waiting->set_at = 0; - waiting->dl_failed = 0; - unlink(unverified_fname); - } - - /* Reset the failure count only if this consensus is actually valid. */ - if (c->valid_after <= now && now <= c->valid_until) { - download_status_reset(&consensus_dl_status[flav]); - } else { - if (!from_cache) - download_status_failed(&consensus_dl_status[flav], 0); - } - - if (flav == usable_consensus_flavor()) { - /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/ - update_consensus_networkstatus_fetch_time(now); - - nodelist_set_consensus(current_consensus); - - dirvote_recalculate_timing(options, now); - routerstatus_list_update_named_server_map(); - - /* Update ewma and adjust policy if needed; first cache the old value */ - old_ewma_enabled = cell_ewma_enabled(); - /* Change the cell EWMA settings */ - cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); - /* If we just enabled ewma, set the cmux policy on all active channels */ - if (cell_ewma_enabled() && !old_ewma_enabled) { - channel_set_cmux_policy_everywhere(&ewma_policy); - } else if (!cell_ewma_enabled() && old_ewma_enabled) { - /* Turn it off everywhere */ - channel_set_cmux_policy_everywhere(NULL); - } - - /* XXXX024 this call might be unnecessary here: can changing the - * current consensus really alter our view of any OR's rate limits? */ - connection_or_update_token_buckets(get_connection_array(), options); - - circuit_build_times_new_consensus_params(get_circuit_build_times_mutable(), - current_consensus); - } - - if (directory_caches_dir_info(options)) { - dirserv_set_cached_consensus_networkstatus(consensus, - flavor, - &c->digests, - c->valid_after); - } - - if (!from_cache) { - write_str_to_file(consensus_fname, consensus, 0); - } - -/** If a consensus appears more than this many seconds before its declared - * valid-after time, declare that our clock is skewed. */ -#define EARLY_CONSENSUS_NOTICE_SKEW 60 - - if (now < c->valid_after - EARLY_CONSENSUS_NOTICE_SKEW) { - char tbuf[ISO_TIME_LEN+1]; - char dbuf[64]; - long delta = now - c->valid_after; - format_iso_time(tbuf, c->valid_after); - format_time_interval(dbuf, sizeof(dbuf), delta); - log_warn(LD_GENERAL, "Our clock is %s behind the time published in the " - "consensus network status document (%s UTC). Tor needs an " - "accurate clock to work correctly. Please check your time and " - "date settings!", dbuf, tbuf); - control_event_general_status(LOG_WARN, - "CLOCK_SKEW MIN_SKEW=%ld SOURCE=CONSENSUS", delta); - } - - router_dir_info_changed(); - - result = 0; - done: - if (free_consensus) - networkstatus_vote_free(c); - tor_free(consensus_fname); - tor_free(unverified_fname); - return result; -} - -/** Called when we have gotten more certificates: see whether we can - * now verify a pending consensus. */ -void -networkstatus_note_certs_arrived(void) -{ - int i; - for (i=0; iconsensus) - continue; - if (networkstatus_check_consensus_signature(waiting->consensus, 0)>=0) { - char *waiting_body = waiting->body; - if (!networkstatus_set_current_consensus( - waiting_body, - networkstatus_get_flavor_name(i), - NSSET_WAS_WAITING_FOR_CERTS)) { - tor_free(waiting_body); - } - } - } -} - -/** If the network-status list has changed since the last time we called this - * function, update the status of every routerinfo from the network-status - * list. If dir_version is 2, it's a v2 networkstatus that changed. - * If dir_version is 3, it's a v3 consensus that changed. - */ -void -routers_update_all_from_networkstatus(time_t now, int dir_version) -{ - routerlist_t *rl = router_get_routerlist(); - networkstatus_t *consensus = networkstatus_get_reasonably_live_consensus(now, - FLAV_NS); - - if (!consensus || dir_version < 3) /* nothing more we should do */ - return; - - /* calls router_dir_info_changed() when it's done -- more routers - * might be up or down now, which might affect whether there's enough - * directory info. */ - routers_update_status_from_consensus_networkstatus(rl->routers, 0); - - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, - ri->cache_info.routerlist_index = ri_sl_idx); - if (rl->old_routers) - signed_descs_update_status_from_consensus_networkstatus(rl->old_routers); - - if (!have_warned_about_old_version) { - int is_server = server_mode(get_options()); - version_status_t status; - const char *recommended = is_server ? - consensus->server_versions : consensus->client_versions; - status = tor_version_is_obsolete(VERSION, recommended); - - if (status == VS_RECOMMENDED) { - log_info(LD_GENERAL, "The directory authorities say my version is ok."); - } else if (status == VS_EMPTY) { - log_info(LD_GENERAL, - "The directory authorities don't recommend any versions."); - } else if (status == VS_NEW || status == VS_NEW_IN_SERIES) { - if (!have_warned_about_new_version) { - log_notice(LD_GENERAL, "This version of Tor (%s) is newer than any " - "recommended version%s, according to the directory " - "authorities. Recommended versions are: %s", - VERSION, - status == VS_NEW_IN_SERIES ? " in its series" : "", - recommended); - have_warned_about_new_version = 1; - control_event_general_status(LOG_WARN, "DANGEROUS_VERSION " - "CURRENT=%s REASON=%s RECOMMENDED=\"%s\"", - VERSION, "NEW", recommended); - } - } else { - log_warn(LD_GENERAL, "Please upgrade! " - "This version of Tor (%s) is %s, according to the directory " - "authorities. Recommended versions are: %s", - VERSION, - status == VS_OLD ? "obsolete" : "not recommended", - recommended); - have_warned_about_old_version = 1; - control_event_general_status(LOG_WARN, "DANGEROUS_VERSION " - "CURRENT=%s REASON=%s RECOMMENDED=\"%s\"", - VERSION, status == VS_OLD ? "OBSOLETE" : "UNRECOMMENDED", - recommended); - } - } -} - -/** Update our view of the list of named servers from the most recently - * retrieved networkstatus consensus. */ -static void -routerstatus_list_update_named_server_map(void) -{ - if (!current_consensus) - return; - - strmap_free(named_server_map, tor_free_); - named_server_map = strmap_new(); - strmap_free(unnamed_server_map, NULL); - unnamed_server_map = strmap_new(); - SMARTLIST_FOREACH_BEGIN(current_consensus->routerstatus_list, - const routerstatus_t *, rs) { - if (rs->is_named) { - strmap_set_lc(named_server_map, rs->nickname, - tor_memdup(rs->identity_digest, DIGEST_LEN)); - } - if (rs->is_unnamed) { - strmap_set_lc(unnamed_server_map, rs->nickname, (void*)1); - } - } SMARTLIST_FOREACH_END(rs); -} - -/** Given a list routers of routerinfo_t *, update each status field - * according to our current consensus networkstatus. May re-order - * routers. */ -void -routers_update_status_from_consensus_networkstatus(smartlist_t *routers, - int reset_failures) -{ - const or_options_t *options = get_options(); - int authdir = authdir_mode_v3(options); - networkstatus_t *ns = current_consensus; - if (!ns || !smartlist_len(ns->routerstatus_list)) - return; - - routers_sort_by_identity(routers); - - SMARTLIST_FOREACH_JOIN(ns->routerstatus_list, routerstatus_t *, rs, - routers, routerinfo_t *, router, - tor_memcmp(rs->identity_digest, - router->cache_info.identity_digest, DIGEST_LEN), - { - }) { - /* Is it the same descriptor, or only the same identity? */ - if (tor_memeq(router->cache_info.signed_descriptor_digest, - rs->descriptor_digest, DIGEST_LEN)) { - if (ns->valid_until > router->cache_info.last_listed_as_valid_until) - router->cache_info.last_listed_as_valid_until = ns->valid_until; - } - - if (authdir) { - /* If we _are_ an authority, we should check whether this router - * is one that will cause us to need a reachability test. */ - routerinfo_t *old_router = - router_get_mutable_by_digest(router->cache_info.identity_digest); - if (old_router != router) { - router->needs_retest_if_added = - dirserv_should_launch_reachability_test(router, old_router); - } - } - if (reset_failures) { - download_status_reset(&rs->dl_status); - } - } SMARTLIST_FOREACH_JOIN_END(rs, router); - - router_dir_info_changed(); -} - -/** Given a list of signed_descriptor_t, update their fields (mainly, when - * they were last listed) from the most recent consensus. */ -void -signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) -{ - networkstatus_t *ns = current_ns_consensus; - if (!ns) - return; - - if (!ns->desc_digest_map) { - char dummy[DIGEST_LEN]; - /* instantiates the digest map. */ - memset(dummy, 0, sizeof(dummy)); - router_get_consensus_status_by_descriptor_digest(ns, dummy); - } - SMARTLIST_FOREACH(descs, signed_descriptor_t *, d, - { - const routerstatus_t *rs = digestmap_get(ns->desc_digest_map, - d->signed_descriptor_digest); - if (rs) { - if (ns->valid_until > d->last_listed_as_valid_until) - d->last_listed_as_valid_until = ns->valid_until; - } - }); -} - -/** Generate networkstatus lines for a single routerstatus_t object, and - * return the result in a newly allocated string. Used only by controller - * interface (for now.) */ -char * -networkstatus_getinfo_helper_single(const routerstatus_t *rs) -{ - return routerstatus_format_entry(rs, NULL, NS_CONTROL_PORT, NULL); -} - -/** Alloc and return a string describing routerstatuses for the most - * recent info of each router we know about that is of purpose - * purpose_string. Return NULL if unrecognized purpose. - * - * Right now this function is oriented toward listing bridges (you - * shouldn't use this for general-purpose routers, since those - * should be listed from the consensus, not from the routers list). */ -char * -networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) -{ - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - char *answer; - routerlist_t *rl = router_get_routerlist(); - smartlist_t *statuses; - uint8_t purpose = router_purpose_from_string(purpose_string); - routerstatus_t rs; - int bridge_auth = authdir_mode_bridge(get_options()); - - if (purpose == ROUTER_PURPOSE_UNKNOWN) { - log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.", - purpose_string); - return NULL; - } - - statuses = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { - node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); - if (!node) - continue; - if (ri->cache_info.published_on < cutoff) - continue; - if (ri->purpose != purpose) - continue; - if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) - dirserv_set_router_is_running(ri, now); - /* then generate and write out status lines for each of them */ - set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0, 0, 0); - smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs)); - } SMARTLIST_FOREACH_END(ri); - - answer = smartlist_join_strings(statuses, "", 0, NULL); - SMARTLIST_FOREACH(statuses, char *, cp, tor_free(cp)); - smartlist_free(statuses); - return answer; -} - -/** Write out router status entries for all our bridge descriptors. */ -void -networkstatus_dump_bridge_status_to_file(time_t now) -{ - char *status = networkstatus_getinfo_by_purpose("bridge", now); - const or_options_t *options = get_options(); - char *fname = NULL; - char *thresholds = NULL, *thresholds_and_status = NULL; - routerlist_t *rl = router_get_routerlist(); - dirserv_compute_bridge_flag_thresholds(rl); - thresholds = dirserv_get_flag_thresholds_line(); - tor_asprintf(&thresholds_and_status, "flag-thresholds %s\n%s", - thresholds, status); - tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges", - options->DataDirectory); - write_str_to_file(fname,thresholds_and_status,0); - tor_free(thresholds); - tor_free(thresholds_and_status); - tor_free(fname); - tor_free(status); -} - -/* DOCDOC get_net_param_from_list */ -static int32_t -get_net_param_from_list(smartlist_t *net_params, const char *param_name, - int32_t default_val, int32_t min_val, int32_t max_val) -{ - int32_t res = default_val; - size_t name_len = strlen(param_name); - - tor_assert(max_val > min_val); - tor_assert(min_val <= default_val); - tor_assert(max_val >= default_val); - - SMARTLIST_FOREACH_BEGIN(net_params, const char *, p) { - if (!strcmpstart(p, param_name) && p[name_len] == '=') { - int ok=0; - long v = tor_parse_long(p+name_len+1, 10, INT32_MIN, - INT32_MAX, &ok, NULL); - if (ok) { - res = (int32_t) v; - break; - } - } - } SMARTLIST_FOREACH_END(p); - - if (res < min_val) { - log_warn(LD_DIR, "Consensus parameter %s is too small. Got %d, raising to " - "%d.", param_name, res, min_val); - res = min_val; - } else if (res > max_val) { - log_warn(LD_DIR, "Consensus parameter %s is too large. Got %d, capping to " - "%d.", param_name, res, max_val); - res = max_val; - } - - return res; -} - -/** Return the value of a integer parameter from the networkstatus ns - * whose name is param_name. If ns is NULL, try loading the - * latest consensus ourselves. Return default_val if no latest - * consensus, or if it has no parameter called param_name. - * Make sure the value parsed from the consensus is at least - * min_val and at most max_val and raise/cap the parsed value - * if necessary. */ -int32_t -networkstatus_get_param(const networkstatus_t *ns, const char *param_name, - int32_t default_val, int32_t min_val, int32_t max_val) -{ - if (!ns) /* if they pass in null, go find it ourselves */ - ns = networkstatus_get_latest_consensus(); - - if (!ns || !ns->net_params) - return default_val; - - return get_net_param_from_list(ns->net_params, param_name, - default_val, min_val, max_val); -} - -/** - * Retrieve the consensus parameter that governs the - * fixed-point precision of our network balancing 'bandwidth-weights' - * (which are themselves integer consensus values). We divide them - * by this value and ensure they never exceed this value. - */ -int -networkstatus_get_weight_scale_param(networkstatus_t *ns) -{ - return networkstatus_get_param(ns, "bwweightscale", - BW_WEIGHT_SCALE, - BW_MIN_WEIGHT_SCALE, - BW_MAX_WEIGHT_SCALE); -} - -/** Return the value of a integer bw weight parameter from the networkstatus - * ns whose name is weight_name. If ns is NULL, try - * loading the latest consensus ourselves. Return default_val if no - * latest consensus, or if it has no parameter called weight_name. */ -int32_t -networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight_name, - int32_t default_val) -{ - int32_t param; - int max; - if (!ns) /* if they pass in null, go find it ourselves */ - ns = networkstatus_get_latest_consensus(); - - if (!ns || !ns->weight_params) - return default_val; - - max = networkstatus_get_weight_scale_param(ns); - param = get_net_param_from_list(ns->weight_params, weight_name, - default_val, -1, - BW_MAX_WEIGHT_SCALE); - if (param > max) { - log_warn(LD_DIR, "Value of consensus weight %s was too large, capping " - "to %d", weight_name, max); - param = max; - } - return param; -} - -/** Return the name of the consensus flavor flav as used to identify - * the flavor in directory documents. */ -const char * -networkstatus_get_flavor_name(consensus_flavor_t flav) -{ - switch (flav) { - case FLAV_NS: - return "ns"; - case FLAV_MICRODESC: - return "microdesc"; - default: - tor_fragile_assert(); - return "??"; - } -} - -/** Return the consensus_flavor_t value for the flavor called flavname, - * or -1 if the flavor is not recognized. */ -int -networkstatus_parse_flavor_name(const char *flavname) -{ - if (!strcmp(flavname, "ns")) - return FLAV_NS; - else if (!strcmp(flavname, "microdesc")) - return FLAV_MICRODESC; - else - return -1; -} - -/** Return 0 if this routerstatus is obsolete, too new, isn't - * running, or otherwise not a descriptor that we would make any - * use of even if we had it. Else return 1. */ -int -client_would_use_router(const routerstatus_t *rs, time_t now, - const or_options_t *options) -{ - if (!rs->is_flagged_running && !options->FetchUselessDescriptors) { - /* If we had this router descriptor, we wouldn't even bother using it. - * But, if we want to have a complete list, fetch it anyway. */ - return 0; - } - if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime - > now) { - /* Most caches probably don't have this descriptor yet. */ - return 0; - } - if (rs->published_on + OLD_ROUTER_DESC_MAX_AGE < now) { - /* We'd drop it immediately for being too old. */ - return 0; - } - return 1; -} - -/** If question is a string beginning with "ns/" in a format the - * control interface expects for a GETINFO question, set *answer to a - * newly-allocated string containing networkstatus lines for the appropriate - * ORs. Return 0 on success, -1 on unrecognized question format. */ -int -getinfo_helper_networkstatus(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg) -{ - const routerstatus_t *status; - (void) conn; - - if (!current_consensus) { - *answer = tor_strdup(""); - return 0; - } - - if (!strcmp(question, "ns/all")) { - smartlist_t *statuses = smartlist_new(); - SMARTLIST_FOREACH(current_consensus->routerstatus_list, - const routerstatus_t *, rs, - { - smartlist_add(statuses, networkstatus_getinfo_helper_single(rs)); - }); - *answer = smartlist_join_strings(statuses, "", 0, NULL); - SMARTLIST_FOREACH(statuses, char *, cp, tor_free(cp)); - smartlist_free(statuses); - return 0; - } else if (!strcmpstart(question, "ns/id/")) { - char d[DIGEST_LEN]; - const char *q = question + 6; - if (*q == '$') - ++q; - - if (base16_decode(d, DIGEST_LEN, q, strlen(q))) { - *errmsg = "Data not decodeable as hex"; - return -1; - } - status = router_get_consensus_status_by_id(d); - } else if (!strcmpstart(question, "ns/name/")) { - status = router_get_consensus_status_by_nickname(question+8, 0); - } else if (!strcmpstart(question, "ns/purpose/")) { - *answer = networkstatus_getinfo_by_purpose(question+11, time(NULL)); - return *answer ? 0 : -1; - } else { - return 0; - } - - if (status) - *answer = networkstatus_getinfo_helper_single(status); - return 0; -} - -/** Free all storage held locally in this module. */ -void -networkstatus_free_all(void) -{ - int i; - networkstatus_vote_free(current_ns_consensus); - networkstatus_vote_free(current_md_consensus); - current_md_consensus = current_ns_consensus = NULL; - - for (i=0; i < N_CONSENSUS_FLAVORS; ++i) { - consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i]; - if (waiting->consensus) { - networkstatus_vote_free(waiting->consensus); - waiting->consensus = NULL; - } - tor_free(waiting->body); - } - - strmap_free(named_server_map, tor_free_); - strmap_free(unnamed_server_map, NULL); -} - diff --git a/src/tor/networkstatus.h b/src/tor/networkstatus.h deleted file mode 100644 index fed32ea..0000000 --- a/src/tor/networkstatus.h +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file networkstatus.h - * \brief Header file for networkstatus.c. - **/ - -#ifndef TOR_NETWORKSTATUS_H -#define TOR_NETWORKSTATUS_H - -void networkstatus_reset_warnings(void); -void networkstatus_reset_download_failures(void); -int router_reload_consensus_networkstatus(void); -void routerstatus_free(routerstatus_t *rs); -void networkstatus_vote_free(networkstatus_t *ns); -networkstatus_voter_info_t *networkstatus_get_voter_by_id( - networkstatus_t *vote, - const char *identity); -int networkstatus_check_consensus_signature(networkstatus_t *consensus, - int warn); -int networkstatus_check_document_signature(const networkstatus_t *consensus, - document_signature_t *sig, - const authority_cert_t *cert); -char *networkstatus_get_cache_filename(const char *identity_digest); -int compare_digest_to_routerstatus_entry(const void *_key, - const void **_member); -int compare_digest_to_vote_routerstatus_entry(const void *_key, - const void **_member); -const routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns, - const char *digest); -routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns, - const char *digest); -int networkstatus_vote_find_entry_idx(networkstatus_t *ns, - const char *digest, int *found_out); -download_status_t *router_get_dl_status_by_descriptor_digest(const char *d); -const routerstatus_t *router_get_consensus_status_by_id(const char *digest); -routerstatus_t *router_get_mutable_consensus_status_by_id( - const char *digest); -const routerstatus_t *router_get_consensus_status_by_descriptor_digest( - networkstatus_t *consensus, - const char *digest); -routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest( - networkstatus_t *consensus, - const char *digest); -const routerstatus_t *router_get_consensus_status_by_nickname( - const char *nickname, - int warn_if_unnamed); -const char *networkstatus_get_router_digest_by_nickname(const char *nickname); -int networkstatus_nickname_is_unnamed(const char *nickname); -void networkstatus_consensus_download_failed(int status_code, - const char *flavname); -void update_consensus_networkstatus_fetch_time(time_t now); -int should_delay_dir_fetches(const or_options_t *options); -void update_networkstatus_downloads(time_t now); -void update_certificate_downloads(time_t now); -int consensus_is_waiting_for_certs(void); -int client_would_use_router(const routerstatus_t *rs, time_t now, - const or_options_t *options); -networkstatus_t *networkstatus_get_latest_consensus(void); -networkstatus_t *networkstatus_get_latest_consensus_by_flavor( - consensus_flavor_t f); -networkstatus_t *networkstatus_get_live_consensus(time_t now); -networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now, - int flavor); -#define NSSET_FROM_CACHE 1 -#define NSSET_WAS_WAITING_FOR_CERTS 2 -#define NSSET_DONT_DOWNLOAD_CERTS 4 -#define NSSET_ACCEPT_OBSOLETE 8 -#define NSSET_REQUIRE_FLAVOR 16 -int networkstatus_set_current_consensus(const char *consensus, - const char *flavor, - unsigned flags); -void networkstatus_note_certs_arrived(void); -void routers_update_all_from_networkstatus(time_t now, int dir_version); -void routers_update_status_from_consensus_networkstatus(smartlist_t *routers, - int reset_failures); -void signed_descs_update_status_from_consensus_networkstatus( - smartlist_t *descs); - -char *networkstatus_getinfo_helper_single(const routerstatus_t *rs); -char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now); -void networkstatus_dump_bridge_status_to_file(time_t now); -int32_t networkstatus_get_param(const networkstatus_t *ns, - const char *param_name, - int32_t default_val, int32_t min_val, - int32_t max_val); -int getinfo_helper_networkstatus(control_connection_t *conn, - const char *question, char **answer, - const char **errmsg); -int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight, - int32_t default_val); -const char *networkstatus_get_flavor_name(consensus_flavor_t flav); -int networkstatus_parse_flavor_name(const char *flavname); -void document_signature_free(document_signature_t *sig); -document_signature_t *document_signature_dup(const document_signature_t *sig); -void networkstatus_free_all(void); -int networkstatus_get_weight_scale_param(networkstatus_t *ns); - -#endif - diff --git a/src/tor/nodelist.c b/src/tor/nodelist.c deleted file mode 100644 index 96d8881..0000000 --- a/src/tor/nodelist.c +++ /dev/null @@ -1,1557 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "or.h" -#include "address.h" -#include "config.h" -#include "control.h" -#include "dirserv.h" -#include "geoip.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "policies.h" -#include "rendservice.h" -#include "router.h" -#include "routerlist.h" -#include "routerset.h" - -#include - -static void nodelist_drop_node(node_t *node, int remove_from_ht); -static void node_free(node_t *node); -static void update_router_have_minimum_dir_info(void); -static double get_frac_paths_needed_for_circs(const or_options_t *options, - const networkstatus_t *ns); - -/** A nodelist_t holds a node_t object for every router we're "willing to use - * for something". Specifically, it should hold a node_t for every node that - * is currently in the routerlist, or currently in the consensus we're using. - */ -typedef struct nodelist_t { - /* A list of all the nodes. */ - smartlist_t *nodes; - /* Hash table to map from node ID digest to node. */ - HT_HEAD(nodelist_map, node_t) nodes_by_id; - -} nodelist_t; - -static INLINE unsigned int -node_id_hash(const node_t *node) -{ -#if SIZEOF_INT == 4 - const uint32_t *p = (const uint32_t*)node->identity; - return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; -#elif SIZEOF_INT == 8 - const uint64_t *p = (const uint32_t*)node->identity; - const uint32_t *p32 = (const uint32_t*)node->identity; - return p[0] ^ p[1] ^ p32[4]; -#endif -} - -static INLINE unsigned int -node_id_eq(const node_t *node1, const node_t *node2) -{ - return tor_memeq(node1->identity, node2->identity, DIGEST_LEN); -} - -HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq); -HT_GENERATE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq, - 0.6, malloc, realloc, free); - -/** The global nodelist. */ -static nodelist_t *the_nodelist=NULL; - -/** Create an empty nodelist if we haven't done so already. */ -static void -init_nodelist(void) -{ - if (PREDICT_UNLIKELY(the_nodelist == NULL)) { - the_nodelist = tor_malloc_zero(sizeof(nodelist_t)); - HT_INIT(nodelist_map, &the_nodelist->nodes_by_id); - the_nodelist->nodes = smartlist_new(); - } -} - -/** As node_get_by_id, but returns a non-const pointer */ -node_t * -node_get_mutable_by_id(const char *identity_digest) -{ - node_t search, *node; - if (PREDICT_UNLIKELY(the_nodelist == NULL)) - return NULL; - - memcpy(&search.identity, identity_digest, DIGEST_LEN); - node = HT_FIND(nodelist_map, &the_nodelist->nodes_by_id, &search); - return node; -} - -/** Return the node_t whose identity is identity_digest, or NULL - * if no such node exists. */ -const node_t * -node_get_by_id(const char *identity_digest) -{ - return node_get_mutable_by_id(identity_digest); -} - -/** Internal: return the node_t whose identity_digest is - * identity_digest. If none exists, create a new one, add it to the - * nodelist, and return it. - * - * Requires that the nodelist be initialized. - */ -static node_t * -node_get_or_create(const char *identity_digest) -{ - node_t *node; - - if ((node = node_get_mutable_by_id(identity_digest))) - return node; - - node = tor_malloc_zero(sizeof(node_t)); - memcpy(node->identity, identity_digest, DIGEST_LEN); - HT_INSERT(nodelist_map, &the_nodelist->nodes_by_id, node); - - smartlist_add(the_nodelist->nodes, node); - node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1; - - node->country = -1; - - return node; -} - -/** Called when a node's address changes. */ -static void -node_addrs_changed(node_t *node) -{ - node->last_reachable = node->last_reachable6 = 0; - node->country = -1; -} - -/** Add ri to an appropriate node in the nodelist. If we replace an - * old routerinfo, and ri_old_out is not NULL, set *ri_old_out - * to the previous routerinfo. - */ -node_t * -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(); - 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()) && !had_router) { - const char *discard=NULL; - uint32_t status = dirserv_router_get_status(ri, &discard); - dirserv_set_node_flags_from_authoritative_status(node, status); - } - - return node; -} - -/** Set the appropriate node_t to use md as its microdescriptor. - * - * Called when a new microdesc has arrived and the usable consensus flavor - * is "microdesc". - **/ -node_t * -nodelist_add_microdesc(microdesc_t *md) -{ - networkstatus_t *ns = - networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC); - const routerstatus_t *rs; - node_t *node; - if (ns == NULL) - return NULL; - init_nodelist(); - - /* Microdescriptors don't carry an identity digest, so we need to figure - * it out by looking up the routerstatus. */ - rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest); - if (rs == NULL) - return NULL; - node = node_get_mutable_by_id(rs->identity_digest); - if (node) { - if (node->md) - node->md->held_by_nodes--; - node->md = md; - md->held_by_nodes++; - } - return node; -} - -/** Tell the nodelist that the current usable consensus is ns. - * 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. - */ -void -nodelist_set_consensus(networkstatus_t *ns) -{ - const or_options_t *options = get_options(); - int authdir = authdir_mode_v3(options); - int client = !server_mode(options); - - init_nodelist(); - if (ns->flavor == FLAV_MICRODESC) - (void) get_microdesc_cache(); /* Make sure it exists first. */ - - SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node, - node->rs = NULL); - - SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { - node_t *node = node_get_or_create(rs->identity_digest); - node->rs = rs; - if (ns->flavor == FLAV_MICRODESC) { - if (node->md == NULL || - tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) { - if (node->md) - node->md->held_by_nodes--; - node->md = microdesc_cache_lookup_by_digest256(NULL, - rs->descriptor_digest); - if (node->md) - node->md->held_by_nodes++; - } - } - - node_set_country(node); - - /* If we're not an authdir, believe others. */ - if (!authdir) { - node->is_valid = rs->is_valid; - node->is_running = rs->is_flagged_running; - node->is_fast = rs->is_fast; - node->is_stable = rs->is_stable; - node->is_possible_guard = rs->is_possible_guard; - node->is_exit = rs->is_exit; - 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); - - nodelist_purge(); - - if (! authdir) { - SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { - /* We have no routerstatus for this router. Clear flags so we can skip - * it, maybe.*/ - if (!node->rs) { - tor_assert(node->ri); /* if it had only an md, or nothing, purge - * would have removed it. */ - if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) { - /* Clear all flags. */ - 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 = - node->ipv6_preferred = 0; - } - } - } SMARTLIST_FOREACH_END(node); - } -} - -/** Helper: return true iff a node has a usable amount of information*/ -static INLINE int -node_is_usable(const node_t *node) -{ - return (node->rs) || (node->ri); -} - -/** Tell the nodelist that md is no longer a microdescriptor for the - * node with identity_digest. */ -void -nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md) -{ - node_t *node = node_get_mutable_by_id(identity_digest); - if (node && node->md == md) { - node->md = NULL; - md->held_by_nodes--; - } -} - -/** Tell the nodelist that ri is no longer in the routerlist. */ -void -nodelist_remove_routerinfo(routerinfo_t *ri) -{ - node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); - if (node && node->ri == ri) { - node->ri = NULL; - if (! node_is_usable(node)) { - nodelist_drop_node(node, 1); - node_free(node); - } - } -} - -/** Remove node from the nodelist. (Asserts that it was there to begin - * with.) */ -static void -nodelist_drop_node(node_t *node, int remove_from_ht) -{ - node_t *tmp; - int idx; - if (remove_from_ht) { - tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node); - tor_assert(tmp == node); - } - - idx = node->nodelist_idx; - tor_assert(idx >= 0); - - tor_assert(node == smartlist_get(the_nodelist->nodes, idx)); - smartlist_del(the_nodelist->nodes, idx); - if (idx < smartlist_len(the_nodelist->nodes)) { - tmp = smartlist_get(the_nodelist->nodes, idx); - tmp->nodelist_idx = idx; - } - node->nodelist_idx = -1; -} - -/** Release storage held by node */ -static void -node_free(node_t *node) -{ - if (!node) - return; - if (node->md) - node->md->held_by_nodes--; - tor_assert(node->nodelist_idx == -1); - tor_free(node); -} - -/** Remove all entries from the nodelist that don't have enough info to be - * usable for anything. */ -void -nodelist_purge(void) -{ - node_t **iter; - if (PREDICT_UNLIKELY(the_nodelist == NULL)) - return; - - /* Remove the non-usable nodes. */ - for (iter = HT_START(nodelist_map, &the_nodelist->nodes_by_id); iter; ) { - node_t *node = *iter; - - if (node->md && !node->rs) { - /* An md is only useful if there is an rs. */ - node->md->held_by_nodes--; - node->md = NULL; - } - - if (node_is_usable(node)) { - iter = HT_NEXT(nodelist_map, &the_nodelist->nodes_by_id, iter); - } else { - iter = HT_NEXT_RMV(nodelist_map, &the_nodelist->nodes_by_id, iter); - nodelist_drop_node(node, 0); - node_free(node); - } - } - nodelist_assert_ok(); -} - -/** Release all storage held by the nodelist. */ -void -nodelist_free_all(void) -{ - if (PREDICT_UNLIKELY(the_nodelist == NULL)) - return; - - HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id); - SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { - node->nodelist_idx = -1; - node_free(node); - } SMARTLIST_FOREACH_END(node); - - smartlist_free(the_nodelist->nodes); - - tor_free(the_nodelist); -} - -/** Check that the nodelist is internally consistent, and consistent with - * the directory info it's derived from. - */ -void -nodelist_assert_ok(void) -{ - routerlist_t *rl = router_get_routerlist(); - networkstatus_t *ns = networkstatus_get_latest_consensus(); - digestmap_t *dm; - - if (!the_nodelist) - return; - - dm = digestmap_new(); - - /* every routerinfo in rl->routers should be in the nodelist. */ - if (rl) { - SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { - const node_t *node = node_get_by_id(ri->cache_info.identity_digest); - tor_assert(node && node->ri == ri); - tor_assert(fast_memeq(ri->cache_info.identity_digest, - node->identity, DIGEST_LEN)); - tor_assert(! digestmap_get(dm, node->identity)); - digestmap_set(dm, node->identity, (void*)node); - } SMARTLIST_FOREACH_END(ri); - } - - /* every routerstatus in ns should be in the nodelist */ - if (ns) { - SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { - const node_t *node = node_get_by_id(rs->identity_digest); - tor_assert(node && node->rs == rs); - tor_assert(fast_memeq(rs->identity_digest, node->identity, DIGEST_LEN)); - digestmap_set(dm, node->identity, (void*)node); - if (ns->flavor == FLAV_MICRODESC) { - /* If it's a microdesc consensus, every entry that has a - * microdescriptor should be in the nodelist. - */ - microdesc_t *md = - microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest); - tor_assert(md == node->md); - if (md) - tor_assert(md->held_by_nodes >= 1); - } - } SMARTLIST_FOREACH_END(rs); - } - - /* The nodelist should have no other entries, and its entries should be - * well-formed. */ - SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { - tor_assert(digestmap_get(dm, node->identity) != NULL); - tor_assert(node_sl_idx == node->nodelist_idx); - } SMARTLIST_FOREACH_END(node); - - tor_assert((long)smartlist_len(the_nodelist->nodes) == - (long)HT_SIZE(&the_nodelist->nodes_by_id)); - - digestmap_free(dm, NULL); -} - -/** Return a list of a node_t * for every node we know about. The caller - * MUST NOT modify the list. (You can set and clear flags in the nodes if - * you must, but you must not add or remove nodes.) */ -smartlist_t * -nodelist_get_list(void) -{ - init_nodelist(); - return the_nodelist->nodes; -} - -/** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name, - * or $DIGEST~name, return the node with the matching identity digest and - * nickname (if any). Return NULL if no such node exists, or if hex_id - * is not well-formed. */ -const node_t * -node_get_by_hex_id(const char *hex_id) -{ - char digest_buf[DIGEST_LEN]; - char nn_buf[MAX_NICKNAME_LEN+1]; - char nn_char='\0'; - - if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) { - const node_t *node = node_get_by_id(digest_buf); - if (!node) - return NULL; - if (nn_char) { - const char *real_name = node_get_nickname(node); - if (!real_name || strcasecmp(real_name, nn_buf)) - return NULL; - if (nn_char == '=') { - const char *named_id = - networkstatus_get_router_digest_by_nickname(nn_buf); - if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN)) - return NULL; - } - } - return node; - } - - return NULL; -} - -/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return - * the corresponding node_t, or NULL if none exists. Warn the user if - * warn_if_unnamed is set, and they have specified a router by - * nickname, but the Named flag isn't set for that router. */ -const node_t * -node_get_by_nickname(const char *nickname, int warn_if_unnamed) -{ - const node_t *node; - if (!the_nodelist) - return NULL; - - /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */ - if ((node = node_get_by_hex_id(nickname)) != NULL) - return node; - - if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) - return NULL; - - /* Okay, so if we get here, the nickname is just a nickname. Is there - * a binding for it in the consensus? */ - { - const char *named_id = - networkstatus_get_router_digest_by_nickname(nickname); - if (named_id) - return node_get_by_id(named_id); - } - - /* Is it marked as owned-by-someone-else? */ - if (networkstatus_nickname_is_unnamed(nickname)) { - log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some " - "router that holds it, but not one listed in the current " - "consensus.", escaped(nickname)); - return NULL; - } - - /* Okay, so the name is not canonical for anybody. */ - { - smartlist_t *matches = smartlist_new(); - const node_t *choice = NULL; - - SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { - if (!strcasecmp(node_get_nickname(node), nickname)) - smartlist_add(matches, node); - } SMARTLIST_FOREACH_END(node); - - if (smartlist_len(matches)>1 && warn_if_unnamed) { - int any_unwarned = 0; - SMARTLIST_FOREACH_BEGIN(matches, node_t *, node) { - if (!node->name_lookup_warned) { - node->name_lookup_warned = 1; - any_unwarned = 1; - } - } SMARTLIST_FOREACH_END(node); - - if (any_unwarned) { - log_warn(LD_CONFIG, "There are multiple matches for the name %s, " - "but none is listed as Named in the directory consensus. " - "Choosing one arbitrarily.", nickname); - } - } else if (smartlist_len(matches)>1 && warn_if_unnamed) { - char fp[HEX_DIGEST_LEN+1]; - node_t *node = smartlist_get(matches, 0); - if (node->name_lookup_warned) { - base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN); - log_warn(LD_CONFIG, - "You specified a server \"%s\" by name, but the directory " - "authorities do not have any key registered for this " - "nickname -- so it could be used by any server, not just " - "the one you meant. " - "To make sure you get the same server in the future, refer " - "to it by key, as \"$%s\".", nickname, fp); - node->name_lookup_warned = 1; - } - } - - if (smartlist_len(matches)) - choice = smartlist_get(matches, 0); - - smartlist_free(matches); - return choice; - } -} - -/** Return the nickname of node, or NULL if we can't find one. */ -const char * -node_get_nickname(const node_t *node) -{ - tor_assert(node); - if (node->rs) - return node->rs->nickname; - else if (node->ri) - return node->ri->nickname; - else - return NULL; -} - -/** Return true iff the nickname of node is canonical, based on the - * latest consensus. */ -int -node_is_named(const node_t *node) -{ - const char *named_id; - const char *nickname = node_get_nickname(node); - if (!nickname) - return 0; - named_id = networkstatus_get_router_digest_by_nickname(nickname); - if (!named_id) - return 0; - return tor_memeq(named_id, node->identity, DIGEST_LEN); -} - -/** Return true iff node appears to be a directory authority or - * directory cache */ -int -node_is_dir(const node_t *node) -{ - if (node->rs) - return node->rs->dir_port != 0; - else if (node->ri) - return node->ri->dir_port != 0; - else - return 0; -} - -/** Return true iff node has either kind of usable descriptor -- that - * is, a routerdescriptor or a microdescriptor. */ -int -node_has_descriptor(const node_t *node) -{ - return (node->ri || - (node->rs && node->md)); -} - -/** Return the router_purpose of node. */ -int -node_get_purpose(const node_t *node) -{ - if (node->ri) - return node->ri->purpose; - else - return ROUTER_PURPOSE_GENERAL; -} - -/** Compute the verbose ("extended") nickname of node and store it - * into the MAX_VERBOSE_NICKNAME_LEN+1 character buffer at - * verbose_name_out */ -void -node_get_verbose_nickname(const node_t *node, - char *verbose_name_out) -{ - const char *nickname = node_get_nickname(node); - int is_named = node_is_named(node); - verbose_name_out[0] = '$'; - base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity, - DIGEST_LEN); - if (!nickname) - return; - verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~'; - strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1); -} - -/** Compute the verbose ("extended") nickname of node with - * given id_digest and store it into the MAX_VERBOSE_NICKNAME_LEN+1 - * character buffer at verbose_name_out - * - * If node_get_by_id() returns NULL, base 16 encoding of - * id_digest is returned instead. */ -void -node_get_verbose_nickname_by_id(const char *id_digest, - char *verbose_name_out) -{ - const node_t *node = node_get_by_id(id_digest); - if (!node) { - verbose_name_out[0] = '$'; - base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, id_digest, DIGEST_LEN); - } else { - node_get_verbose_nickname(node, verbose_name_out); - } -} - -/** Return true iff it seems that node allows circuits to exit - * through it directlry from the client. */ -int -node_allows_single_hop_exits(const node_t *node) -{ - if (node && node->ri) - return node->ri->allow_single_hop_exits; - else - return 0; -} - -/** Return true iff it seems that node has an exit policy that doesn't - * actually permit anything to exit, or we don't know its exit policy */ -int -node_exit_policy_rejects_all(const node_t *node) -{ - if (node->rejects_all) - return 1; - - if (node->ri) - return node->ri->policy_is_reject_star; - else if (node->md) - return node->md->exit_policy == NULL || - short_policy_is_reject_star(node->md->exit_policy); - else - return 1; -} - -/** Return true iff the exit policy for node is such that we can treat - * rejecting an address of type family unexpectedly as a sign of that - * node's failure. */ -int -node_exit_policy_is_exact(const node_t *node, sa_family_t family) -{ - if (family == AF_UNSPEC) { - return 1; /* Rejecting an address but not telling us what address - * is a bad sign. */ - } else if (family == AF_INET) { - return node->ri != NULL; - } else if (family == AF_INET6) { - return 0; - } - tor_fragile_assert(); - return 1; -} - -/** Return list of tor_addr_port_t with all OR ports (in the sense IP - * addr + TCP port) for node. Caller must free all elements - * using tor_free() and free the list using smartlist_free(). - * - * XXX this is potentially a memory fragmentation hog -- if on - * critical path consider the option of having the caller allocate the - * memory - */ -smartlist_t * -node_get_all_orports(const node_t *node) -{ - smartlist_t *sl = smartlist_new(); - - if (node->ri != NULL) { - if (node->ri->addr != 0) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_from_ipv4h(&ap->addr, node->ri->addr); - ap->port = node->ri->or_port; - smartlist_add(sl, ap); - } - if (!tor_addr_is_null(&node->ri->ipv6_addr)) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_copy(&ap->addr, &node->ri->ipv6_addr); - ap->port = node->ri->or_port; - smartlist_add(sl, ap); - } - } else if (node->rs != NULL) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_from_ipv4h(&ap->addr, node->rs->addr); - ap->port = node->rs->or_port; - smartlist_add(sl, ap); - } - - return sl; -} - -/** Wrapper around node_get_prim_orport for backward - compatibility. */ -void -node_get_addr(const node_t *node, tor_addr_t *addr_out) -{ - tor_addr_port_t ap; - node_get_prim_orport(node, &ap); - tor_addr_copy(addr_out, &ap.addr); -} - -/** Return the host-order IPv4 address for node, or 0 if it doesn't - * seem to have one. */ -uint32_t -node_get_prim_addr_ipv4h(const node_t *node) -{ - if (node->ri) { - return node->ri->addr; - } else if (node->rs) { - return node->rs->addr; - } - return 0; -} - -/** Copy a string representation of an IP address for node into - * the len-byte buffer at buf. */ -void -node_get_address_string(const node_t *node, char *buf, size_t len) -{ - if (node->ri) { - strlcpy(buf, node->ri->address, len); - } else if (node->rs) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); - tor_addr_to_str(buf, &addr, len, 0); - } else { - buf[0] = '\0'; - } -} - -/** Return node's declared uptime, or -1 if it doesn't seem to have - * one. */ -long -node_get_declared_uptime(const node_t *node) -{ - if (node->ri) - return node->ri->uptime; - else - return -1; -} - -/** Return node's platform string, or NULL if we don't know it. */ -const char * -node_get_platform(const node_t *node) -{ - /* If we wanted, we could record the version in the routerstatus_t, since - * the consensus lists it. We don't, though, so this function just won't - * work with microdescriptors. */ - if (node->ri) - return node->ri->platform; - else - return NULL; -} - -/** Return node's time of publication, or 0 if we don't have one. */ -time_t -node_get_published_on(const node_t *node) -{ - if (node->ri) - return node->ri->cache_info.published_on; - else - return 0; -} - -/** Return true iff node is one representing this router. */ -int -node_is_me(const node_t *node) -{ - return router_digest_is_me(node->identity); -} - -/** Return node declared family (as a list of names), or NULL if - * the node didn't declare a family. */ -const smartlist_t * -node_get_declared_family(const node_t *node) -{ - if (node->ri && node->ri->declared_family) - return node->ri->declared_family; - else if (node->md && node->md->family) - return node->md->family; - else - return NULL; -} - -/** Return 1 if we prefer the IPv6 address and OR TCP port of - * node, 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 - * node into *ap_out. 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 - * node into *ap_out. */ -void -node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) -{ - const or_options_t *options = get_options(); - tor_assert(ap_out); - - /* Cheap implementation of config option ClientUseIPv6 -- simply - don't prefer IPv6 when ClientUseIPv6 is not set and we're not a - client running with bridges. 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 ((options->ClientUseIPv6 || options->UseBridges) && - 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 - * node into *ap_out. */ -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; - } -} - -/** Return true iff node has a curve25519 onion key. */ -int -node_has_curve25519_onion_key(const node_t *node) -{ - if (node->ri) - return node->ri->onion_curve25519_pkey != NULL; - else if (node->md) - return node->md->onion_curve25519_pkey != NULL; - else - return 0; -} - -/** Refresh the country code of ri. This function MUST be called on - * each router when the GeoIP database is reloaded, and on all new routers. */ -void -node_set_country(node_t *node) -{ - tor_addr_t addr = TOR_ADDR_NULL; - - /* XXXXipv6 */ - if (node->rs) - tor_addr_from_ipv4h(&addr, node->rs->addr); - else if (node->ri) - tor_addr_from_ipv4h(&addr, node->ri->addr); - - node->country = geoip_get_country_by_addr(&addr); -} - -/** Set the country code of all routers in the routerlist. */ -void -nodelist_refresh_countries(void) -{ - smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, node_t *, node, - node_set_country(node)); -} - -/** Return true iff router1 and router2 have similar enough network addresses - * that we should treat them as being in the same family */ -static INLINE int -addrs_in_same_network_family(const tor_addr_t *a1, - const tor_addr_t *a2) -{ - return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC); -} - -/** Return true if node's nickname matches nickname - * (case-insensitive), or if node's identity key digest - * matches a hexadecimal value stored in nickname. Return - * false otherwise. */ -static int -node_nickname_matches(const node_t *node, const char *nickname) -{ - const char *n = node_get_nickname(node); - if (n && nickname[0]!='$' && !strcasecmp(n, nickname)) - return 1; - return hex_digest_nickname_matches(nickname, - node->identity, - n, - node_is_named(node)); -} - -/** Return true iff node is named by some nickname in lst. */ -static INLINE int -node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node) -{ - if (!lst) return 0; - SMARTLIST_FOREACH(lst, const char *, name, { - if (node_nickname_matches(node, name)) - return 1; - }); - return 0; -} - -/** Return true iff r1 and r2 are in the same family, but not the same - * router. */ -int -nodes_in_same_family(const node_t *node1, const node_t *node2) -{ - const or_options_t *options = get_options(); - - /* Are they in the same family because of their addresses? */ - if (options->EnforceDistinctSubnets) { - tor_addr_t a1, a2; - node_get_addr(node1, &a1); - node_get_addr(node2, &a2); - if (addrs_in_same_network_family(&a1, &a2)) - return 1; - } - - /* Are they in the same family because the agree they are? */ - { - const smartlist_t *f1, *f2; - f1 = node_get_declared_family(node1); - f2 = node_get_declared_family(node2); - if (f1 && f2 && - node_in_nickname_smartlist(f1, node2) && - node_in_nickname_smartlist(f2, node1)) - return 1; - } - - /* Are they in the same option because the user says they are? */ - if (options->NodeFamilySets) { - SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, { - if (routerset_contains_node(rs, node1) && - routerset_contains_node(rs, node2)) - return 1; - }); - } - - return 0; -} - -/** - * Add all the family of node, including node itself, to - * the smartlist sl. - * - * This is used to make sure we don't pick siblings in a single path, or - * pick more than one relay from a family for our entry guard list. - * Note that a node may be added to sl more than once if it is - * part of node's family for more than one reason. - */ -void -nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) -{ - const smartlist_t *all_nodes = nodelist_get_list(); - const smartlist_t *declared_family; - const or_options_t *options = get_options(); - - tor_assert(node); - - declared_family = node_get_declared_family(node); - - /* Let's make sure that we have the node itself, if it's a real node. */ - { - const node_t *real_node = node_get_by_id(node->identity); - if (real_node) - smartlist_add(sl, (node_t*)real_node); - } - - /* First, add any nodes with similar network addresses. */ - if (options->EnforceDistinctSubnets) { - tor_addr_t node_addr; - node_get_addr(node, &node_addr); - - SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) { - tor_addr_t a; - node_get_addr(node2, &a); - if (addrs_in_same_network_family(&a, &node_addr)) - smartlist_add(sl, (void*)node2); - } SMARTLIST_FOREACH_END(node2); - } - - /* Now, add all nodes in the declared_family of this node, if they - * also declare this node to be in their family. */ - if (declared_family) { - /* Add every r such that router declares familyness with node, and node - * declares familyhood with router. */ - SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) { - const node_t *node2; - const smartlist_t *family2; - if (!(node2 = node_get_by_nickname(name, 0))) - continue; - if (!(family2 = node_get_declared_family(node2))) - continue; - SMARTLIST_FOREACH_BEGIN(family2, const char *, name2) { - if (node_nickname_matches(node, name2)) { - smartlist_add(sl, (void*)node2); - break; - } - } SMARTLIST_FOREACH_END(name2); - } SMARTLIST_FOREACH_END(name); - } - - /* If the user declared any families locally, honor those too. */ - if (options->NodeFamilySets) { - SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, { - if (routerset_contains_node(rs, node)) { - routerset_get_all_nodes(sl, rs, NULL, 0); - } - }); - } -} - -/** Find a router that's up, that has this IP address, and - * that allows exit to this address:port, or return NULL if there - * isn't a good one. - * Don't exit enclave to excluded relays -- it wouldn't actually - * hurt anything, but this way there are fewer confused users. - */ -const node_t * -router_find_exact_exit_enclave(const char *address, uint16_t port) -{/*XXXX MOVE*/ - uint32_t addr; - struct in_addr in; - tor_addr_t a; - const or_options_t *options = get_options(); - - if (!tor_inet_aton(address, &in)) - return NULL; /* it's not an IP already */ - addr = ntohl(in.s_addr); - - tor_addr_from_ipv4h(&a, addr); - - SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, { - if (node_get_addr_ipv4h(node) == addr && - node->is_running && - compare_tor_addr_to_node_policy(&a, port, node) == - ADDR_POLICY_ACCEPTED && - !routerset_contains_node(options->ExcludeExitNodesUnion_, node)) - return node; - }); - return NULL; -} - -/** Return 1 if router is not suitable for these parameters, else 0. - * If need_uptime is non-zero, we require a minimum uptime. - * If need_capacity is non-zero, we require a minimum advertised - * bandwidth. - * If need_guard, we require that the router is a possible entry guard. - */ -int -node_is_unreliable(const node_t *node, int need_uptime, - int need_capacity, int need_guard) -{ - if (need_uptime && !node->is_stable) - return 1; - if (need_capacity && !node->is_fast) - return 1; - if (need_guard && !node->is_possible_guard) - return 1; - return 0; -} - -/** Return 1 if all running sufficiently-stable routers we can use will reject - * addr:port. Return 0 if any might accept it. */ -int -router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port, - int need_uptime) -{ - addr_policy_result_t r; - - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { - if (node->is_running && - !node_is_unreliable(node, need_uptime, 0, 0)) { - - r = compare_tor_addr_to_node_policy(addr, port, node); - - if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED) - return 0; /* this one could be ok. good enough. */ - } - } SMARTLIST_FOREACH_END(node); - return 1; /* all will reject. */ -} - -/** Mark the router with ID digest as running or non-running - * in our routerlist. */ -void -router_set_status(const char *digest, int up) -{ - node_t *node; - tor_assert(digest); - - SMARTLIST_FOREACH(router_get_fallback_dir_servers(), - dir_server_t *, d, - if (tor_memeq(d->digest, digest, DIGEST_LEN)) - d->is_running = up); - - SMARTLIST_FOREACH(router_get_trusted_dir_servers(), - dir_server_t *, d, - if (tor_memeq(d->digest, digest, DIGEST_LEN)) - d->is_running = up); - - node = node_get_mutable_by_id(digest); - if (node) { -#if 0 - log_debug(LD_DIR,"Marking router %s as %s.", - node_describe(node), up ? "up" : "down"); -#endif - if (!up && node_is_me(node) && !net_is_disabled()) - log_warn(LD_NET, "We just marked ourself as down. Are your external " - "addresses reachable?"); - node->is_running = up; - } - - router_dir_info_changed(); -} - -/** True iff, the last time we checked whether we had enough directory info - * to build circuits, the answer was "yes". */ -static int have_min_dir_info = 0; -/** True iff enough has changed since the last time we checked whether we had - * enough directory info to build circuits that our old answer can no longer - * be trusted. */ -static int need_to_update_have_min_dir_info = 1; -/** String describing what we're missing before we have enough directory - * info. */ -static char dir_info_status[256] = ""; - -/** Return true iff we have enough networkstatus and router information to - * start building circuits. Right now, this means "more than half the - * networkstatus documents, and at least 1/4 of expected routers." */ -//XXX should consider whether we have enough exiting nodes here. -int -router_have_minimum_dir_info(void) -{ - if (PREDICT_UNLIKELY(need_to_update_have_min_dir_info)) { - update_router_have_minimum_dir_info(); - need_to_update_have_min_dir_info = 0; - } - return have_min_dir_info; -} - -/** Called when our internal view of the directory has changed. This can be - * when the authorities change, networkstatuses change, the list of routerdescs - * changes, or number of running routers changes. - */ -void -router_dir_info_changed(void) -{ - need_to_update_have_min_dir_info = 1; - rend_hsdir_routers_changed(); -} - -/** Return a string describing what we're missing before we have enough - * directory info. */ -const char * -get_dir_info_status_string(void) -{ - return dir_info_status; -} - -/** Iterate over the servers listed in consensus, and count how many of - * them seem like ones we'd use, and how many of those we have - * descriptors for. Store the former in *num_usable and the latter in - * *num_present. If in_set is non-NULL, only consider those - * routers in in_set. If exit_only is true, only consider nodes - * with the Exit flag. If *descs_out is present, add a node_t for each - * usable descriptor to it. - */ -static void -count_usable_descriptors(int *num_present, int *num_usable, - smartlist_t *descs_out, - const networkstatus_t *consensus, - const or_options_t *options, time_t now, - routerset_t *in_set, int exit_only) -{ - const int md = (consensus->flavor == FLAV_MICRODESC); - *num_present = 0, *num_usable=0; - - SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs) - { - const node_t *node = node_get_by_id(rs->identity_digest); - if (!node) - continue; /* This would be a bug: every entry in the consensus is - * supposed to have a node. */ - if (exit_only && ! rs->is_exit) - continue; - if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1)) - continue; - if (client_would_use_router(rs, now, options)) { - const char * const digest = rs->descriptor_digest; - int present; - ++*num_usable; /* the consensus says we want it. */ - if (md) - present = NULL != microdesc_cache_lookup_by_digest256(NULL, digest); - else - present = NULL != router_get_by_descriptor_digest(digest); - if (present) { - /* we have the descriptor listed in the consensus. */ - ++*num_present; - } - if (descs_out) - smartlist_add(descs_out, (node_t*)node); - } - } - SMARTLIST_FOREACH_END(rs); - - log_debug(LD_DIR, "%d usable, %d present (%s%s).", - *num_usable, *num_present, - md ? "microdesc" : "desc", exit_only ? " exits" : "s"); -} - -/** Return an estimate of which fraction of usable paths through the Tor - * network we have available for use. */ -static double -compute_frac_paths_available(const networkstatus_t *consensus, - const or_options_t *options, time_t now, - int *num_present_out, int *num_usable_out, - char **status_out) -{ - smartlist_t *guards = smartlist_new(); - smartlist_t *mid = smartlist_new(); - smartlist_t *exits = smartlist_new(); - smartlist_t *myexits= smartlist_new(); - smartlist_t *myexits_unflagged = smartlist_new(); - double f_guard, f_mid, f_exit, f_myexit, f_myexit_unflagged; - int np, nu; /* Ignored */ - const int authdir = authdir_mode_v3(options); - - count_usable_descriptors(num_present_out, num_usable_out, - mid, consensus, options, now, NULL, 0); - if (options->EntryNodes) { - count_usable_descriptors(&np, &nu, guards, consensus, options, now, - options->EntryNodes, 0); - } else { - SMARTLIST_FOREACH(mid, const node_t *, node, { - if (authdir) { - if (node->rs && node->rs->is_possible_guard) - smartlist_add(guards, (node_t*)node); - } else { - if (node->is_possible_guard) - smartlist_add(guards, (node_t*)node); - } - }); - } - - /* All nodes with exit flag */ - count_usable_descriptors(&np, &nu, exits, consensus, options, now, - NULL, 1); - /* All nodes with exit flag in ExitNodes option */ - count_usable_descriptors(&np, &nu, myexits, consensus, options, now, - options->ExitNodes, 1); - /* Now compute the nodes in the ExitNodes option where which we don't know - * what their exit policy is, or we know it permits something. */ - count_usable_descriptors(&np, &nu, myexits_unflagged, - consensus, options, now, - options->ExitNodes, 0); - SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) { - if (node_has_descriptor(node) && node_exit_policy_rejects_all(node)) - SMARTLIST_DEL_CURRENT(myexits_unflagged, node); - } SMARTLIST_FOREACH_END(node); - - f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD); - f_mid = frac_nodes_with_descriptors(mid, WEIGHT_FOR_MID); - f_exit = frac_nodes_with_descriptors(exits, WEIGHT_FOR_EXIT); - f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT); - f_myexit_unflagged= - frac_nodes_with_descriptors(myexits_unflagged,WEIGHT_FOR_EXIT); - - /* If our ExitNodes list has eliminated every possible Exit node, and there - * were some possible Exit nodes, then instead consider nodes that permit - * exiting to some ports. */ - if (smartlist_len(myexits) == 0 && - smartlist_len(myexits_unflagged)) { - f_myexit = f_myexit_unflagged; - } - - smartlist_free(guards); - smartlist_free(mid); - smartlist_free(exits); - smartlist_free(myexits); - smartlist_free(myexits_unflagged); - - /* This is a tricky point here: we don't want to make it easy for a - * directory to trickle exits to us until it learns which exits we have - * configured, so require that we have a threshold both of total exits - * and usable exits. */ - if (f_myexit < f_exit) - f_exit = f_myexit; - - if (status_out) - tor_asprintf(status_out, - "%d%% of guards bw, " - "%d%% of midpoint bw, and " - "%d%% of exit bw", - (int)(f_guard*100), - (int)(f_mid*100), - (int)(f_exit*100)); - - return f_guard * f_mid * f_exit; -} - -/** We just fetched a new set of descriptors. Compute how far through - * the "loading descriptors" bootstrapping phase we are, so we can inform - * the controller of our progress. */ -int -count_loading_descriptors_progress(void) -{ - int num_present = 0, num_usable=0; - time_t now = time(NULL); - const or_options_t *options = get_options(); - const networkstatus_t *consensus = - networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor()); - double paths, fraction; - - if (!consensus) - return 0; /* can't count descriptors if we have no list of them */ - - paths = compute_frac_paths_available(consensus, options, now, - &num_present, &num_usable, - NULL); - - fraction = paths / get_frac_paths_needed_for_circs(options,consensus); - if (fraction > 1.0) - return 0; /* it's not the number of descriptors holding us back */ - return BOOTSTRAP_STATUS_LOADING_DESCRIPTORS + (int) - (fraction*(BOOTSTRAP_STATUS_CONN_OR-1 - - BOOTSTRAP_STATUS_LOADING_DESCRIPTORS)); -} - -/** Return the fraction of paths needed before we're willing to build - * circuits, as configured in options, or in the consensus ns. */ -static double -get_frac_paths_needed_for_circs(const or_options_t *options, - const networkstatus_t *ns) -{ -#define DFLT_PCT_USABLE_NEEDED 60 - if (options->PathsNeededToBuildCircuits >= 0.0) { - return options->PathsNeededToBuildCircuits; - } else { - return networkstatus_get_param(ns, "min_paths_for_circs_pct", - DFLT_PCT_USABLE_NEEDED, - 25, 95)/100.0; - } -} - -/** Change the value of have_min_dir_info, setting it true iff we have enough - * network and router information to build circuits. Clear the value of - * need_to_update_have_min_dir_info. */ -static void -update_router_have_minimum_dir_info(void) -{ - time_t now = time(NULL); - int res; - const or_options_t *options = get_options(); - const networkstatus_t *consensus = - networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor()); - int using_md; - - if (!consensus) { - if (!networkstatus_get_latest_consensus()) - strlcpy(dir_info_status, "We have no usable consensus.", - sizeof(dir_info_status)); - else - strlcpy(dir_info_status, "We have no recent usable consensus.", - sizeof(dir_info_status)); - res = 0; - goto done; - } - - if (should_delay_dir_fetches(get_options())) { - log_notice(LD_DIR, "no known bridge descriptors running yet; stalling"); - strlcpy(dir_info_status, "No live bridge descriptors.", - sizeof(dir_info_status)); - res = 0; - goto done; - } - - using_md = consensus->flavor == FLAV_MICRODESC; - - { - char *status = NULL; - int num_present=0, num_usable=0; - double paths = compute_frac_paths_available(consensus, options, now, - &num_present, &num_usable, - &status); - - if (paths < get_frac_paths_needed_for_circs(options,consensus)) { - tor_snprintf(dir_info_status, sizeof(dir_info_status), - "We need more %sdescriptors: we have %d/%d, and " - "can only build %d%% of likely paths. (We have %s.)", - using_md?"micro":"", num_present, num_usable, - (int)(paths*100), status); - /* log_notice(LD_NET, "%s", dir_info_status); */ - tor_free(status); - res = 0; - control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0); - goto done; - } - - tor_free(status); - res = 1; - } - - done: - if (res && !have_min_dir_info) { - log_notice(LD_DIR, - "We now have enough directory information to build circuits."); - control_event_client_status(LOG_NOTICE, "ENOUGH_DIR_INFO"); - control_event_bootstrap(BOOTSTRAP_STATUS_CONN_OR, 0); - } - if (!res && have_min_dir_info) { - int quiet = directory_too_idle_to_fetch_descriptors(options, now); - tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR, - "Our directory information is no longer up-to-date " - "enough to build circuits: %s", dir_info_status); - - /* a) make us log when we next complete a circuit, so we know when Tor - * is back up and usable, and b) disable some activities that Tor - * should only do while circuits are working, like reachability tests - * and fetching bridge descriptors only over circuits. */ - can_complete_circuit = 0; - - control_event_client_status(LOG_NOTICE, "NOT_ENOUGH_DIR_INFO"); - } - have_min_dir_info = res; - need_to_update_have_min_dir_info = 0; -} - diff --git a/src/tor/nodelist.h b/src/tor/nodelist.h deleted file mode 100644 index 565caa7..0000000 --- a/src/tor/nodelist.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \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_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); -node_t *nodelist_add_microdesc(microdesc_t *md); -void nodelist_set_consensus(networkstatus_t *ns); - -void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md); -void nodelist_remove_routerinfo(routerinfo_t *ri); -void nodelist_purge(void); - -void nodelist_free_all(void); -void nodelist_assert_ok(void); - -const node_t *node_get_by_nickname(const char *nickname, int warn_if_unnamed); -void node_get_verbose_nickname(const node_t *node, - char *verbose_name_out); -void node_get_verbose_nickname_by_id(const char *id_digest, - char *verbose_name_out); -int node_is_named(const node_t *node); -int node_is_dir(const node_t *node); -int node_has_descriptor(const node_t *node); -int node_get_purpose(const node_t *node); -#define node_is_bridge(node) \ - (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE) -int node_is_me(const node_t *node); -int node_exit_policy_rejects_all(const node_t *node); -int node_exit_policy_is_exact(const node_t *node, sa_family_t family); -smartlist_t *node_get_all_orports(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); -int node_has_curve25519_onion_key(const node_t *node); - -smartlist_t *nodelist_get_list(void); - -/* Temporary during transition to multiple addresses. */ -void node_get_addr(const node_t *node, tor_addr_t *addr_out); -#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) - -void nodelist_refresh_countries(void); -void node_set_country(node_t *node); -void nodelist_add_node_and_family(smartlist_t *nodes, const node_t *node); -int nodes_in_same_family(const node_t *node1, const node_t *node2); - -const node_t *router_find_exact_exit_enclave(const char *address, - uint16_t port); -int node_is_unreliable(const node_t *router, int need_uptime, - int need_capacity, int need_guard); -int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port, - int need_uptime); -void router_set_status(const char *digest, int up); -int router_have_minimum_dir_info(void); -void router_dir_info_changed(void); -const char *get_dir_info_status_string(void); -int count_loading_descriptors_progress(void); - -#endif - diff --git a/src/tor/ntmain.h b/src/tor/ntmain.h deleted file mode 100644 index c6927f1..0000000 --- a/src/tor/ntmain.h +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file ntmain.h - * \brief Header file for ntmain.c. - **/ - -#ifndef TOR_NTMAIN_H -#define TOR_NTMAIN_H - -//#ifdef _WIN32 -//#if !defined (WINCE) -//#define NT_SERVICE -//#endif -//#endif - -#ifdef NT_SERVICE -int nt_service_parse_options(int argc, char **argv, int *should_exit); -int nt_service_is_stopping(void); -void nt_service_set_state(DWORD state); -#else -#define nt_service_is_stopping() 0 -#endif - -#endif - diff --git a/src/tor/onion.c b/src/tor/onion.c deleted file mode 100644 index 30b983d..0000000 --- a/src/tor/onion.c +++ /dev/null @@ -1,1183 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion.c - * \brief Functions to queue create cells, wrap the various onionskin types, - * and parse and create the CREATE cell and its allies. - **/ - -#include "or.h" -#include "circuitlist.h" -#include "config.h" -#include "cpuworker.h" -#include "networkstatus.h" -#include "onion.h" -#include "onion_fast.h" -#include "onion_ntor.h" -#include "onion_tap.h" -#include "relay.h" -#include "rephist.h" -#include "router.h" - -/** Type for a linked list of circuits that are waiting for a free CPU worker - * to process a waiting onion handshake. */ -typedef struct onion_queue_t { - TOR_TAILQ_ENTRY(onion_queue_t) next; - or_circuit_t *circ; - uint16_t handshake_type; - create_cell_t *onionskin; - time_t when_added; -} onion_queue_t; - -/** 5 seconds on the onion queue til we just send back a destroy */ -#define ONIONQUEUE_WAIT_CUTOFF 5 - -/** Array of queues of circuits waiting for CPU workers. An element is NULL - * if that queue is empty.*/ -TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t) - ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = { - TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */ - TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */ - TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */ -}; - -/** Number of entries of each type currently in each element of ol_list[]. */ -static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1]; - -static int num_ntors_per_tap(void); -static void onion_queue_entry_remove(onion_queue_t *victim); - -/* XXXX024 Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN. - * - * (By which I think I meant, "make sure that no - * X_ONIONSKIN_CHALLENGE/REPLY_LEN is greater than - * MAX_ONIONSKIN_CHALLENGE/REPLY_LEN." Also, make sure that we can pass - * over-large values via EXTEND2/EXTENDED2, for future-compatibility.*/ - -/** Return true iff we have room to queue another onionskin of type - * type. */ -static int -have_room_for_onionskin(uint16_t type) -{ - const or_options_t *options = get_options(); - int num_cpus; - uint64_t tap_usec, ntor_usec; - uint64_t ntor_during_tap_usec, tap_during_ntor_usec; - - /* If we've got fewer than 50 entries, we always have room for one more. */ - if (ol_entries[type] < 50) - return 1; - num_cpus = get_num_cpus(options); - /* Compute how many microseconds we'd expect to need to clear all - * onionskins in various combinations of the queues. */ - - /* How long would it take to process all the TAP cells in the queue? */ - tap_usec = estimated_usec_for_onionskins( - ol_entries[ONION_HANDSHAKE_TYPE_TAP], - ONION_HANDSHAKE_TYPE_TAP) / num_cpus; - - /* How long would it take to process all the NTor cells in the queue? */ - ntor_usec = estimated_usec_for_onionskins( - ol_entries[ONION_HANDSHAKE_TYPE_NTOR], - ONION_HANDSHAKE_TYPE_NTOR) / num_cpus; - - /* How long would it take to process the tap cells that we expect to - * process while draining the ntor queue? */ - tap_during_ntor_usec = estimated_usec_for_onionskins( - MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP], - ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()), - ONION_HANDSHAKE_TYPE_TAP) / num_cpus; - - /* How long would it take to process the ntor cells that we expect to - * process while draining the tap queue? */ - ntor_during_tap_usec = estimated_usec_for_onionskins( - MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR], - ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()), - ONION_HANDSHAKE_TYPE_NTOR) / num_cpus; - - /* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue - * this. */ - if (type == ONION_HANDSHAKE_TYPE_NTOR && - (ntor_usec + tap_during_ntor_usec) / 1000 > - (uint64_t)options->MaxOnionQueueDelay) - return 0; - - if (type == ONION_HANDSHAKE_TYPE_TAP && - (tap_usec + ntor_during_tap_usec) / 1000 > - (uint64_t)options->MaxOnionQueueDelay) - return 0; - -#ifdef CURVE25519_ENABLED - /* If we support the ntor handshake, then don't let TAP handshakes use - * more than 2/3 of the space on the queue. */ - if (type == ONION_HANDSHAKE_TYPE_TAP && - tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3) - return 0; -#else - (void) type; -#endif - - return 1; -} - -/** Add circ to the end of ol_list and return 0, except - * if ol_list is too long, in which case do nothing and return -1. - */ -int -onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) -{ - onion_queue_t *tmp; - time_t now = time(NULL); - - if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { - log_warn(LD_BUG, "Handshake %d out of range! Dropping.", - onionskin->handshake_type); - return -1; - } - - tmp = tor_malloc_zero(sizeof(onion_queue_t)); - tmp->circ = circ; - tmp->handshake_type = onionskin->handshake_type; - tmp->onionskin = onionskin; - tmp->when_added = now; - - if (!have_room_for_onionskin(onionskin->handshake_type)) { -#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60) - static ratelim_t last_warned = - RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL); - char *m; - if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR && - (m = rate_limit_log(&last_warned, approx_time()))) { - log_warn(LD_GENERAL, - "Your computer is too slow to handle this many circuit " - "creation requests! Please consider using the " - "MaxAdvertisedBandwidth config option or choosing a more " - "restricted exit policy.%s",m); - tor_free(m); - } - tor_free(tmp); - return -1; - } - - ++ol_entries[onionskin->handshake_type]; - log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.", - onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", - ol_entries[ONION_HANDSHAKE_TYPE_NTOR], - ol_entries[ONION_HANDSHAKE_TYPE_TAP]); - - circ->onionqueue_entry = tmp; - TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next); - - /* cull elderly requests. */ - while (1) { - onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]); - if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF) - break; - - circ = head->circ; - circ->onionqueue_entry = NULL; - onion_queue_entry_remove(head); - log_info(LD_CIRC, - "Circuit create request is too old; canceling due to overload."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); - } - return 0; -} - -/** Return a fairness parameter, to prefer processing NTOR style - * handshakes but still slowly drain the TAP queue so we don't starve - * it entirely. */ -static int -num_ntors_per_tap(void) -{ -#define DEFAULT_NUM_NTORS_PER_TAP 10 -#define MIN_NUM_NTORS_PER_TAP 1 -#define MAX_NUM_NTORS_PER_TAP 100000 - - return networkstatus_get_param(NULL, "NumNTorsPerTAP", - DEFAULT_NUM_NTORS_PER_TAP, - MIN_NUM_NTORS_PER_TAP, - MAX_NUM_NTORS_PER_TAP); -} - -/** Choose which onion queue we'll pull from next. If one is empty choose - * the other; if they both have elements, load balance across them but - * favoring NTOR. */ -static uint16_t -decide_next_handshake_type(void) -{ - /* The number of times we've chosen ntor lately when both were available. */ - static int recently_chosen_ntors = 0; - - if (!ol_entries[ONION_HANDSHAKE_TYPE_NTOR]) - return ONION_HANDSHAKE_TYPE_TAP; /* no ntors? try tap */ - - if (!ol_entries[ONION_HANDSHAKE_TYPE_TAP]) { - - /* Nick wants us to prioritize new tap requests when there aren't - * any in the queue and we've processed k ntor cells since the last - * tap cell. This strategy is maybe a good idea, since it starves tap - * less in the case where tap is rare, or maybe a poor idea, since it - * makes the new tap cell unfairly jump in front of ntor cells that - * got here first. In any case this edge case will only become relevant - * once tap is rare. We should reevaluate whether we like this decision - * once tap gets more rare. */ - if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR] && - recently_chosen_ntors <= num_ntors_per_tap()) - ++recently_chosen_ntors; - - return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */ - } - - /* They both have something queued. Pick ntor if we haven't done that - * too much lately. */ - if (++recently_chosen_ntors <= num_ntors_per_tap()) { - return ONION_HANDSHAKE_TYPE_NTOR; - } - - /* Else, it's time to let tap have its turn. */ - recently_chosen_ntors = 0; - return ONION_HANDSHAKE_TYPE_TAP; -} - -/** Remove the highest priority item from ol_list[] and return it, or - * return NULL if the lists are empty. - */ -or_circuit_t * -onion_next_task(create_cell_t **onionskin_out) -{ - or_circuit_t *circ; - uint16_t handshake_to_choose = decide_next_handshake_type(); - onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[handshake_to_choose]); - - if (!head) - return NULL; /* no onions pending, we're done */ - - tor_assert(head->circ); - tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE); -// tor_assert(head->circ->p_chan); /* make sure it's still valid */ -/* XXX I only commented out the above line to make the unit tests - * more manageable. That's probably not good long-term. -RD */ - circ = head->circ; - if (head->onionskin) - --ol_entries[head->handshake_type]; - log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.", - head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", - ol_entries[ONION_HANDSHAKE_TYPE_NTOR], - ol_entries[ONION_HANDSHAKE_TYPE_TAP]); - - *onionskin_out = head->onionskin; - head->onionskin = NULL; /* prevent free. */ - circ->onionqueue_entry = NULL; - onion_queue_entry_remove(head); - return circ; -} - -/** Return the number of handshake_type-style create requests pending. - */ -int -onion_num_pending(uint16_t handshake_type) -{ - return ol_entries[handshake_type]; -} - -/** Go through ol_list, find the onion_queue_t element which points to - * circ, remove and free that element. Leave circ itself alone. - */ -void -onion_pending_remove(or_circuit_t *circ) -{ - onion_queue_t *victim; - - if (!circ) - return; - - victim = circ->onionqueue_entry; - if (victim) - onion_queue_entry_remove(victim); -} - -/** Remove a queue entry victim from the queue, unlinking it from - * its circuit and freeing it and any structures it owns.*/ -static void -onion_queue_entry_remove(onion_queue_t *victim) -{ - if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { - log_warn(LD_BUG, "Handshake %d out of range! Dropping.", - victim->handshake_type); - /* XXX leaks */ - return; - } - - TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next); - - if (victim->circ) - victim->circ->onionqueue_entry = NULL; - - if (victim->onionskin) - --ol_entries[victim->handshake_type]; - - tor_free(victim->onionskin); - tor_free(victim); -} - -/** Remove all circuits from the pending list. Called from tor_free_all. */ -void -clear_pending_onions(void) -{ - onion_queue_t *victim; - int i; - for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) { - while ((victim = TOR_TAILQ_FIRST(&ol_list[i]))) { - onion_queue_entry_remove(victim); - } - } - memset(ol_entries, 0, sizeof(ol_entries)); -} - -/* ============================================================ */ - -/** Fill in a server_onion_keys_t object at keys with all of the keys - * and other info we might need to do onion handshakes. (We make a copy of - * our keys for each cpuworker to avoid race conditions with the main thread, - * and to avoid locking) */ -void -setup_server_onion_keys(server_onion_keys_t *keys) -{ - memset(keys, 0, sizeof(server_onion_keys_t)); - memcpy(keys->my_identity, router_get_my_id_digest(), DIGEST_LEN); - dup_onion_keys(&keys->onion_key, &keys->last_onion_key); -#ifdef CURVE25519_ENABLED - keys->curve25519_key_map = construct_ntor_key_map(); - keys->junk_keypair = tor_malloc_zero(sizeof(curve25519_keypair_t)); - curve25519_keypair_generate(keys->junk_keypair, 0); -#endif -} - -/** Release all storage held in keys, but do not free keys - * itself (as it's likely to be stack-allocated.) */ -void -release_server_onion_keys(server_onion_keys_t *keys) -{ - if (! keys) - return; - - crypto_pk_free(keys->onion_key); - crypto_pk_free(keys->last_onion_key); -#ifdef CURVE25519_ENABLED - ntor_key_map_free(keys->curve25519_key_map); - tor_free(keys->junk_keypair); -#endif - memset(keys, 0, sizeof(server_onion_keys_t)); -} - -/** Release whatever storage is held in state, depending on its - * type, and clear its pointer. */ -void -onion_handshake_state_release(onion_handshake_state_t *state) -{ - switch (state->tag) { - case ONION_HANDSHAKE_TYPE_TAP: - crypto_dh_free(state->u.tap); - state->u.tap = NULL; - break; - case ONION_HANDSHAKE_TYPE_FAST: - fast_handshake_state_free(state->u.fast); - state->u.fast = NULL; - break; -#ifdef CURVE25519_ENABLED - case ONION_HANDSHAKE_TYPE_NTOR: - ntor_handshake_state_free(state->u.ntor); - state->u.ntor = NULL; - break; -#endif - default: - log_warn(LD_BUG, "called with unknown handshake state type %d", - (int)state->tag); - tor_fragile_assert(); - } -} - -/** Perform the first step of a circuit-creation handshake of type type - * (one of ONION_HANDSHAKE_TYPE_*): generate the initial "onion skin" in - * onion_skin_out, and store any state information in state_out. - * Return -1 on failure, and the length of the onionskin on acceptance. - */ -int -onion_skin_create(int type, - const extend_info_t *node, - onion_handshake_state_t *state_out, - uint8_t *onion_skin_out) -{ - int r = -1; - - switch (type) { - case ONION_HANDSHAKE_TYPE_TAP: - if (!node->onion_key) - return -1; - - if (onion_skin_TAP_create(node->onion_key, - &state_out->u.tap, - (char*)onion_skin_out) < 0) - return -1; - - r = TAP_ONIONSKIN_CHALLENGE_LEN; - break; - case ONION_HANDSHAKE_TYPE_FAST: - if (fast_onionskin_create(&state_out->u.fast, onion_skin_out) < 0) - return -1; - - r = CREATE_FAST_LEN; - break; - case ONION_HANDSHAKE_TYPE_NTOR: -#ifdef CURVE25519_ENABLED - if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) - return -1; - if (onion_skin_ntor_create((const uint8_t*)node->identity_digest, - &node->curve25519_onion_key, - &state_out->u.ntor, - onion_skin_out) < 0) - return -1; - - r = NTOR_ONIONSKIN_LEN; -#else - return -1; -#endif - break; - default: - log_warn(LD_BUG, "called with unknown handshake state type %d", type); - tor_fragile_assert(); - r = -1; - } - - if (r > 0) - state_out->tag = (uint16_t) type; - - return r; -} - -/** Perform the second (server-side) step of a circuit-creation handshake of - * type type, responding to the client request in onion_skin - * using the keys in keys. On success, write our response into - * reply_out, generate keys_out_len bytes worth of key material - * in keys_out_len, a hidden service nonce to rend_nonce_out, - * and return the length of the reply. On failure, return -1. - */ -int -onion_skin_server_handshake(int type, - const uint8_t *onion_skin, size_t onionskin_len, - const server_onion_keys_t *keys, - uint8_t *reply_out, - uint8_t *keys_out, size_t keys_out_len, - uint8_t *rend_nonce_out) -{ - int r = -1; - - switch (type) { - case ONION_HANDSHAKE_TYPE_TAP: - if (onionskin_len != TAP_ONIONSKIN_CHALLENGE_LEN) - return -1; - if (onion_skin_TAP_server_handshake((const char*)onion_skin, - keys->onion_key, keys->last_onion_key, - (char*)reply_out, - (char*)keys_out, keys_out_len)<0) - return -1; - r = TAP_ONIONSKIN_REPLY_LEN; - memcpy(rend_nonce_out, reply_out+DH_KEY_LEN, DIGEST_LEN); - break; - case ONION_HANDSHAKE_TYPE_FAST: - if (onionskin_len != CREATE_FAST_LEN) - return -1; - if (fast_server_handshake(onion_skin, reply_out, keys_out, keys_out_len)<0) - return -1; - r = CREATED_FAST_LEN; - memcpy(rend_nonce_out, reply_out+DIGEST_LEN, DIGEST_LEN); - break; - case ONION_HANDSHAKE_TYPE_NTOR: -#ifdef CURVE25519_ENABLED - if (onionskin_len < NTOR_ONIONSKIN_LEN) - return -1; - { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; - uint8_t *keys_tmp = tor_malloc(keys_out_len + DIGEST_LEN); - - if (onion_skin_ntor_server_handshake( - onion_skin, keys->curve25519_key_map, - keys->junk_keypair, - keys->my_identity, - reply_out, keys_tmp, keys_tmp_len)<0) { - tor_free(keys_tmp); - return -1; - } - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN); - memwipe(keys_tmp, 0, keys_tmp_len); - tor_free(keys_tmp); - r = NTOR_REPLY_LEN; - } -#else - return -1; -#endif - break; - default: - log_warn(LD_BUG, "called with unknown handshake state type %d", type); - tor_fragile_assert(); - return -1; - } - - return r; -} - -/** Perform the final (client-side) step of a circuit-creation handshake of - * type type, using our state in handshake_state and the - * server's response in reply. On success, generate keys_out_len - * bytes worth of key material in keys_out_len, set - * rend_authenticator_out to the "KH" field that can be used to - * establish introduction points at this hop, and return 0. On failure, - * return -1. */ -int -onion_skin_client_handshake(int type, - const onion_handshake_state_t *handshake_state, - const uint8_t *reply, size_t reply_len, - uint8_t *keys_out, size_t keys_out_len, - uint8_t *rend_authenticator_out) -{ - if (handshake_state->tag != type) - return -1; - - switch (type) { - case ONION_HANDSHAKE_TYPE_TAP: - if (reply_len != TAP_ONIONSKIN_REPLY_LEN) - return -1; - if (onion_skin_TAP_client_handshake(handshake_state->u.tap, - (const char*)reply, - (char *)keys_out, keys_out_len) < 0) - return -1; - - memcpy(rend_authenticator_out, reply+DH_KEY_LEN, DIGEST_LEN); - - return 0; - case ONION_HANDSHAKE_TYPE_FAST: - if (reply_len != CREATED_FAST_LEN) - return -1; - if (fast_client_handshake(handshake_state->u.fast, reply, - keys_out, keys_out_len) < 0) - return -1; - - memcpy(rend_authenticator_out, reply+DIGEST_LEN, DIGEST_LEN); - return 0; -#ifdef CURVE25519_ENABLED - case ONION_HANDSHAKE_TYPE_NTOR: - if (reply_len < NTOR_REPLY_LEN) - return -1; - { - size_t keys_tmp_len = keys_out_len + DIGEST_LEN; - uint8_t *keys_tmp = tor_malloc(keys_tmp_len); - if (onion_skin_ntor_client_handshake(handshake_state->u.ntor, - reply, - keys_tmp, keys_tmp_len) < 0) { - tor_free(keys_tmp); - return -1; - } - memcpy(keys_out, keys_tmp, keys_out_len); - memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN); - memwipe(keys_tmp, 0, keys_tmp_len); - tor_free(keys_tmp); - } - return 0; -#endif - default: - log_warn(LD_BUG, "called with unknown handshake state type %d", type); - tor_fragile_assert(); - return -1; - } -} - -/** Helper: return 0 if cell appears valid, -1 otherwise. If - * unknown_ok is true, allow cells with handshake types we don't - * recognize. */ -static int -check_create_cell(const create_cell_t *cell, int unknown_ok) -{ - switch (cell->cell_type) { - case CELL_CREATE: - if (cell->handshake_type != ONION_HANDSHAKE_TYPE_TAP && - cell->handshake_type != ONION_HANDSHAKE_TYPE_NTOR) - return -1; - break; - case CELL_CREATE_FAST: - if (cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) - return -1; - break; - case CELL_CREATE2: - break; - default: - return -1; - } - - switch (cell->handshake_type) { - case ONION_HANDSHAKE_TYPE_TAP: - if (cell->handshake_len != TAP_ONIONSKIN_CHALLENGE_LEN) - return -1; - break; - case ONION_HANDSHAKE_TYPE_FAST: - if (cell->handshake_len != CREATE_FAST_LEN) - return -1; - break; -#ifdef CURVE25519_ENABLED - case ONION_HANDSHAKE_TYPE_NTOR: - if (cell->handshake_len != NTOR_ONIONSKIN_LEN) - return -1; - break; -#endif - default: - if (! unknown_ok) - return -1; - } - - return 0; -} - -/** Write the various parameters into the create cell. Separate from - * create_cell_parse() to make unit testing easier. - */ -void -create_cell_init(create_cell_t *cell_out, uint8_t cell_type, - uint16_t handshake_type, uint16_t handshake_len, - const uint8_t *onionskin) -{ - memset(cell_out, 0, sizeof(*cell_out)); - - cell_out->cell_type = cell_type; - cell_out->handshake_type = handshake_type; - cell_out->handshake_len = handshake_len; - memcpy(cell_out->onionskin, onionskin, handshake_len); -} - -/** Helper: parse the CREATE2 payload at p, which could be up to - * p_len bytes long, and use it to fill the fields of - * cell_out. Return 0 on success and -1 on failure. - * - * Note that part of the body of an EXTEND2 cell is a CREATE2 payload, so - * this function is also used for parsing those. - */ -static int -parse_create2_payload(create_cell_t *cell_out, const uint8_t *p, size_t p_len) -{ - uint16_t handshake_type, handshake_len; - - if (p_len < 4) - return -1; - - handshake_type = ntohs(get_uint16(p)); - handshake_len = ntohs(get_uint16(p+2)); - - if (handshake_len > CELL_PAYLOAD_SIZE - 4 || handshake_len > p_len - 4) - return -1; - if (handshake_type == ONION_HANDSHAKE_TYPE_FAST) - return -1; - - create_cell_init(cell_out, CELL_CREATE2, handshake_type, handshake_len, - p+4); - return 0; -} - -/** Magic string which, in a CREATE or EXTEND cell, indicates that a seeming - * TAP payload is really an ntor payload. We'd do away with this if every - * relay supported EXTEND2, but we want to be able to extend from A to B with - * ntor even when A doesn't understand EXTEND2 and so can't generate a - * CREATE2 cell. - **/ -#define NTOR_CREATE_MAGIC "ntorNTORntorNTOR" - -/** Parse a CREATE, CREATE_FAST, or CREATE2 cell from cell_in into - * cell_out. Return 0 on success, -1 on failure. (We reject some - * syntactically valid CREATE2 cells that we can't generate or react to.) */ -int -create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in) -{ - switch (cell_in->command) { - case CELL_CREATE: - if (tor_memeq(cell_in->payload, NTOR_CREATE_MAGIC, 16)) { - create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR, - NTOR_ONIONSKIN_LEN, cell_in->payload+16); - } else { - create_cell_init(cell_out, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP, - TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->payload); - } - break; - case CELL_CREATE_FAST: - create_cell_init(cell_out, CELL_CREATE_FAST, ONION_HANDSHAKE_TYPE_FAST, - CREATE_FAST_LEN, cell_in->payload); - break; - case CELL_CREATE2: - if (parse_create2_payload(cell_out, cell_in->payload, - CELL_PAYLOAD_SIZE) < 0) - return -1; - break; - default: - return -1; - } - - return check_create_cell(cell_out, 0); -} - -/** Helper: return 0 if cell appears valid, -1 otherwise. */ -static int -check_created_cell(const created_cell_t *cell) -{ - switch (cell->cell_type) { - case CELL_CREATED: - if (cell->handshake_len != TAP_ONIONSKIN_REPLY_LEN && - cell->handshake_len != NTOR_REPLY_LEN) - return -1; - break; - case CELL_CREATED_FAST: - if (cell->handshake_len != CREATED_FAST_LEN) - return -1; - break; - case CELL_CREATED2: - if (cell->handshake_len > RELAY_PAYLOAD_SIZE-2) - return -1; - break; - } - - return 0; -} - -/** Parse a CREATED, CREATED_FAST, or CREATED2 cell from cell_in into - * cell_out. Return 0 on success, -1 on failure. */ -int -created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in) -{ - memset(cell_out, 0, sizeof(*cell_out)); - - switch (cell_in->command) { - case CELL_CREATED: - cell_out->cell_type = CELL_CREATED; - cell_out->handshake_len = TAP_ONIONSKIN_REPLY_LEN; - memcpy(cell_out->reply, cell_in->payload, TAP_ONIONSKIN_REPLY_LEN); - break; - case CELL_CREATED_FAST: - cell_out->cell_type = CELL_CREATED_FAST; - cell_out->handshake_len = CREATED_FAST_LEN; - memcpy(cell_out->reply, cell_in->payload, CREATED_FAST_LEN); - break; - case CELL_CREATED2: - { - const uint8_t *p = cell_in->payload; - cell_out->cell_type = CELL_CREATED2; - cell_out->handshake_len = ntohs(get_uint16(p)); - if (cell_out->handshake_len > CELL_PAYLOAD_SIZE - 2) - return -1; - memcpy(cell_out->reply, p+2, cell_out->handshake_len); - break; - } - } - - return check_created_cell(cell_out); -} - -/** Helper: return 0 if cell appears valid, -1 otherwise. */ -static int -check_extend_cell(const extend_cell_t *cell) -{ - if (tor_digest_is_zero((const char*)cell->node_id)) - return -1; - /* We don't currently allow EXTEND2 cells without an IPv4 address */ - if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) - return -1; - if (cell->create_cell.cell_type == CELL_CREATE) { - if (cell->cell_type != RELAY_COMMAND_EXTEND) - return -1; - } else if (cell->create_cell.cell_type == CELL_CREATE2) { - if (cell->cell_type != RELAY_COMMAND_EXTEND2 && - cell->cell_type != RELAY_COMMAND_EXTEND) - return -1; - } else { - /* In particular, no CREATE_FAST cells are allowed */ - return -1; - } - if (cell->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_FAST) - return -1; - - return check_create_cell(&cell->create_cell, 1); -} - -/** Protocol constants for specifier types in EXTEND2 - * @{ - */ -#define SPECTYPE_IPV4 0 -#define SPECTYPE_IPV6 1 -#define SPECTYPE_LEGACY_ID 2 -/** @} */ - -/** Parse an EXTEND or EXTEND2 cell (according to command) from the - * payload_length bytes of payload into cell_out. Return - * 0 on success, -1 on failure. */ -int -extend_cell_parse(extend_cell_t *cell_out, const uint8_t command, - const uint8_t *payload, size_t payload_length) -{ - const uint8_t *eop; - - memset(cell_out, 0, sizeof(*cell_out)); - if (payload_length > RELAY_PAYLOAD_SIZE) - return -1; - eop = payload + payload_length; - - switch (command) { - case RELAY_COMMAND_EXTEND: - { - if (payload_length != 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN) - return -1; - - cell_out->cell_type = RELAY_COMMAND_EXTEND; - tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload)); - cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4)); - tor_addr_make_unspec(&cell_out->orport_ipv6.addr); - if (tor_memeq(payload + 6, NTOR_CREATE_MAGIC, 16)) { - cell_out->create_cell.cell_type = CELL_CREATE2; - cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR; - cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN; - memcpy(cell_out->create_cell.onionskin, payload + 22, - NTOR_ONIONSKIN_LEN); - } else { - cell_out->create_cell.cell_type = CELL_CREATE; - cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP; - cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN; - memcpy(cell_out->create_cell.onionskin, payload + 6, - TAP_ONIONSKIN_CHALLENGE_LEN); - } - memcpy(cell_out->node_id, payload + 6 + TAP_ONIONSKIN_CHALLENGE_LEN, - DIGEST_LEN); - break; - } - case RELAY_COMMAND_EXTEND2: - { - uint8_t n_specs, spectype, speclen; - int i; - int found_ipv4 = 0, found_ipv6 = 0, found_id = 0; - tor_addr_make_unspec(&cell_out->orport_ipv4.addr); - tor_addr_make_unspec(&cell_out->orport_ipv6.addr); - - if (payload_length == 0) - return -1; - - cell_out->cell_type = RELAY_COMMAND_EXTEND2; - n_specs = *payload++; - /* Parse the specifiers. We'll only take the first IPv4 and first IPv6 - * address, and the node ID, and ignore everything else */ - for (i = 0; i < n_specs; ++i) { - if (eop - payload < 2) - return -1; - spectype = payload[0]; - speclen = payload[1]; - payload += 2; - if (eop - payload < speclen) - return -1; - switch (spectype) { - case SPECTYPE_IPV4: - if (speclen != 6) - return -1; - if (!found_ipv4) { - tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, - get_uint32(payload)); - cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4)); - found_ipv4 = 1; - } - break; - case SPECTYPE_IPV6: - if (speclen != 18) - return -1; - if (!found_ipv6) { - tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr, - (const char*)payload); - cell_out->orport_ipv6.port = ntohs(get_uint16(payload+16)); - found_ipv6 = 1; - } - break; - case SPECTYPE_LEGACY_ID: - if (speclen != 20) - return -1; - if (found_id) - return -1; - memcpy(cell_out->node_id, payload, 20); - found_id = 1; - break; - } - payload += speclen; - } - if (!found_id || !found_ipv4) - return -1; - if (parse_create2_payload(&cell_out->create_cell,payload,eop-payload)<0) - return -1; - break; - } - default: - return -1; - } - - return check_extend_cell(cell_out); -} - -/** Helper: return 0 if cell appears valid, -1 otherwise. */ -static int -check_extended_cell(const extended_cell_t *cell) -{ - if (cell->created_cell.cell_type == CELL_CREATED) { - if (cell->cell_type != RELAY_COMMAND_EXTENDED) - return -1; - } else if (cell->created_cell.cell_type == CELL_CREATED2) { - if (cell->cell_type != RELAY_COMMAND_EXTENDED2) - return -1; - } else { - return -1; - } - - return check_created_cell(&cell->created_cell); -} - -/** Parse an EXTENDED or EXTENDED2 cell (according to command) from the - * payload_length bytes of payload into cell_out. Return - * 0 on success, -1 on failure. */ -int -extended_cell_parse(extended_cell_t *cell_out, - const uint8_t command, const uint8_t *payload, - size_t payload_len) -{ - memset(cell_out, 0, sizeof(*cell_out)); - if (payload_len > RELAY_PAYLOAD_SIZE) - return -1; - - switch (command) { - case RELAY_COMMAND_EXTENDED: - if (payload_len != TAP_ONIONSKIN_REPLY_LEN) - return -1; - cell_out->cell_type = RELAY_COMMAND_EXTENDED; - cell_out->created_cell.cell_type = CELL_CREATED; - cell_out->created_cell.handshake_len = TAP_ONIONSKIN_REPLY_LEN; - memcpy(cell_out->created_cell.reply, payload, TAP_ONIONSKIN_REPLY_LEN); - break; - case RELAY_COMMAND_EXTENDED2: - { - cell_out->cell_type = RELAY_COMMAND_EXTENDED2; - cell_out->created_cell.cell_type = CELL_CREATED2; - cell_out->created_cell.handshake_len = ntohs(get_uint16(payload)); - if (cell_out->created_cell.handshake_len > RELAY_PAYLOAD_SIZE - 2 || - cell_out->created_cell.handshake_len > payload_len - 2) - return -1; - memcpy(cell_out->created_cell.reply, payload+2, - cell_out->created_cell.handshake_len); - } - break; - default: - return -1; - } - - return check_extended_cell(cell_out); -} - -/** Fill cell_out with a correctly formatted version of the - * CREATE{,_FAST,2} cell in cell_in. Return 0 on success, -1 on - * failure. This is a cell we didn't originate if relayed is true. */ -static int -create_cell_format_impl(cell_t *cell_out, const create_cell_t *cell_in, - int relayed) -{ - uint8_t *p; - size_t space; - if (check_create_cell(cell_in, relayed) < 0) - return -1; - - memset(cell_out->payload, 0, sizeof(cell_out->payload)); - cell_out->command = cell_in->cell_type; - - p = cell_out->payload; - space = sizeof(cell_out->payload); - - switch (cell_in->cell_type) { - case CELL_CREATE: - if (cell_in->handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { - memcpy(p, NTOR_CREATE_MAGIC, 16); - p += 16; - space -= 16; - } - /* Fall through */ - case CELL_CREATE_FAST: - tor_assert(cell_in->handshake_len <= space); - memcpy(p, cell_in->onionskin, cell_in->handshake_len); - break; - case CELL_CREATE2: - tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-4); - set_uint16(cell_out->payload, htons(cell_in->handshake_type)); - set_uint16(cell_out->payload+2, htons(cell_in->handshake_len)); - memcpy(cell_out->payload + 4, cell_in->onionskin, cell_in->handshake_len); - break; - default: - return -1; - } - - return 0; -} - -int -create_cell_format(cell_t *cell_out, const create_cell_t *cell_in) -{ - return create_cell_format_impl(cell_out, cell_in, 0); -} - -int -create_cell_format_relayed(cell_t *cell_out, const create_cell_t *cell_in) -{ - return create_cell_format_impl(cell_out, cell_in, 1); -} - -/** Fill cell_out with a correctly formatted version of the - * CREATED{,_FAST,2} cell in cell_in. Return 0 on success, -1 on - * failure. */ -int -created_cell_format(cell_t *cell_out, const created_cell_t *cell_in) -{ - if (check_created_cell(cell_in) < 0) - return -1; - - memset(cell_out->payload, 0, sizeof(cell_out->payload)); - cell_out->command = cell_in->cell_type; - - switch (cell_in->cell_type) { - case CELL_CREATED: - case CELL_CREATED_FAST: - tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)); - memcpy(cell_out->payload, cell_in->reply, cell_in->handshake_len); - break; - case CELL_CREATED2: - tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-2); - set_uint16(cell_out->payload, htons(cell_in->handshake_len)); - memcpy(cell_out->payload + 2, cell_in->reply, cell_in->handshake_len); - break; - default: - return -1; - } - return 0; -} - -/** Format the EXTEND{,2} cell in cell_in, storing its relay payload in - * payload_out, the number of bytes used in *len_out, and the - * relay command in *command_out. The payload_out must have - * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ -int -extend_cell_format(uint8_t *command_out, uint16_t *len_out, - uint8_t *payload_out, const extend_cell_t *cell_in) -{ - uint8_t *p, *eop; - if (check_extend_cell(cell_in) < 0) - return -1; - - p = payload_out; - eop = payload_out + RELAY_PAYLOAD_SIZE; - - memset(p, 0, RELAY_PAYLOAD_SIZE); - - switch (cell_in->cell_type) { - case RELAY_COMMAND_EXTEND: - { - *command_out = RELAY_COMMAND_EXTEND; - *len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN; - set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); - set_uint16(p+4, ntohs(cell_in->orport_ipv4.port)); - if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { - memcpy(p+6, NTOR_CREATE_MAGIC, 16); - memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN); - } else { - memcpy(p+6, cell_in->create_cell.onionskin, - TAP_ONIONSKIN_CHALLENGE_LEN); - } - memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->node_id, DIGEST_LEN); - } - break; - case RELAY_COMMAND_EXTEND2: - { - uint8_t n = 2; - *command_out = RELAY_COMMAND_EXTEND2; - - *p++ = n; /* 2 identifiers */ - *p++ = SPECTYPE_IPV4; /* First is IPV4. */ - *p++ = 6; /* It's 6 bytes long. */ - set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); - set_uint16(p+4, htons(cell_in->orport_ipv4.port)); - p += 6; - *p++ = SPECTYPE_LEGACY_ID; /* Next is an identity digest. */ - *p++ = 20; /* It's 20 bytes long */ - memcpy(p, cell_in->node_id, DIGEST_LEN); - p += 20; - - /* Now we can send the handshake */ - set_uint16(p, htons(cell_in->create_cell.handshake_type)); - set_uint16(p+2, htons(cell_in->create_cell.handshake_len)); - p += 4; - - if (cell_in->create_cell.handshake_len > eop - p) - return -1; - - memcpy(p, cell_in->create_cell.onionskin, - cell_in->create_cell.handshake_len); - - p += cell_in->create_cell.handshake_len; - *len_out = p - payload_out; - } - break; - default: - return -1; - } - - return 0; -} - -/** Format the EXTENDED{,2} cell in cell_in, storing its relay payload - * in payload_out, the number of bytes used in *len_out, and the - * relay command in *command_out. The payload_out must have - * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ -int -extended_cell_format(uint8_t *command_out, uint16_t *len_out, - uint8_t *payload_out, const extended_cell_t *cell_in) -{ - uint8_t *p; - if (check_extended_cell(cell_in) < 0) - return -1; - - p = payload_out; - memset(p, 0, RELAY_PAYLOAD_SIZE); - - switch (cell_in->cell_type) { - case RELAY_COMMAND_EXTENDED: - { - *command_out = RELAY_COMMAND_EXTENDED; - *len_out = TAP_ONIONSKIN_REPLY_LEN; - memcpy(payload_out, cell_in->created_cell.reply, - TAP_ONIONSKIN_REPLY_LEN); - } - break; - case RELAY_COMMAND_EXTENDED2: - { - *command_out = RELAY_COMMAND_EXTENDED2; - *len_out = 2 + cell_in->created_cell.handshake_len; - set_uint16(payload_out, htons(cell_in->created_cell.handshake_len)); - if (2+cell_in->created_cell.handshake_len > RELAY_PAYLOAD_SIZE) - return -1; - memcpy(payload_out+2, cell_in->created_cell.reply, - cell_in->created_cell.handshake_len); - } - break; - default: - return -1; - } - - return 0; -} - diff --git a/src/tor/onion.h b/src/tor/onion.h deleted file mode 100644 index d62f032..0000000 --- a/src/tor/onion.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion.h - * \brief Header file for onion.c. - **/ - -#ifndef TOR_ONION_H -#define TOR_ONION_H - -struct create_cell_t; -int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin); -or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out); -int onion_num_pending(uint16_t handshake_type); -void onion_pending_remove(or_circuit_t *circ); -void clear_pending_onions(void); - -typedef struct server_onion_keys_t { - uint8_t my_identity[DIGEST_LEN]; - crypto_pk_t *onion_key; - crypto_pk_t *last_onion_key; -#ifdef CURVE25519_ENABLED - di_digest256_map_t *curve25519_key_map; - curve25519_keypair_t *junk_keypair; -#endif -} server_onion_keys_t; - -#define MAX_ONIONSKIN_CHALLENGE_LEN 255 -#define MAX_ONIONSKIN_REPLY_LEN 255 - -void setup_server_onion_keys(server_onion_keys_t *keys); -void release_server_onion_keys(server_onion_keys_t *keys); - -void onion_handshake_state_release(onion_handshake_state_t *state); - -int onion_skin_create(int type, - const extend_info_t *node, - onion_handshake_state_t *state_out, - uint8_t *onion_skin_out); -int onion_skin_server_handshake(int type, - const uint8_t *onion_skin, size_t onionskin_len, - const server_onion_keys_t *keys, - uint8_t *reply_out, - uint8_t *keys_out, size_t key_out_len, - uint8_t *rend_nonce_out); -int onion_skin_client_handshake(int type, - const onion_handshake_state_t *handshake_state, - const uint8_t *reply, size_t reply_len, - uint8_t *keys_out, size_t key_out_len, - uint8_t *rend_authenticator_out); - -/** A parsed CREATE, CREATE_FAST, or CREATE2 cell. */ -typedef struct create_cell_t { - /** The cell command. One of CREATE{,_FAST,2} */ - uint8_t cell_type; - /** One of the ONION_HANDSHAKE_TYPE_* values */ - uint16_t handshake_type; - /** The number of bytes used in onionskin. */ - uint16_t handshake_len; - /** The client-side message for the circuit creation handshake. */ - uint8_t onionskin[CELL_PAYLOAD_SIZE - 4]; -} create_cell_t; - -/** A parsed CREATED, CREATED_FAST, or CREATED2 cell. */ -typedef struct created_cell_t { - /** The cell command. One of CREATED{,_FAST,2} */ - uint8_t cell_type; - /** The number of bytes used in reply. */ - uint16_t handshake_len; - /** The server-side message for the circuit creation handshake. */ - uint8_t reply[CELL_PAYLOAD_SIZE - 2]; -} created_cell_t; - -/** A parsed RELAY_EXTEND or RELAY_EXTEND2 cell */ -typedef struct extend_cell_t { - /** One of RELAY_EXTEND or RELAY_EXTEND2 */ - uint8_t cell_type; - /** An IPv4 address and port for the node we're connecting to. */ - tor_addr_port_t orport_ipv4; - /** An IPv6 address and port for the node we're connecting to. Not currently - * used. */ - tor_addr_port_t orport_ipv6; - /** Identity fingerprint of the node we're conecting to.*/ - uint8_t node_id[DIGEST_LEN]; - /** The "create cell" embedded in this extend cell. Note that unlike the - * create cells we generate ourself, this once can have a handshake type we - * don't recognize. */ - create_cell_t create_cell; -} extend_cell_t; - -/** A parsed RELAY_EXTEND or RELAY_EXTEND2 cell */ -typedef struct extended_cell_t { - /** One of RELAY_EXTENDED or RELAY_EXTENDED2. */ - uint8_t cell_type; - /** The "created cell" embedded in this extended cell. */ - created_cell_t created_cell; -} extended_cell_t; - -void create_cell_init(create_cell_t *cell_out, uint8_t cell_type, - uint16_t handshake_type, uint16_t handshake_len, - const uint8_t *onionskin); -int create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in); -int created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in); -int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command, - const uint8_t *payload_in, size_t payload_len); -int extended_cell_parse(extended_cell_t *cell_out, const uint8_t command, - const uint8_t *payload_in, size_t payload_len); - -int create_cell_format(cell_t *cell_out, const create_cell_t *cell_in); -int create_cell_format_relayed(cell_t *cell_out, const create_cell_t *cell_in); -int created_cell_format(cell_t *cell_out, const created_cell_t *cell_in); -int extend_cell_format(uint8_t *command_out, uint16_t *len_out, - uint8_t *payload_out, const extend_cell_t *cell_in); -int extended_cell_format(uint8_t *command_out, uint16_t *len_out, - uint8_t *payload_out, const extended_cell_t *cell_in); - -#endif - diff --git a/src/tor/onion_fast.c b/src/tor/onion_fast.c deleted file mode 100644 index 8e778db..0000000 --- a/src/tor/onion_fast.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_fast.c - * \brief Functions implement the CREATE_FAST circuit handshake. - **/ - -#include "or.h" -#include "onion_fast.h" - -/** Release all state held in victim. */ -void -fast_handshake_state_free(fast_handshake_state_t *victim) -{ - if (! victim) - return; - memwipe(victim, 0, sizeof(fast_handshake_state_t)); - tor_free(victim); -} - -/** Create the state needed to perform a CREATE_FAST handshake. Return 0 - * on success, -1 on failure. */ -int -fast_onionskin_create(fast_handshake_state_t **handshake_state_out, - uint8_t *handshake_out) -{ - fast_handshake_state_t *s; - *handshake_state_out = s = tor_malloc(sizeof(fast_handshake_state_t)); - if (crypto_rand((char*)s->state, sizeof(s->state)) < 0) { - tor_free(s); - return -1; - } - memcpy(handshake_out, s->state, DIGEST_LEN); - return 0; -} - -/** Implement the server side of the CREATE_FAST abbreviated handshake. The - * client has provided DIGEST_LEN key bytes in key_in ("x"). We - * generate a reply of DIGEST_LEN*2 bytes in key_out, consisting of a - * new random "y", followed by H(x|y) to check for correctness. We set - * key_out_len bytes of key material in key_out. - * Return 0 on success, <0 on failure. - **/ -int -fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */ - uint8_t *handshake_reply_out, /* DIGEST_LEN*2 bytes */ - uint8_t *key_out, - size_t key_out_len) -{ - uint8_t tmp[DIGEST_LEN+DIGEST_LEN]; - uint8_t *out = NULL; - size_t out_len; - int r = -1; - - if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0) - return -1; - - memcpy(tmp, key_in, DIGEST_LEN); - memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); - out_len = key_out_len+DIGEST_LEN; - out = tor_malloc(out_len); - if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) { - goto done; - } - memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN); - memcpy(key_out, out+DIGEST_LEN, key_out_len); - r = 0; - done: - memwipe(tmp, 0, sizeof(tmp)); - memwipe(out, 0, out_len); - tor_free(out); - return r; -} - -/** Implement the second half of the client side of the CREATE_FAST handshake. - * We sent the server handshake_state ("x") already, and the server - * told us handshake_reply_out (y|H(x|y)). Make sure that the hash is - * correct, and generate key material in key_out. Return 0 on success, - * true on failure. - * - * NOTE: The "CREATE_FAST" handshake path is distinguishable from regular - * "onionskin" handshakes, and is not secure if an adversary can see or modify - * the messages. Therefore, it should only be used by clients, and only as - * the first hop of a circuit (since the first hop is already authenticated - * and protected by TLS). - */ -int -fast_client_handshake(const fast_handshake_state_t *handshake_state, - const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/ - uint8_t *key_out, - size_t key_out_len) -{ - uint8_t tmp[DIGEST_LEN+DIGEST_LEN]; - uint8_t *out; - size_t out_len; - int r = -1; - - memcpy(tmp, handshake_state->state, DIGEST_LEN); - memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); - out_len = key_out_len+DIGEST_LEN; - out = tor_malloc(out_len); - if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) { - goto done; - } - if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) { - /* H(K) does *not* match. Something fishy. */ - log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. " - "Bug or attack."); - goto done; - } - memcpy(key_out, out+DIGEST_LEN, key_out_len); - r = 0; - done: - memwipe(tmp, 0, sizeof(tmp)); - memwipe(out, 0, out_len); - tor_free(out); - return r; -} - diff --git a/src/tor/onion_fast.h b/src/tor/onion_fast.h deleted file mode 100644 index 8c07837..0000000 --- a/src/tor/onion_fast.h +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_fast.h - * \brief Header file for onion_fast.c. - **/ - -#ifndef TOR_ONION_FAST_H -#define TOR_ONION_FAST_H - -#define CREATE_FAST_LEN DIGEST_LEN -#define CREATED_FAST_LEN (DIGEST_LEN*2) - -typedef struct fast_handshake_state_t { - uint8_t state[DIGEST_LEN]; -} fast_handshake_state_t; - -void fast_handshake_state_free(fast_handshake_state_t *victim); - -int fast_onionskin_create(fast_handshake_state_t **handshake_state_out, - uint8_t *handshake_out); - -int fast_server_handshake(const uint8_t *message_in, - uint8_t *handshake_reply_out, - uint8_t *key_out, - size_t key_out_len); - -int fast_client_handshake(const fast_handshake_state_t *handshake_state, - const uint8_t *handshake_reply_out, - uint8_t *key_out, - size_t key_out_len); - -#endif - diff --git a/src/tor/onion_main.c b/src/tor/onion_main.c deleted file mode 100644 index 9fe915b..0000000 --- a/src/tor/onion_main.c +++ /dev/null @@ -1,2908 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_main.c - * \brief Toplevel module. Handles signals, multiplexes between - * connections, implements main loop, and drives scheduled events. - **/ - -#define MAIN_PRIVATE -#include "or.h" -#include "addressmap.h" -#include "backtrace.h" -#include "buffers.h" -#include "channel.h" -#include "channeltls.h" -#include "circuitbuild.h" -#include "circuitlist.h" -#include "circuituse.h" -#include "command.h" -#include "config.h" -#include "confparse.h" -#include "connection.h" -#include "connection_edge.h" -#include "connection_or.h" -#include "control.h" -#include "cpuworker.h" -#include "directory.h" -#include "dirserv.h" -#include "dirvote.h" -#include "dns.h" -#include "dnsserv.h" -#include "entrynodes.h" -#include "geoip.h" -#include "hibernate.h" -#include "onion_main.h" -#include "microdesc.h" -#include "networkstatus.h" -#include "nodelist.h" -#include "ntmain.h" -#include "onion.h" -#include "policies.h" -#include "transports.h" -#include "relay.h" -#include "rendclient.h" -#include "rendcommon.h" -#include "rendservice.h" -#include "rephist.h" -#include "router.h" -#include "routerlist.h" -#include "routerparse.h" -#include "statefile.h" -#include "status.h" -#include "ext_orport.h" -#include "anonymize.h" -#ifdef USE_DMALLOC -#include -#include -#endif -#include "memarea.h" -#include "sandbox.h" - -#ifdef HAVE_EVENT2_EVENT_H -#include -#else -#include -#endif - -#ifdef USE_BUFFEREVENTS -#include -#endif - -void evdns_shutdown(int); - -/********* PROTOTYPES **********/ - -static void dumpmemusage(int severity); -static void dumpstats(int severity); /* log stats */ -static void conn_read_callback(evutil_socket_t fd, short event, void *_conn); -static void conn_write_callback(evutil_socket_t fd, short event, void *_conn); -static void second_elapsed_callback(periodic_timer_t *timer, void *args); -static int conn_close_if_marked(int i); -static void connection_start_reading_from_linked_conn(connection_t *conn); -static int connection_should_read_from_linked_conn(connection_t *conn); - -/********* START VARIABLES **********/ - -#ifndef USE_BUFFEREVENTS -int global_read_bucket; /**< Max number of bytes I can read this second. */ -int global_write_bucket; /**< Max number of bytes I can write this second. */ - -/** Max number of relayed (bandwidth class 1) bytes I can read this second. */ -int global_relayed_read_bucket; -/** Max number of relayed (bandwidth class 1) bytes I can write this second. */ -int global_relayed_write_bucket; -/** What was the read bucket before the last second_elapsed_callback() call? - * (used to determine how many bytes we've read). */ -static int stats_prev_global_read_bucket; -/** What was the write bucket before the last second_elapsed_callback() call? - * (used to determine how many bytes we've written). */ -static int stats_prev_global_write_bucket; -#endif - -/* DOCDOC stats_prev_n_read */ -static uint64_t stats_prev_n_read = 0; -/* DOCDOC stats_prev_n_written */ -static uint64_t stats_prev_n_written = 0; - -/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/ -/** How many bytes have we read since we started the process? */ -static uint64_t stats_n_bytes_read = 0; -/** How many bytes have we written since we started the process? */ -static uint64_t stats_n_bytes_written = 0; -/** What time did this process start up? */ -time_t time_of_process_start = 0; -/** How many seconds have we been running? */ -long stats_n_seconds_working = 0; -/** When do we next launch DNS wildcarding checks? */ -static time_t time_to_check_for_correct_dns = 0; - -/** How often will we honor SIGNEWNYM requests? */ -#define MAX_SIGNEWNYM_RATE 10 -/** When did we last process a SIGNEWNYM request? */ -static time_t time_of_last_signewnym = 0; -/** Is there a signewnym request we're currently waiting to handle? */ -static int signewnym_is_pending = 0; -/** How many times have we called newnym? */ -static unsigned newnym_epoch = 0; - -/** Smartlist of all open connections. */ -static smartlist_t *connection_array = NULL; -/** List of connections that have been marked for close and need to be freed - * and removed from connection_array. */ -static smartlist_t *closeable_connection_lst = NULL; -/** List of linked connections that are currently reading data into their - * inbuf from their partner's outbuf. */ -static smartlist_t *active_linked_connection_lst = NULL; -/** Flag: Set to true iff we entered the current libevent main loop via - * loop_once. If so, there's no need to trigger a loopexit in order - * to handle linked connections. */ -static int called_loop_once = 0; - -/** We set this to 1 when we've opened a circuit, so we can print a log - * entry to inform the user that Tor is working. We set it to 0 when - * we think the fact that we once opened a circuit doesn't mean we can do so - * any longer (a big time jump happened, when we notice our directory is - * heinously out-of-date, etc. - */ -int can_complete_circuit=0; - -const char tor_git_revision[] = ""; - -/** How often do we check for router descriptors that we should download - * when we have too little directory info? */ -#define GREEDY_DESCRIPTOR_RETRY_INTERVAL (10) -/** How often do we check for router descriptors that we should download - * when we have enough directory info? */ -#define LAZY_DESCRIPTOR_RETRY_INTERVAL (60) -/** How often do we 'forgive' undownloadable router descriptors and attempt - * to download them again? */ -#define DESCRIPTOR_FAILURE_RESET_INTERVAL (60*60) - -/** Decides our behavior when no logs are configured/before any - * logs have been configured. For 0, we log notice to stdout as normal. - * For 1, we log warnings only. For 2, we log nothing. - */ -int quiet_level = 0; - -/********* END VARIABLES ************/ - -/**************************************************************************** -* -* This section contains accessors and other methods on the connection_array -* variables (which are global within this file and unavailable outside it). -* -****************************************************************************/ - -#if 0 && defined(USE_BUFFEREVENTS) -static void -free_old_inbuf(connection_t *conn) -{ - if (! conn->inbuf) - return; - - tor_assert(conn->outbuf); - tor_assert(buf_datalen(conn->inbuf) == 0); - tor_assert(buf_datalen(conn->outbuf) == 0); - buf_free(conn->inbuf); - buf_free(conn->outbuf); - conn->inbuf = conn->outbuf = NULL; - - if (conn->read_event) { - event_del(conn->read_event); - tor_event_free(conn->read_event); - } - if (conn->write_event) { - event_del(conn->read_event); - tor_event_free(conn->write_event); - } - conn->read_event = conn->write_event = NULL; -} -#endif - -#if defined(_WIN32) && defined(USE_BUFFEREVENTS) -/** Remove the kernel-space send and receive buffers for s. For use - * with IOCP only. */ -static int -set_buffer_lengths_to_zero(tor_socket_t s) -{ - int zero = 0; - int r = 0; - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) { - log_warn(LD_NET, "Unable to clear SO_SNDBUF"); - r = -1; - } - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) { - log_warn(LD_NET, "Unable to clear SO_RCVBUF"); - r = -1; - } - return r; -} -#endif - -/** Add conn to the array of connections that we can poll on. The - * connection's socket must be set; the connection starts out - * non-reading and non-writing. - */ -int -connection_add_impl(connection_t *conn, int is_connecting) -{ - tor_assert(conn); - tor_assert(SOCKET_OK(conn->s) || - conn->linked || - (conn->type == CONN_TYPE_AP && - TO_EDGE_CONN(conn)->is_dns_request)); - - tor_assert(conn->conn_array_index == -1); /* can only connection_add once */ - conn->conn_array_index = smartlist_len(connection_array); - smartlist_add(connection_array, conn); - -#ifdef USE_BUFFEREVENTS - if (connection_type_uses_bufferevent(conn)) { - if (SOCKET_OK(conn->s) && !conn->linked) { - -#ifdef _WIN32 - if (tor_libevent_using_iocp_bufferevents() && - get_options()->UserspaceIOCPBuffers) { - set_buffer_lengths_to_zero(conn->s); - } -#endif - - conn->bufev = bufferevent_socket_new( - tor_libevent_get_base(), - conn->s, - BEV_OPT_DEFER_CALLBACKS); - if (!conn->bufev) { - log_warn(LD_BUG, "Unable to create socket bufferevent"); - smartlist_del(connection_array, conn->conn_array_index); - conn->conn_array_index = -1; - return -1; - } - if (is_connecting) { - /* Put the bufferevent into a "connecting" state so that we'll get - * a "connected" event callback on successful write. */ - bufferevent_socket_connect(conn->bufev, NULL, 0); - } - connection_configure_bufferevent_callbacks(conn); - } else if (conn->linked && conn->linked_conn && - connection_type_uses_bufferevent(conn->linked_conn)) { - tor_assert(!(SOCKET_OK(conn->s))); - if (!conn->bufev) { - struct bufferevent *pair[2] = { NULL, NULL }; - if (bufferevent_pair_new(tor_libevent_get_base(), - BEV_OPT_DEFER_CALLBACKS, - pair) < 0) { - log_warn(LD_BUG, "Unable to create bufferevent pair"); - smartlist_del(connection_array, conn->conn_array_index); - conn->conn_array_index = -1; - return -1; - } - tor_assert(pair[0]); - conn->bufev = pair[0]; - conn->linked_conn->bufev = pair[1]; - } /* else the other side already was added, and got a bufferevent_pair */ - connection_configure_bufferevent_callbacks(conn); - } else { - tor_assert(!conn->linked); - } - - if (conn->bufev) - tor_assert(conn->inbuf == NULL); - - if (conn->linked_conn && conn->linked_conn->bufev) - tor_assert(conn->linked_conn->inbuf == NULL); - } -#else - (void) is_connecting; -#endif - - if (!HAS_BUFFEREVENT(conn) && (SOCKET_OK(conn->s) || conn->linked)) { - conn->read_event = tor_event_new(tor_libevent_get_base(), - conn->s, EV_READ|EV_PERSIST, conn_read_callback, conn); - conn->write_event = tor_event_new(tor_libevent_get_base(), - conn->s, EV_WRITE|EV_PERSIST, conn_write_callback, conn); - /* XXXX CHECK FOR NULL RETURN! */ - } - - log_debug(LD_NET,"new conn type %s, socket %d, address %s, n_conns %d.", - conn_type_to_string(conn->type), (int)conn->s, conn->address, - smartlist_len(connection_array)); - - return 0; -} - -/** Tell libevent that we don't care about conn any more. */ -void -connection_unregister_events(connection_t *conn) -{ - if (conn->read_event) { - if (event_del(conn->read_event)) - log_warn(LD_BUG, "Error removing read event for %d", (int)conn->s); - tor_free(conn->read_event); - } - if (conn->write_event) { - if (event_del(conn->write_event)) - log_warn(LD_BUG, "Error removing write event for %d", (int)conn->s); - tor_free(conn->write_event); - } -#ifdef USE_BUFFEREVENTS - if (conn->bufev) { - bufferevent_free(conn->bufev); - conn->bufev = NULL; - } -#endif - if (conn->type == CONN_TYPE_AP_DNS_LISTENER) { - dnsserv_close_listener(conn); - } -} - -/** Remove the connection from the global list, and remove the - * corresponding poll entry. Calling this function will shift the last - * connection (if any) into the position occupied by conn. - */ -int -connection_remove(connection_t *conn) -{ - int current_index; - connection_t *tmp; - - tor_assert(conn); - - log_debug(LD_NET,"removing socket %d (type %s), n_conns now %d", - (int)conn->s, conn_type_to_string(conn->type), - smartlist_len(connection_array)); - - control_event_conn_bandwidth(conn); - - tor_assert(conn->conn_array_index >= 0); - current_index = conn->conn_array_index; - connection_unregister_events(conn); /* This is redundant, but cheap. */ - if (current_index == smartlist_len(connection_array)-1) { /* at the end */ - smartlist_del(connection_array, current_index); - return 0; - } - - /* replace this one with the one at the end */ - smartlist_del(connection_array, current_index); - tmp = smartlist_get(connection_array, current_index); - tmp->conn_array_index = current_index; - - return 0; -} - -/** If conn is an edge conn, remove it from the list - * of conn's on this circuit. If it's not on an edge, - * flush and send destroys for all circuits on this conn. - * - * Remove it from connection_array (if applicable) and - * from closeable_connection_list. - * - * Then free it. - */ -static void -connection_unlink(connection_t *conn) -{ - connection_about_to_close_connection(conn); - if (conn->conn_array_index >= 0) { - connection_remove(conn); - } - if (conn->linked_conn) { - conn->linked_conn->linked_conn = NULL; - if (! conn->linked_conn->marked_for_close && - conn->linked_conn->reading_from_linked_conn) - connection_start_reading(conn->linked_conn); - conn->linked_conn = NULL; - } - smartlist_remove(closeable_connection_lst, conn); - smartlist_remove(active_linked_connection_lst, conn); - if (conn->type == CONN_TYPE_EXIT) { - assert_connection_edge_not_dns_pending(TO_EDGE_CONN(conn)); - } - if (conn->type == CONN_TYPE_OR) { - if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) - connection_or_remove_from_identity_map(TO_OR_CONN(conn)); - /* connection_unlink() can only get called if the connection - * was already on the closeable list, and it got there by - * connection_mark_for_close(), which was called from - * connection_or_close_normally() or - * connection_or_close_for_error(), so the channel should - * already be in CHANNEL_STATE_CLOSING, and then the - * connection_about_to_close_connection() goes to - * connection_or_about_to_close(), which calls channel_closed() - * to notify the channel_t layer, and closed the channel, so - * nothing more to do here to deal with the channel associated - * with an orconn. - */ - } - connection_free(conn); -} - -/** Initialize the global connection list, closeable connection list, - * and active connection list. */ -STATIC void -init_connection_lists(void) -{ - if (!connection_array) - connection_array = smartlist_new(); - if (!closeable_connection_lst) - closeable_connection_lst = smartlist_new(); - if (!active_linked_connection_lst) - active_linked_connection_lst = smartlist_new(); -} - -/** Schedule conn to be closed. **/ -void -add_connection_to_closeable_list(connection_t *conn) -{ - tor_assert(!smartlist_contains(closeable_connection_lst, conn)); - tor_assert(conn->marked_for_close); - assert_connection_ok(conn, time(NULL)); - smartlist_add(closeable_connection_lst, conn); -} - -/** Return 1 if conn is on the closeable list, else return 0. */ -int -connection_is_on_closeable_list(connection_t *conn) -{ - return smartlist_contains(closeable_connection_lst, conn); -} - -/** Return true iff conn is in the current poll array. */ -int -connection_in_array(connection_t *conn) -{ - return smartlist_contains(connection_array, conn); -} - -/** Set *array to an array of all connections, and *n - * to the length of the array. *array and *n must not - * be modified. - */ -smartlist_t * -get_connection_array(void) -{ - if (!connection_array) - connection_array = smartlist_new(); - return connection_array; -} - -/** Provides the traffic read and written over the life of the process. */ - -uint64_t -get_bytes_read(void) -{ - return stats_n_bytes_read; -} - -/* DOCDOC get_bytes_written */ -uint64_t -get_bytes_written(void) -{ - return stats_n_bytes_written; -} - -/** Set the event mask on conn to events. (The event - * mask is a bitmask whose bits are READ_EVENT and WRITE_EVENT) - */ -void -connection_watch_events(connection_t *conn, watchable_events_t events) -{ - IF_HAS_BUFFEREVENT(conn, { - short ev = ((short)events) & (EV_READ|EV_WRITE); - short old_ev = bufferevent_get_enabled(conn->bufev); - if ((ev & ~old_ev) != 0) { - bufferevent_enable(conn->bufev, ev); - } - if ((old_ev & ~ev) != 0) { - bufferevent_disable(conn->bufev, old_ev & ~ev); - } - return; - }); - if (events & READ_EVENT) - connection_start_reading(conn); - else - connection_stop_reading(conn); - - if (events & WRITE_EVENT) - connection_start_writing(conn); - else - connection_stop_writing(conn); -} - -/** Return true iff conn is listening for read events. */ -int -connection_is_reading(connection_t *conn) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, - return (bufferevent_get_enabled(conn->bufev) & EV_READ) != 0; - ); - return conn->reading_from_linked_conn || - (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); -} - -/** Tell the main loop to stop notifying conn of any read events. */ -MOCK_IMPL(void, -connection_stop_reading,(connection_t *conn)) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, { - bufferevent_disable(conn->bufev, EV_READ); - return; - }); - - tor_assert(conn->read_event); - - if (conn->linked) { - conn->reading_from_linked_conn = 0; - connection_stop_reading_from_linked_conn(conn); - } else { - if (event_del(conn->read_event)) - log_warn(LD_NET, "Error from libevent setting read event state for %d " - "to unwatched: %s", - (int)conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); - } -} - -/** Tell the main loop to start notifying conn of any read events. */ -MOCK_IMPL(void, -connection_start_reading,(connection_t *conn)) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, { - bufferevent_enable(conn->bufev, EV_READ); - return; - }); - - tor_assert(conn->read_event); - - if (conn->linked) { - conn->reading_from_linked_conn = 1; - if (connection_should_read_from_linked_conn(conn)) - connection_start_reading_from_linked_conn(conn); - } else { - if (event_add(conn->read_event, NULL)) - log_warn(LD_NET, "Error from libevent setting read event state for %d " - "to watched: %s", - (int)conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); - } -} - -/** Return true iff conn is listening for write events. */ -int -connection_is_writing(connection_t *conn) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, - return (bufferevent_get_enabled(conn->bufev) & EV_WRITE) != 0; - ); - - return conn->writing_to_linked_conn || - (conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL)); -} - -/** Tell the main loop to stop notifying conn of any write events. */ -MOCK_IMPL(void, -connection_stop_writing,(connection_t *conn)) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, { - bufferevent_disable(conn->bufev, EV_WRITE); - return; - }); - - tor_assert(conn->write_event); - - if (conn->linked) { - conn->writing_to_linked_conn = 0; - if (conn->linked_conn) - connection_stop_reading_from_linked_conn(conn->linked_conn); - } else { - if (event_del(conn->write_event)) - log_warn(LD_NET, "Error from libevent setting write event state for %d " - "to unwatched: %s", - (int)conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); - } -} - -/** Tell the main loop to start notifying conn of any write events. */ -MOCK_IMPL(void, -connection_start_writing,(connection_t *conn)) -{ - tor_assert(conn); - - IF_HAS_BUFFEREVENT(conn, { - bufferevent_enable(conn->bufev, EV_WRITE); - return; - }); - - tor_assert(conn->write_event); - - if (conn->linked) { - conn->writing_to_linked_conn = 1; - if (conn->linked_conn && - connection_should_read_from_linked_conn(conn->linked_conn)) - connection_start_reading_from_linked_conn(conn->linked_conn); - } else { - if (event_add(conn->write_event, NULL)) - log_warn(LD_NET, "Error from libevent setting write event state for %d " - "to watched: %s", - (int)conn->s, - tor_socket_strerror(tor_socket_errno(conn->s))); - } -} - -/** Return true iff conn is linked conn, and reading from the conn - * linked to it would be good and feasible. (Reading is "feasible" if the - * other conn exists and has data in its outbuf, and is "good" if we have our - * reading_from_linked_conn flag set and the other conn has its - * writing_to_linked_conn flag set.)*/ -static int -connection_should_read_from_linked_conn(connection_t *conn) -{ - if (conn->linked && conn->reading_from_linked_conn) { - if (! conn->linked_conn || - (conn->linked_conn->writing_to_linked_conn && - buf_datalen(conn->linked_conn->outbuf))) - return 1; - } - return 0; -} - -/** Helper: Tell the main loop to begin reading bytes into conn from - * its linked connection, if it is not doing so already. Called by - * connection_start_reading and connection_start_writing as appropriate. */ -static void -connection_start_reading_from_linked_conn(connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->linked == 1); - - if (!conn->active_on_link) { - conn->active_on_link = 1; - smartlist_add(active_linked_connection_lst, conn); - if (!called_loop_once) { - /* This is the first event on the list; we won't be in LOOP_ONCE mode, - * so we need to make sure that the event_base_loop() actually exits at - * the end of its run through the current connections and lets us - * activate read events for linked connections. */ - struct timeval tv = { 0, 0 }; - tor_event_base_loopexit(tor_libevent_get_base(), &tv); - } - } else { - tor_assert(smartlist_contains(active_linked_connection_lst, conn)); - } -} - -/** Tell the main loop to stop reading bytes into conn from its linked - * connection, if is currently doing so. Called by connection_stop_reading, - * connection_stop_writing, and connection_read. */ -void -connection_stop_reading_from_linked_conn(connection_t *conn) -{ - tor_assert(conn); - tor_assert(conn->linked == 1); - - if (conn->active_on_link) { - conn->active_on_link = 0; - /* FFFF We could keep an index here so we can smartlist_del - * cleanly. On the other hand, this doesn't show up on profiles, - * so let's leave it alone for now. */ - smartlist_remove(active_linked_connection_lst, conn); - } else { - tor_assert(!smartlist_contains(active_linked_connection_lst, conn)); - } -} - -/** Close all connections that have been scheduled to get closed. */ -STATIC void -close_closeable_connections(void) -{ - int i; - for (i = 0; i < smartlist_len(closeable_connection_lst); ) { - connection_t *conn = smartlist_get(closeable_connection_lst, i); - if (conn->conn_array_index < 0) { - connection_unlink(conn); /* blow it away right now */ - } else { - if (!conn_close_if_marked(conn->conn_array_index)) - ++i; - } - } -} - -/** Libevent callback: this gets invoked when (connection_t*)conn has - * some data to read. */ -static void -conn_read_callback(evutil_socket_t fd, short event, void *_conn) -{ - connection_t *conn = _conn; - (void)fd; - (void)event; - - log_debug(LD_NET,"socket %d wants to read.",(int)conn->s); - - /* assert_connection_ok(conn, time(NULL)); */ - - if (connection_handle_read(conn) < 0) { - if (!conn->marked_for_close) { -#ifndef _WIN32 - log_warn(LD_BUG,"Unhandled error on read for %s connection " - "(fd %d); removing", - conn_type_to_string(conn->type), (int)conn->s); - tor_fragile_assert(); -#endif - if (CONN_IS_EDGE(conn)) - connection_edge_end_errno(TO_EDGE_CONN(conn)); - connection_mark_for_close(conn); - } - } - assert_connection_ok(conn, time(NULL)); - - if (smartlist_len(closeable_connection_lst)) - close_closeable_connections(); -} - -/** Libevent callback: this gets invoked when (connection_t*)conn has - * some data to write. */ -static void -conn_write_callback(evutil_socket_t fd, short events, void *_conn) -{ - connection_t *conn = _conn; - (void)fd; - (void)events; - - LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "socket %d wants to write.", - (int)conn->s)); - - /* assert_connection_ok(conn, time(NULL)); */ - - if (connection_handle_write(conn, 0) < 0) { - if (!conn->marked_for_close) { - /* this connection is broken. remove it. */ - log_fn(LOG_WARN,LD_BUG, - "unhandled error on write for %s connection (fd %d); removing", - conn_type_to_string(conn->type), (int)conn->s); - tor_fragile_assert(); - if (CONN_IS_EDGE(conn)) { - /* otherwise we cry wolf about duplicate close */ - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - if (!edge_conn->end_reason) - edge_conn->end_reason = END_STREAM_REASON_INTERNAL; - edge_conn->edge_has_sent_end = 1; - } - connection_close_immediate(conn); /* So we don't try to flush. */ - connection_mark_for_close(conn); - } - } - assert_connection_ok(conn, time(NULL)); - - if (smartlist_len(closeable_connection_lst)) - close_closeable_connections(); -} - -/** If the connection at connection_array[i] is marked for close, then: - * - If it has data that it wants to flush, try to flush it. - * - If it _still_ has data to flush, and conn->hold_open_until_flushed is - * true, then leave the connection open and return. - * - Otherwise, remove the connection from connection_array and from - * all other lists, close it, and free it. - * Returns 1 if the connection was closed, 0 otherwise. - */ -static int -conn_close_if_marked(int i) -{ - connection_t *conn; - int retval; - time_t now; - - conn = smartlist_get(connection_array, i); - if (!conn->marked_for_close) - return 0; /* nothing to see here, move along */ - now = time(NULL); - assert_connection_ok(conn, now); - /* assert_all_pending_dns_resolves_ok(); */ - -#ifdef USE_BUFFEREVENTS - if (conn->bufev) { - if (conn->hold_open_until_flushed && - evbuffer_get_length(bufferevent_get_output(conn->bufev))) { - /* don't close yet. */ - return 0; - } - if (conn->linked_conn && ! conn->linked_conn->marked_for_close) { - /* We need to do this explicitly so that the linked connection - * notices that there was an EOF. */ - bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED); - } - } -#endif - - log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").", - conn->s); - - /* If the connection we are about to close was trying to connect to - a proxy server and failed, the client won't be able to use that - proxy. We should warn the user about this. */ - if (conn->proxy_state == PROXY_INFANT) - log_failed_proxy_connection(conn); - - IF_HAS_BUFFEREVENT(conn, goto unlink); - if ((SOCKET_OK(conn->s) || conn->linked_conn) && - connection_wants_to_flush(conn)) { - /* s == -1 means it's an incomplete edge connection, or that the socket - * has already been closed as unflushable. */ - ssize_t sz = connection_bucket_write_limit(conn, now); - if (!conn->hold_open_until_flushed) - log_info(LD_NET, - "Conn (addr %s, fd %d, type %s, state %d) marked, but wants " - "to flush %d bytes. (Marked at %s:%d)", - escaped_safe_str_client(conn->address), - (int)conn->s, conn_type_to_string(conn->type), conn->state, - (int)conn->outbuf_flushlen, - conn->marked_for_close_file, conn->marked_for_close); - if (conn->linked_conn) { - retval = move_buf_to_buf(conn->linked_conn->inbuf, conn->outbuf, - &conn->outbuf_flushlen); - if (retval >= 0) { - /* The linked conn will notice that it has data when it notices that - * we're gone. */ - connection_start_reading_from_linked_conn(conn->linked_conn); - } - log_debug(LD_GENERAL, "Flushed last %d bytes from a linked conn; " - "%d left; flushlen %d; wants-to-flush==%d", retval, - (int)connection_get_outbuf_len(conn), - (int)conn->outbuf_flushlen, - connection_wants_to_flush(conn)); - } else if (connection_speaks_cells(conn)) { - if (conn->state == OR_CONN_STATE_OPEN) { - retval = flush_buf_tls(TO_OR_CONN(conn)->tls, conn->outbuf, sz, - &conn->outbuf_flushlen); - } else - retval = -1; /* never flush non-open broken tls connections */ - } else { - retval = flush_buf(conn->s, conn->outbuf, sz, &conn->outbuf_flushlen); - } - if (retval >= 0 && /* Technically, we could survive things like - TLS_WANT_WRITE here. But don't bother for now. */ - conn->hold_open_until_flushed && connection_wants_to_flush(conn)) { - if (retval > 0) { - LOG_FN_CONN(conn, (LOG_INFO,LD_NET, - "Holding conn (fd %d) open for more flushing.", - (int)conn->s)); - conn->timestamp_lastwritten = now; /* reset so we can flush more */ - } else if (sz == 0) { - /* Also, retval==0. If we get here, we didn't want to write anything - * (because of rate-limiting) and we didn't. */ - - /* Connection must flush before closing, but it's being rate-limited. - * Let's remove from Libevent, and mark it as blocked on bandwidth - * so it will be re-added on next token bucket refill. Prevents - * busy Libevent loops where we keep ending up here and returning - * 0 until we are no longer blocked on bandwidth. - */ - if (connection_is_writing(conn)) { - conn->write_blocked_on_bw = 1; - connection_stop_writing(conn); - } - if (connection_is_reading(conn)) { - /* XXXX024 We should make this code unreachable; if a connection is - * marked for close and flushing, there is no point in reading to it - * at all. Further, checking at this point is a bit of a hack: it - * would make much more sense to react in - * connection_handle_read_impl, or to just stop reading in - * mark_and_flush */ -#if 0 -#define MARKED_READING_RATE 180 - static ratelim_t marked_read_lim = RATELIM_INIT(MARKED_READING_RATE); - char *m; - if ((m = rate_limit_log(&marked_read_lim, now))) { - log_warn(LD_BUG, "Marked connection (fd %d, type %s, state %s) " - "is still reading; that shouldn't happen.%s", - (int)conn->s, conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state), m); - tor_free(m); - } -#endif - conn->read_blocked_on_bw = 1; - connection_stop_reading(conn); - } - } - return 0; - } - if (connection_wants_to_flush(conn)) { - int severity; - if (conn->type == CONN_TYPE_EXIT || - (conn->type == CONN_TYPE_OR && server_mode(get_options())) || - (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER)) - severity = LOG_INFO; - else - severity = LOG_NOTICE; - /* XXXX Maybe allow this to happen a certain amount per hour; it usually - * is meaningless. */ - log_fn(severity, LD_NET, "We stalled too much while trying to write %d " - "bytes to address %s. If this happens a lot, either " - "something is wrong with your network connection, or " - "something is wrong with theirs. " - "(fd %d, type %s, state %d, marked at %s:%d).", - (int)connection_get_outbuf_len(conn), - escaped_safe_str_client(conn->address), - (int)conn->s, conn_type_to_string(conn->type), conn->state, - conn->marked_for_close_file, - conn->marked_for_close); - } - } - -#ifdef USE_BUFFEREVENTS - unlink: -#endif - connection_unlink(conn); /* unlink, remove, free */ - return 1; -} - -/** We've just tried every dirserver we know about, and none of - * them were reachable. Assume the network is down. Change state - * so next time an application connection arrives we'll delay it - * and try another directory fetch. Kill off all the circuit_wait - * streams that are waiting now, since they will all timeout anyway. - */ -void -directory_all_unreachable(time_t now) -{ - connection_t *conn; - (void)now; - - stats_n_seconds_working=0; /* reset it */ - - while ((conn = connection_get_by_type_state(CONN_TYPE_AP, - AP_CONN_STATE_CIRCUIT_WAIT))) { - entry_connection_t *entry_conn = TO_ENTRY_CONN(conn); - log_notice(LD_NET, - "Is your network connection down? " - "Failing connection to '%s:%d'.", - safe_str_client(entry_conn->socks_request->address), - entry_conn->socks_request->port); - connection_mark_unattached_ap(entry_conn, - END_STREAM_REASON_NET_UNREACHABLE); - } - control_event_general_status(LOG_ERR, "DIR_ALL_UNREACHABLE"); -} - -/** This function is called whenever we successfully pull down some new - * network statuses or server descriptors. */ -void -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 = from_cache || - directory_too_idle_to_fetch_descriptors(options, now); - tor_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()); - update_all_descriptor_downloads(now); - return; - } else { - if (directory_fetches_from_authorities(options)) { - update_all_descriptor_downloads(now); - } - - /* if we have enough dir info, then update our guard status with - * whatever we just learned. */ - entry_guards_compute_status(options, now); - /* Don't even bother trying to get extrainfo until the rest of our - * directory info is up-to-date */ - if (options->DownloadExtraInfo) - update_extrainfo_downloads(now); - } - - if (server_mode(options) && !net_is_disabled() && !from_cache && - (can_complete_circuit || !any_predicted_circuits(now))) - consider_testing_reachability(1, 1); -} - -/** How long do we wait before killing OR connections with no circuits? - * In Tor versions up to 0.2.1.25 and 0.2.2.12-alpha, we waited 15 minutes - * before cancelling these connections, which caused fast relays to accrue - * many many idle connections. Hopefully 3 minutes is low enough that - * it kills most idle connections, without being so low that we cause - * clients to bounce on and off. - */ -#define IDLE_OR_CONN_TIMEOUT 180 - -/** Perform regular maintenance tasks for a single connection. This - * function gets run once per second per connection by run_scheduled_events. - */ -static void -run_connection_housekeeping(int i, time_t now) -{ - cell_t cell; - connection_t *conn = smartlist_get(connection_array, i); - const or_options_t *options = get_options(); - or_connection_t *or_conn; - int past_keepalive = - now >= conn->timestamp_lastwritten + options->KeepalivePeriod; - - if (conn->outbuf && !connection_get_outbuf_len(conn) && - conn->type == CONN_TYPE_OR) - TO_OR_CONN(conn)->timestamp_lastempty = now; - - if (conn->marked_for_close) { - /* nothing to do here */ - return; - } - - /* Expire any directory connections that haven't been active (sent - * if a server or received if a client) for 5 min */ - if (conn->type == CONN_TYPE_DIR && - ((DIR_CONN_IS_SERVER(conn) && - conn->timestamp_lastwritten - + options->TestingDirConnectionMaxStall < now) || - (!DIR_CONN_IS_SERVER(conn) && - conn->timestamp_lastread - + options->TestingDirConnectionMaxStall < now))) { - log_info(LD_DIR,"Expiring wedged directory conn (fd %d, purpose %d)", - (int)conn->s, conn->purpose); - /* This check is temporary; it's to let us know whether we should consider - * parsing partial serverdesc responses. */ - if (conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC && - connection_get_inbuf_len(conn) >= 1024) { - log_info(LD_DIR,"Trying to extract information from wedged server desc " - "download."); - connection_dir_reached_eof(TO_DIR_CONN(conn)); - } else { - connection_mark_for_close(conn); - } - return; - } - - if (!connection_speaks_cells(conn)) - return; /* we're all done here, the rest is just for OR conns */ - - /* If we haven't written to an OR connection for a while, then either nuke - the connection or send a keepalive, depending. */ - - or_conn = TO_OR_CONN(conn); -#ifdef USE_BUFFEREVENTS - tor_assert(conn->bufev); -#else - tor_assert(conn->outbuf); -#endif - - if (channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)) && - !connection_or_get_num_circuits(or_conn)) { - /* It's bad for new circuits, and has no unmarked circuits on it: - * mark it now. */ - log_info(LD_OR, - "Expiring non-used OR connection to fd %d (%s:%d) [Too old].", - (int)conn->s, conn->address, conn->port); - if (conn->state == OR_CONN_STATE_CONNECTING) - connection_or_connect_failed(TO_OR_CONN(conn), - END_OR_CONN_REASON_TIMEOUT, - "Tor gave up on the connection"); - connection_or_close_normally(TO_OR_CONN(conn), 1); - } else if (!connection_state_is_open(conn)) { - if (past_keepalive) { - /* We never managed to actually get this connection open and happy. */ - log_info(LD_OR,"Expiring non-open OR connection to fd %d (%s:%d).", - (int)conn->s,conn->address, conn->port); - connection_or_close_normally(TO_OR_CONN(conn), 0); - } - } else if (we_are_hibernating() && - !connection_or_get_num_circuits(or_conn) && - !connection_get_outbuf_len(conn)) { - /* We're hibernating, there's no circuits, and nothing to flush.*/ - log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " - "[Hibernating or exiting].", - (int)conn->s,conn->address, conn->port); - connection_or_close_normally(TO_OR_CONN(conn), 1); - } else if (!connection_or_get_num_circuits(or_conn) && - now >= or_conn->timestamp_last_added_nonpadding + - IDLE_OR_CONN_TIMEOUT) { - log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " - "[idle %d].", (int)conn->s,conn->address, conn->port, - (int)(now - or_conn->timestamp_last_added_nonpadding)); - connection_or_close_normally(TO_OR_CONN(conn), 0); - } else if ( - now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 && - now >= conn->timestamp_lastwritten + options->KeepalivePeriod*10) { - log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, - "Expiring stuck OR connection to fd %d (%s:%d). (%d bytes to " - "flush; %d seconds since last write)", - (int)conn->s, conn->address, conn->port, - (int)connection_get_outbuf_len(conn), - (int)(now-conn->timestamp_lastwritten)); - connection_or_close_normally(TO_OR_CONN(conn), 0); - } else if (past_keepalive && !connection_get_outbuf_len(conn)) { - /* send a padding cell */ - log_fn(LOG_DEBUG,LD_OR,"Sending keepalive to (%s:%d)", - conn->address, conn->port); - memset(&cell,0,sizeof(cell_t)); - cell.command = CELL_PADDING; - connection_or_write_cell_to_buf(&cell, or_conn); - } -} - -/** Honor a NEWNYM request: make future requests unlinkable to past - * requests. */ -static void -signewnym_impl(time_t now) -{ - const or_options_t *options = get_options(); - if (!proxy_mode(options)) { - log_info(LD_CONTROL, "Ignoring SIGNAL NEWNYM because client functionality " - "is disabled."); - return; - } - - circuit_mark_all_dirty_circs_as_unusable(); - addressmap_clear_transient(); - rend_client_purge_state(); - time_of_last_signewnym = now; - signewnym_is_pending = 0; - - ++newnym_epoch; - - control_event_signal(SIGNEWNYM); -} - -/** Return the number of times that signewnym has been called. */ -unsigned -get_signewnym_epoch(void) -{ - return newnym_epoch; -} - -/** Perform regular maintenance tasks. This function gets run once per - * second by second_elapsed_callback(). - */ -static void -run_scheduled_events(time_t now) -{ - static time_t last_rotated_x509_certificate = 0; - static time_t time_to_check_v3_certificate = 0; - static time_t time_to_check_listeners = 0; - static time_t time_to_check_descriptor = 0; - static time_t time_to_download_networkstatus = 0; - static time_t time_to_shrink_memory = 0; - static time_t time_to_try_getting_descriptors = 0; - static time_t time_to_reset_descriptor_failures = 0; - static time_t time_to_add_entropy = 0; - static time_t time_to_write_bridge_status_file = 0; - static time_t time_to_downrate_stability = 0; - static time_t time_to_save_stability = 0; - static time_t time_to_clean_caches = 0; - static time_t time_to_recheck_bandwidth = 0; - static time_t time_to_check_for_expired_networkstatus = 0; - static time_t time_to_write_stats_files = 0; - static time_t time_to_write_bridge_stats = 0; - static time_t time_to_check_port_forwarding = 0; - static time_t time_to_launch_reachability_tests = 0; - static int should_init_bridge_stats = 1; - static time_t time_to_retry_dns_init = 0; - static time_t time_to_next_heartbeat = 0; - const or_options_t *options = get_options(); - - int is_server = server_mode(options); - int i; - int have_dir_info; - - /** 0. See if we've been asked to shut down and our timeout has - * expired; or if our bandwidth limits are exhausted and we - * should hibernate; or if it's time to wake up from hibernation. - */ - consider_hibernation(now); - -#if 0 - { - static time_t nl_check_time = 0; - if (nl_check_time <= now) { - nodelist_assert_ok(); - nl_check_time = now + 30; - } - } -#endif - - /* 0b. If we've deferred a signewnym, make sure it gets handled - * eventually. */ - if (signewnym_is_pending && - time_of_last_signewnym + MAX_SIGNEWNYM_RATE <= now) { - log_info(LD_CONTROL, "Honoring delayed NEWNYM request"); - signewnym_impl(now); - } - - /* 0c. If we've deferred log messages for the controller, handle them now */ - flush_pending_log_callbacks(); - - /** 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys, - * shut down and restart all cpuworkers, and update the directory if - * necessary. - */ - if (is_server && - get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) { - log_info(LD_GENERAL,"Rotating onion key."); - rotate_onion_key(); - cpuworkers_rotate(); - if (router_rebuild_descriptor(1)<0) { - log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); - } - if (advertised_server_mode() && !options->DisableNetwork) - router_upload_dir_desc_to_dirservers(0); - } - - if (!options->DisableNetwork && time_to_try_getting_descriptors < now) { - update_all_descriptor_downloads(now); - update_extrainfo_downloads(now); - if (router_have_minimum_dir_info()) - time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL; - else - time_to_try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL; - } - - if (time_to_reset_descriptor_failures < now) { - router_reset_descriptor_download_failures(); - time_to_reset_descriptor_failures = - now + DESCRIPTOR_FAILURE_RESET_INTERVAL; - } - - if (options->UseBridges) - fetch_bridge_descriptors(options, now); - - /** 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our - * TLS context. */ - if (!last_rotated_x509_certificate) - last_rotated_x509_certificate = now; - if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) { - log_info(LD_GENERAL,"Rotating tls context."); - if (router_initialize_tls_context() < 0) { - log_warn(LD_BUG, "Error reinitializing TLS context"); - /* XXX is it a bug here, that we just keep going? -RD */ - } - last_rotated_x509_certificate = now; - /* We also make sure to rotate the TLS connections themselves if they've - * been up for too long -- but that's done via is_bad_for_new_circs in - * connection_run_housekeeping() above. */ - } - - if (time_to_add_entropy < now) { - if (time_to_add_entropy) { - /* We already seeded once, so don't die on failure. */ - crypto_seed_rng(0); - } -/** How often do we add more entropy to OpenSSL's RNG pool? */ -#define ENTROPY_INTERVAL (60*60) - time_to_add_entropy = now + ENTROPY_INTERVAL; - } - - /** 1c. If we have to change the accounting interval or record - * bandwidth used in this accounting interval, do so. */ - if (accounting_is_enabled(options)) - accounting_run_housekeeping(now); - - if (time_to_launch_reachability_tests < now && - (authdir_mode_tests_reachability(options)) && - !net_is_disabled()) { - time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; - /* try to determine reachability of the other Tor relays */ - dirserv_test_reachability(now); - } - - /** 1d. Periodically, we discount older stability information so that new - * stability info counts more, and save the stability information to disk as - * appropriate. */ - if (time_to_downrate_stability < now) - time_to_downrate_stability = rep_hist_downrate_old_runs(now); - if (authdir_mode_tests_reachability(options)) { - if (time_to_save_stability < now) { - if (time_to_save_stability && rep_hist_record_mtbf_data(now, 1)<0) { - log_warn(LD_GENERAL, "Couldn't store mtbf data."); - } -#define SAVE_STABILITY_INTERVAL (30*60) - time_to_save_stability = now + SAVE_STABILITY_INTERVAL; - } - } - - /* 1e. Periodically, if we're a v3 authority, we check whether our cert is - * close to expiring and warn the admin if it is. */ - if (time_to_check_v3_certificate < now) { - v3_authority_check_key_expiry(); -#define CHECK_V3_CERTIFICATE_INTERVAL (5*60) - time_to_check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL; - } - - /* 1f. Check whether our networkstatus has expired. - */ - if (time_to_check_for_expired_networkstatus < now) { - networkstatus_t *ns = networkstatus_get_latest_consensus(); - /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in - * networkstatus_get_reasonably_live_consensus(), but that value is way - * way too high. Arma: is the bridge issue there resolved yet? -NM */ -#define NS_EXPIRY_SLOP (24*60*60) - if (ns && ns->valid_until < now+NS_EXPIRY_SLOP && - router_have_minimum_dir_info()) { - router_dir_info_changed(); - } -#define CHECK_EXPIRED_NS_INTERVAL (2*60) - time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL; - } - - /* 1g. Check whether we should write statistics to disk. - */ - if (time_to_write_stats_files < now) { -#define CHECK_WRITE_STATS_INTERVAL (60*60) - time_t next_time_to_write_stats_files = (time_to_write_stats_files > 0 ? - time_to_write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL; - if (options->CellStatistics) { - time_t next_write = - rep_hist_buffer_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->DirReqStatistics) { - time_t next_write = geoip_dirreq_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->EntryStatistics) { - time_t next_write = geoip_entry_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->ExitPortStatistics) { - time_t next_write = rep_hist_exit_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->ConnDirectionStatistics) { - time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->BridgeAuthoritativeDir) { - time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - time_to_write_stats_files = next_time_to_write_stats_files; - } - - /* 1h. Check whether we should write bridge statistics to disk. - */ - if (should_record_bridge_info(options)) { - if (time_to_write_bridge_stats < now) { - if (should_init_bridge_stats) { - /* (Re-)initialize bridge statistics. */ - geoip_bridge_stats_init(now); - time_to_write_bridge_stats = now + WRITE_STATS_INTERVAL; - should_init_bridge_stats = 0; - } else { - /* Possibly write bridge statistics to disk and ask when to write - * them next time. */ - time_to_write_bridge_stats = geoip_bridge_stats_write( - time_to_write_bridge_stats); - } - } - } else if (!should_init_bridge_stats) { - /* Bridge mode was turned off. Ensure that stats are re-initialized - * next time bridge mode is turned on. */ - should_init_bridge_stats = 1; - } - - /* Remove old information from rephist and the rend cache. */ - if (time_to_clean_caches < now) { - rep_history_clean(now - options->RephistTrackTime); - rend_cache_clean(now); - rend_cache_clean_v2_descs_as_dir(now); - microdesc_cache_rebuild(NULL, 0); -#define CLEAN_CACHES_INTERVAL (30*60) - time_to_clean_caches = now + CLEAN_CACHES_INTERVAL; - } - -#define RETRY_DNS_INTERVAL (10*60) - /* If we're a server and initializing dns failed, retry periodically. */ - if (time_to_retry_dns_init < now) { - time_to_retry_dns_init = now + RETRY_DNS_INTERVAL; - if (is_server && has_dns_init_failed()) - dns_init(); - } - - /** 2. Periodically, we consider force-uploading our descriptor - * (if we've passed our internal checks). */ - -/** How often do we check whether part of our router info has changed in a - * way that would require an upload? That includes checking whether our IP - * address has changed. */ -#define CHECK_DESCRIPTOR_INTERVAL (60) - - /* 2b. Once per minute, regenerate and upload the descriptor if the old - * one is inaccurate. */ - if (time_to_check_descriptor < now && !options->DisableNetwork) { - static int dirport_reachability_count = 0; - time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; - check_descriptor_bandwidth_changed(now); - check_descriptor_ipaddress_changed(now); - mark_my_descriptor_dirty_if_too_old(now); - consider_publishable_server(0); - /* also, check religiously for reachability, if it's within the first - * 20 minutes of our uptime. */ - if (is_server && - (can_complete_circuit || !any_predicted_circuits(now)) && - !we_are_hibernating()) { - if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { - consider_testing_reachability(1, dirport_reachability_count==0); - if (++dirport_reachability_count > 5) - dirport_reachability_count = 0; - } else if (time_to_recheck_bandwidth < now) { - /* If we haven't checked for 12 hours and our bandwidth estimate is - * low, do another bandwidth test. This is especially important for - * bridges, since they might go long periods without much use. */ - const routerinfo_t *me = router_get_my_routerinfo(); - if (time_to_recheck_bandwidth && me && - me->bandwidthcapacity < me->bandwidthrate && - me->bandwidthcapacity < 51200) { - reset_bandwidth_test(); - } -#define BANDWIDTH_RECHECK_INTERVAL (12*60*60) - time_to_recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL; - } - } - - /* If any networkstatus documents are no longer recent, we need to - * update all the descriptors' running status. */ - /* Remove dead routers. */ - routerlist_remove_old_routers(); - } - - /* 2c. Every minute (or every second if TestingTorNetwork), check - * whether we want to download any networkstatus documents. */ - -/* How often do we check whether we should download network status - * documents? */ -#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60) - - if (time_to_download_networkstatus < now && !options->DisableNetwork) { - time_to_download_networkstatus = - now + networkstatus_dl_check_interval(options); - update_networkstatus_downloads(now); - } - - /** 2c. Let directory voting happen. */ - if (authdir_mode_v3(options)) - dirvote_act(options, now); - - /** 3a. Every second, we examine pending circuits and prune the - * ones which have been pending for more than a few seconds. - * We do this before step 4, so it can try building more if - * it's not comfortable with the number of available circuits. - */ - /* (If our circuit build timeout can ever become lower than a second (which - * it can't, currently), we should do this more often.) */ - circuit_expire_building(); - - /** 3b. Also look at pending streams and prune the ones that 'began' - * a long time ago but haven't gotten a 'connected' yet. - * Do this before step 4, so we can put them back into pending - * state to be picked up by the new circuit. - */ - connection_ap_expire_beginning(); - - /** 3c. And expire connections that we've held open for too long. - */ - connection_expire_held_open(); - - /** 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!net_is_disabled() && time_to_check_listeners < now) { - retry_all_listeners(NULL, NULL, 0); - time_to_check_listeners = now+60; - } - - /** 4. Every second, we try a new circuit if there are no valid - * circuits. Every NewCircuitPeriod seconds, we expire circuits - * that became dirty more than MaxCircuitDirtiness seconds ago, - * and we make a new circ if there are no clean circuits. - */ - have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !net_is_disabled()) - circuit_build_needed_circs(now); - - /* every 10 seconds, but not at the same second as other such events */ - if (now % 10 == 5) - circuit_expire_old_circuits_serverside(now); - - /** 5. We do housekeeping for each connection... */ - connection_or_set_bad_connections(NULL, 0); - for (i=0;ioutbuf) - buf_shrink(conn->outbuf); - if (conn->inbuf) - buf_shrink(conn->inbuf); - }); - clean_cell_pool(); - buf_shrink_freelists(0); -/** How often do we check buffers and pools for empty space that can be - * deallocated? */ -#define MEM_SHRINK_INTERVAL (60) - time_to_shrink_memory = now + MEM_SHRINK_INTERVAL; - } - - /** 6. And remove any marked circuits... */ - circuit_close_all_marked(); - - /** 7. And upload service descriptors if necessary. */ - if (can_complete_circuit && !net_is_disabled()) { - rend_consider_services_upload(now); - rend_consider_descriptor_republication(); - } - - /** 8. and blow away any connections that need to die. have to do this now, - * because if we marked a conn for close and left its socket -1, then - * we'll pass it to poll/select and bad things will happen. - */ - close_closeable_connections(); - - /** 8b. And if anything in our state is ready to get flushed to disk, we - * flush it. */ - or_state_save(now); - - /** 8c. Do channel cleanup just like for connections */ - channel_run_cleanup(); - channel_listener_run_cleanup(); - - /** 9. and if we're a server, check whether our DNS is telling stories to - * us. */ - if (!net_is_disabled() && - public_server_mode(options) && time_to_check_for_correct_dns < now) { - if (!time_to_check_for_correct_dns) { - time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); - } else { - dns_launch_correctness_checks(); - time_to_check_for_correct_dns = now + 12*3600 + - crypto_rand_int(12*3600); - } - } - - /** 10. write bridge networkstatus file to disk */ - if (options->BridgeAuthoritativeDir && - time_to_write_bridge_status_file < now) { - networkstatus_dump_bridge_status_to_file(now); -#define BRIDGE_STATUSFILE_INTERVAL (30*60) - time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL; - } - - /** 11. check the port forwarding app */ - if (!net_is_disabled() && - time_to_check_port_forwarding < now && - options->PortForwarding && - is_server) { -#define PORT_FORWARDING_CHECK_INTERVAL 5 - 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; - } - - /** 11b. check pending unconfigured managed proxies */ - if (!net_is_disabled() && pt_proxies_configuration_pending()) - pt_configure_remaining_proxies(); - - /** 12. write the heartbeat message */ - if (options->HeartbeatPeriod && - time_to_next_heartbeat <= now) { - if (time_to_next_heartbeat) /* don't log the first heartbeat */ - log_heartbeat(now); - time_to_next_heartbeat = now+options->HeartbeatPeriod; - } -} - -/** Timer: used to invoke second_elapsed_callback() once per second. */ -static periodic_timer_t *second_timer = NULL; -/** Number of libevent errors in the last second: we die if we get too many. */ -static int n_libevent_errors = 0; - -/** Libevent callback: invoked once every second. */ -static void -second_elapsed_callback(periodic_timer_t *timer, void *arg) -{ - /* XXXX This could be sensibly refactored into multiple callbacks, and we - * could use Libevent's timers for this rather than checking the current - * time against a bunch of timeouts every second. */ - static time_t current_second = 0; - time_t now; - size_t bytes_written; - size_t bytes_read; - int seconds_elapsed; - const or_options_t *options = get_options(); - (void)timer; - (void)arg; - - n_libevent_errors = 0; - - /* log_notice(LD_GENERAL, "Tick."); */ - now = time(NULL); - update_approx_time(now); - - /* the second has rolled over. check more stuff. */ - seconds_elapsed = current_second ? (int)(now - current_second) : 0; -#ifdef USE_BUFFEREVENTS - { - uint64_t cur_read,cur_written; - connection_get_rate_limit_totals(&cur_read, &cur_written); - bytes_written = (size_t)(cur_written - stats_prev_n_written); - bytes_read = (size_t)(cur_read - stats_prev_n_read); - stats_n_bytes_read += bytes_read; - stats_n_bytes_written += bytes_written; - if (accounting_is_enabled(options) && seconds_elapsed >= 0) - accounting_add_bytes(bytes_read, bytes_written, seconds_elapsed); - stats_prev_n_written = cur_written; - stats_prev_n_read = cur_read; - } -#else - bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read); - bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written); - stats_prev_n_read = stats_n_bytes_read; - stats_prev_n_written = stats_n_bytes_written; -#endif - - control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); - control_event_stream_bandwidth_used(); - control_event_conn_bandwidth_used(); - control_event_circ_bandwidth_used(); - control_event_circuit_cell_stats(); - - if (server_mode(options) && - !net_is_disabled() && - seconds_elapsed > 0 && - can_complete_circuit && - stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != - (stats_n_seconds_working+seconds_elapsed) / - TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { - /* every 20 minutes, check and complain if necessary */ - const routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable()) { - log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that " - "its ORPort is reachable. Please check your firewalls, ports, " - "address, /etc/hosts file, etc.", - me->address, me->or_port); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED ORADDRESS=%s:%d", - me->address, me->or_port); - } - - if (me && !check_whether_dirport_reachable()) { - log_warn(LD_CONFIG, - "Your server (%s:%d) has not managed to confirm that its " - "DirPort is reachable. Please check your firewalls, ports, " - "address, /etc/hosts file, etc.", - me->address, me->dir_port); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED DIRADDRESS=%s:%d", - me->address, me->dir_port); - } - } - -/** If more than this many seconds have elapsed, probably the clock - * jumped: doesn't count. */ -#define NUM_JUMPED_SECONDS_BEFORE_WARN 100 - if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN || - seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { - circuit_note_clock_jumped(seconds_elapsed); - /* XXX if the time jumps *back* many months, do our events in - * run_scheduled_events() recover? I don't think they do. -RD */ - } else if (seconds_elapsed > 0) - stats_n_seconds_working += seconds_elapsed; - - run_scheduled_events(now); - - current_second = now; /* remember which second it is, for next time */ -} - -#ifndef USE_BUFFEREVENTS -/** Timer: used to invoke refill_callback(). */ -static periodic_timer_t *refill_timer = NULL; - -/** Libevent callback: invoked periodically to refill token buckets - * and count r/w bytes. It is only used when bufferevents are disabled. */ -static void -refill_callback(periodic_timer_t *timer, void *arg) -{ - static struct timeval current_millisecond; - struct timeval now; - - size_t bytes_written; - size_t bytes_read; - int milliseconds_elapsed = 0; - int seconds_rolled_over = 0; - - const or_options_t *options = get_options(); - - (void)timer; - (void)arg; - - tor_gettimeofday(&now); - - /* If this is our first time, no time has passed. */ - if (current_millisecond.tv_sec) { - long mdiff = tv_mdiff(¤t_millisecond, &now); - if (mdiff > INT_MAX) - mdiff = INT_MAX; - milliseconds_elapsed = (int)mdiff; - seconds_rolled_over = (int)(now.tv_sec - current_millisecond.tv_sec); - } - - bytes_written = stats_prev_global_write_bucket - global_write_bucket; - bytes_read = stats_prev_global_read_bucket - global_read_bucket; - - stats_n_bytes_read += bytes_read; - stats_n_bytes_written += bytes_written; - if (accounting_is_enabled(options) && milliseconds_elapsed >= 0) - accounting_add_bytes(bytes_read, bytes_written, seconds_rolled_over); - - if (milliseconds_elapsed > 0) - connection_bucket_refill(milliseconds_elapsed, now.tv_sec); - - stats_prev_global_read_bucket = global_read_bucket; - stats_prev_global_write_bucket = global_write_bucket; - - current_millisecond = now; /* remember what time it is, for next time */ -} -#endif - -#ifndef _WIN32 -/** Called when a possibly ignorable libevent error occurs; ensures that we - * don't get into an infinite loop by ignoring too many errors from - * libevent. */ -static int -got_libevent_error(void) -{ - if (++n_libevent_errors > 8) { - log_err(LD_NET, "Too many libevent errors in one second; dying"); - return -1; - } - return 0; -} -#endif - -#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60) - -/** Called when our IP address seems to have changed. at_interface - * should be true if we detected a change in our interface, and false if we - * detected a change in our published address. */ -void -ip_address_changed(int at_interface) -{ - int server = server_mode(get_options()); - - if (at_interface) { - if (! server) { - /* Okay, change our keys. */ - if (init_keys()<0) - log_warn(LD_GENERAL, "Unable to rotate keys after IP change!"); - } - } else { - if (server) { - if (stats_n_seconds_working > UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST) - reset_bandwidth_test(); - stats_n_seconds_working = 0; - router_reset_reachability(); - mark_my_descriptor_dirty("IP address changed"); - } - } - - dns_servers_relaunch_checks(); -} - -/** Forget what we've learned about the correctness of our DNS servers, and - * start learning again. */ -void -dns_servers_relaunch_checks(void) -{ - if (server_mode(get_options())) { - dns_reset_correctness_checks(); - time_to_check_for_correct_dns = 0; - } -} - -/** Called when we get a SIGHUP: reload configuration files and keys, - * retry all connections, and so on. */ -static int -do_hup(void) -{ - const or_options_t *options = get_options(); - -#ifdef USE_DMALLOC - dmalloc_log_stats(); - dmalloc_log_changed(0, 1, 0, 0); -#endif - - log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config and " - "resetting internal state."); - if (accounting_is_enabled(options)) - accounting_record_bandwidth_usage(time(NULL), get_or_state()); - - router_reset_warnings(); - routerlist_reset_warnings(); - /* first, reload config variables, in case they've changed */ - if (options->ReloadTorrcOnSIGHUP) { - /* no need to provide argc/v, they've been cached in init_from_config */ - if (options_init_from_torrc(0, NULL) < 0) { - log_err(LD_CONFIG,"Reading config failed--see warnings above. " - "For usage, try -h."); - return -1; - } - options = get_options(); /* they have changed now */ - } else { - char *msg = NULL; - log_notice(LD_GENERAL, "Not reloading config file: the controller told " - "us not to."); - /* Make stuff get rescanned, reloaded, etc. */ - if (set_options((or_options_t*)options, &msg) < 0) { - if (!msg) - msg = tor_strdup("Unknown error"); - log_warn(LD_GENERAL, "Unable to re-set previous options: %s", msg); - tor_free(msg); - } - } - if (authdir_mode_handles_descs(options, -1)) { - /* reload the approved-routers file */ - if (dirserv_load_fingerprint_file() < 0) { - /* warnings are logged from dirserv_load_fingerprint_file() directly */ - log_info(LD_GENERAL, "Error reloading fingerprints. " - "Continuing with old list."); - } - } - - /* Rotate away from the old dirty circuits. This has to be done - * after we've read the new options, but before we start using - * circuits for directory fetches. */ - circuit_mark_all_dirty_circs_as_unusable(); - - /* retry appropriate downloads */ - router_reset_status_download_failures(); - router_reset_descriptor_download_failures(); - if (!options->DisableNetwork) - update_networkstatus_downloads(time(NULL)); - - /* We'll retry routerstatus downloads in about 10 seconds; no need to - * force a retry there. */ - - if (server_mode(options)) { - /* Restart cpuworker and dnsworker processes, so they get up-to-date - * configuration options. */ - cpuworkers_rotate(); - dns_reset(); - } - return 0; -} - -/** Tor main loop. */ -int -do_main_loop(void) -{ - int loop_result; - time_t now; - - /* initialize dns resolve map, spawn workers if needed */ - if (dns_init() < 0) { - if (get_options()->ServerDNSAllowBrokenConfig) - log_warn(LD_GENERAL, "Couldn't set up any working nameservers. " - "Network not up yet? Will try again soon."); - else { - log_err(LD_GENERAL,"Error initializing dns subsystem; exiting. To " - "retry instead, set the ServerDNSAllowBrokenResolvConf option."); - } - } - -#ifdef USE_BUFFEREVENTS - log_warn(LD_GENERAL, "Tor was compiled with the --enable-bufferevents " - "option. This is still experimental, and might cause strange " - "bugs. If you want a more stable Tor, be sure to build without " - "--enable-bufferevents."); -#endif - - handle_signals(1); - - /* load the private keys, if we're supposed to have them, and set up the - * TLS context. */ - if (! client_identity_key_is_set()) { - if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; exiting"); - return -1; - } - } - - /* Set up the packed_cell_t memory pool. */ - init_cell_pool(); - - /* Set up our buckets */ - connection_bucket_init(); -#ifndef USE_BUFFEREVENTS - stats_prev_global_read_bucket = global_read_bucket; - stats_prev_global_write_bucket = global_write_bucket; -#endif - - /* initialize the bootstrap status events to know we're starting up */ - control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); - - if (trusted_dirs_reload_certs()) { - log_warn(LD_DIR, - "Couldn't load all cached v3 certificates. Starting anyway."); - } - if (router_reload_consensus_networkstatus()) { - return -1; - } - /* load the routers file, or assign the defaults. */ - if (router_reload_router_list()) { - return -1; - } - /* load the networkstatuses. (This launches a download for new routers as - * appropriate.) - */ - now = time(NULL); - directory_info_has_arrived(now, 1); - - if (server_mode(get_options())) { - /* launch cpuworkers. Need to do this *after* we've read the onion key. */ - cpu_init(); - } - - /* set up once-a-second callback. */ - if (! second_timer) { - struct timeval one_second; - one_second.tv_sec = 1; - one_second.tv_usec = 0; - - second_timer = periodic_timer_new(tor_libevent_get_base(), - &one_second, - second_elapsed_callback, - NULL); - tor_assert(second_timer); - } - -#ifndef USE_BUFFEREVENTS - if (!refill_timer) { - struct timeval refill_interval; - int msecs = get_options()->TokenBucketRefillInterval; - - refill_interval.tv_sec = msecs/1000; - refill_interval.tv_usec = (msecs%1000)*1000; - - refill_timer = periodic_timer_new(tor_libevent_get_base(), - &refill_interval, - refill_callback, - NULL); - tor_assert(refill_timer); - } -#endif - - for (;;) { - if (nt_service_is_stopping()) - return 0; - - if (check_interrupted()) - return 0; - -#ifndef _WIN32 - /* Make it easier to tell whether libevent failure is our fault or not. */ - errno = 0; -#endif - /* All active linked conns should get their read events activated. */ - SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, - event_active(conn->read_event, EV_READ, 1)); - called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; - - update_approx_time(time(NULL)); - - /* poll until we have an event, or the second ends, or until we have - * some active linked connections to trigger events for. */ - loop_result = event_base_loop(tor_libevent_get_base(), - called_loop_once ? EVLOOP_ONCE : 0); - - /* let catch() handle things like ^c, and otherwise don't worry about it */ - if (loop_result < 0) { - int e = tor_socket_errno(-1); - /* let the program survive things like ^z */ - if (e != EINTR && !ERRNO_IS_EINPROGRESS(e)) { - log_err(LD_NET,"libevent call with %s failed: %s [%d]", - tor_libevent_get_method(), tor_socket_strerror(e), e); - return -1; -#ifndef _WIN32 - } else if (e == EINVAL) { - log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?"); - if (got_libevent_error()) - return -1; -#endif - } else { - if (ERRNO_IS_EINPROGRESS(e)) - log_warn(LD_BUG, - "libevent call returned EINPROGRESS? Please report."); - log_debug(LD_NET,"libevent call interrupted."); - /* You can't trust the results of this poll(). Go back to the - * top of the big for loop. */ - continue; - } - } - } -} - -#ifndef _WIN32 /* Only called when we're willing to use signals */ -/** Libevent callback: invoked when we get a signal. - */ -static void -signal_callback(int fd, short events, void *arg) -{ - uintptr_t sig = (uintptr_t)arg; - (void)fd; - (void)events; - - process_signal(sig); -} -#endif - -/** Do the work of acting on a signal received in sig */ -void -process_signal(uintptr_t sig) -{ - switch (sig) - { - case SIGTERM: - log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly."); - tor_cleanup(); - exit(0); - break; - case SIGINT: - if (!server_mode(get_options())) { /* do it now */ - log_notice(LD_GENERAL,"Interrupt: exiting cleanly."); - tor_cleanup(); - exit(0); - } - hibernate_begin_shutdown(); - break; -#ifdef SIGPIPE - case SIGPIPE: - log_debug(LD_GENERAL,"Caught SIGPIPE. Ignoring."); - break; -#endif - case SIGUSR1: - /* prefer to log it at INFO, but make sure we always see it */ - dumpstats(get_min_log_level() 0) ; /* keep reaping until no more - zombies */ - break; -#endif - case SIGNEWNYM: { - time_t now = time(NULL); - if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) { - signewnym_is_pending = 1; - log_notice(LD_CONTROL, - "Rate limiting NEWNYM request: delaying by %d second(s)", - (int)(MAX_SIGNEWNYM_RATE+time_of_last_signewnym-now)); - } else { - signewnym_impl(now); - } - break; - } - case SIGCLEARDNSCACHE: - addressmap_clear_transient(); - control_event_signal(sig); - break; - } -} - -/** Returns Tor's uptime. */ -long -get_uptime(void) -{ - return stats_n_seconds_working; -} - -extern uint64_t rephist_total_alloc; -extern uint32_t rephist_total_num; - -/** - * Write current memory usage information to the log. - */ -static void -dumpmemusage(int severity) -{ - connection_dump_buffer_mem_stats(severity); - tor_log(severity, LD_GENERAL, "In rephist: "U64_FORMAT" used by %d Tors.", - U64_PRINTF_ARG(rephist_total_alloc), rephist_total_num); - dump_routerlist_mem_usage(severity); - dump_cell_pool_usage(severity); - dump_dns_mem_usage(severity); - buf_dump_freelist_sizes(severity); - tor_log_mallinfo(severity); -} - -/** Write all statistics to the log, with log level severity. Called - * in response to a SIGUSR1. */ -static void -dumpstats(int severity) -{ - time_t now = time(NULL); - time_t elapsed; - size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len; - - tor_log(severity, LD_GENERAL, "Dumping stats:"); - - SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { - int i = conn_sl_idx; - tor_log(severity, LD_GENERAL, - "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago", - i, (int)conn->s, conn->type, conn_type_to_string(conn->type), - conn->state, conn_state_to_string(conn->type, conn->state), - (int)(now - conn->timestamp_created)); - if (!connection_is_listener(conn)) { - tor_log(severity,LD_GENERAL, - "Conn %d is to %s:%d.", i, - safe_str_client(conn->address), - conn->port); - tor_log(severity,LD_GENERAL, - "Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)", - i, - (int)connection_get_inbuf_len(conn), - (int)buf_allocation(conn->inbuf), - (int)(now - conn->timestamp_lastread)); - tor_log(severity,LD_GENERAL, - "Conn %d: %d bytes waiting on outbuf " - "(len %d, last written %d secs ago)",i, - (int)connection_get_outbuf_len(conn), - (int)buf_allocation(conn->outbuf), - (int)(now - conn->timestamp_lastwritten)); - if (conn->type == CONN_TYPE_OR) { - or_connection_t *or_conn = TO_OR_CONN(conn); - if (or_conn->tls) { - tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, - &wbuf_cap, &wbuf_len); - tor_log(severity, LD_GENERAL, - "Conn %d: %d/%d bytes used on OpenSSL read buffer; " - "%d/%d bytes used on write buffer.", - i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); - } - } - } - circuit_dump_by_conn(conn, severity); /* dump info about all the circuits - * using this conn */ - } SMARTLIST_FOREACH_END(conn); - - channel_dumpstats(severity); - channel_listener_dumpstats(severity); - - tor_log(severity, LD_NET, - "Cells processed: "U64_FORMAT" padding\n" - " "U64_FORMAT" create\n" - " "U64_FORMAT" created\n" - " "U64_FORMAT" relay\n" - " ("U64_FORMAT" relayed)\n" - " ("U64_FORMAT" delivered)\n" - " "U64_FORMAT" destroy", - U64_PRINTF_ARG(stats_n_padding_cells_processed), - U64_PRINTF_ARG(stats_n_create_cells_processed), - U64_PRINTF_ARG(stats_n_created_cells_processed), - U64_PRINTF_ARG(stats_n_relay_cells_processed), - U64_PRINTF_ARG(stats_n_relay_cells_relayed), - U64_PRINTF_ARG(stats_n_relay_cells_delivered), - U64_PRINTF_ARG(stats_n_destroy_cells_processed)); - if (stats_n_data_cells_packaged) - tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%", - 100*(U64_TO_DBL(stats_n_data_bytes_packaged) / - U64_TO_DBL(stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) ); - if (stats_n_data_cells_received) - tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%", - 100*(U64_TO_DBL(stats_n_data_bytes_received) / - U64_TO_DBL(stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) ); - - cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP"); - cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor"); - - if (now - time_of_process_start >= 0) - elapsed = now - time_of_process_start; - else - elapsed = 0; - - if (elapsed) { - tor_log(severity, LD_NET, - "Average bandwidth: "U64_FORMAT"/%d = %d bytes/sec reading", - U64_PRINTF_ARG(stats_n_bytes_read), - (int)elapsed, - (int) (stats_n_bytes_read/elapsed)); - tor_log(severity, LD_NET, - "Average bandwidth: "U64_FORMAT"/%d = %d bytes/sec writing", - U64_PRINTF_ARG(stats_n_bytes_written), - (int)elapsed, - (int) (stats_n_bytes_written/elapsed)); - } - - tor_log(severity, LD_NET, "--------------- Dumping memory information:"); - dumpmemusage(severity); - - rep_hist_dump_stats(now,severity); - rend_service_dump_stats(severity); - dump_pk_ops(severity); - dump_distinct_digest_count(severity); -} - -/** Called by exit() as we shut down the process. - */ -static void -exit_function(void) -{ - /* NOTE: If we ever daemonize, this gets called immediately. That's - * okay for now, because we only use this on Windows. */ -#ifdef _WIN32 - WSACleanup(); -#endif -} - -/** Set up the signal handlers for either parent or child. */ -void -handle_signals(int is_parent) -{ -#ifndef _WIN32 /* do signal stuff only on Unix */ - int i; - static const int signals[] = { - SIGINT, /* do a controlled slow shutdown */ - SIGTERM, /* to terminate now */ - SIGPIPE, /* otherwise SIGPIPE kills us */ - SIGUSR1, /* dump stats */ - SIGUSR2, /* go to loglevel debug */ - SIGHUP, /* to reload config, retry conns, etc */ -#ifdef SIGXFSZ - SIGXFSZ, /* handle file-too-big resource exhaustion */ -#endif - SIGCHLD, /* handle dns/cpu workers that exit */ - -1 }; - static struct event *signal_events[16]; /* bigger than it has to be. */ - if (is_parent) { - for (i = 0; signals[i] >= 0; ++i) { - signal_events[i] = tor_evsignal_new( - tor_libevent_get_base(), signals[i], signal_callback, - (void*)(uintptr_t)signals[i]); - if (event_add(signal_events[i], NULL)) - log_warn(LD_BUG, "Error from libevent when adding event for signal %d", - signals[i]); - } - } else { - struct sigaction action; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); - action.sa_handler = SIG_IGN; - sigaction(SIGINT, &action, NULL); - sigaction(SIGTERM, &action, NULL); - sigaction(SIGPIPE, &action, NULL); - sigaction(SIGUSR1, &action, NULL); - sigaction(SIGUSR2, &action, NULL); - sigaction(SIGHUP, &action, NULL); -#ifdef SIGXFSZ - sigaction(SIGXFSZ, &action, NULL); -#endif - } -#else /* MS windows */ - (void)is_parent; -#endif /* signal stuff */ -} - -/** Main entry point for the Tor command-line client. - */ -int -tor_init(int argc, char *argv[]) -{ - char progname[256]; - int quiet = 0; - - time_of_process_start = time(NULL); - init_connection_lists(); - /* Have the log set up with our application name. */ - tor_snprintf(progname, sizeof(progname), "Tor %s", get_version()); - log_set_application_name(progname); - /* Initialize the history structures. */ - rep_hist_init(); - /* Initialize the service cache. */ - rend_cache_init(); - addressmap_init(); /* Init the client dns cache. Do it always, since it's - * cheap. */ - - { - /* We search for the "quiet" option first, since it decides whether we - * will log anything at all to the command line. */ - config_line_t *opts = NULL, *cmdline_opts = NULL; - const config_line_t *cl; - (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts); - for (cl = cmdline_opts; cl; cl = cl->next) { - if (!strcmp(cl->key, "--hush")) - quiet = 1; - if (!strcmp(cl->key, "--quiet") || - !strcmp(cl->key, "--dump-config")) - quiet = 2; - /* --version, --digests, and --help imply --hush */ - if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") || - !strcmp(cl->key, "--list-torrc-options") || - !strcmp(cl->key, "--library-versions") || - !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) { - if (quiet < 1) - quiet = 1; - } - } - config_free_lines(opts); - config_free_lines(cmdline_opts); - } - - /* give it somewhere to log to initially */ - switch (quiet) { - case 2: - /* no initial logging */ - break; - case 1: - add_temp_log(LOG_WARN); - break; - default: - add_temp_log(LOG_NOTICE); - } - quiet_level = quiet; - - { - const char *version = get_version(); - const char *bev_str = -#ifdef USE_BUFFEREVENTS - "(with bufferevents) "; -#else - ""; -#endif - log_notice(LD_GENERAL, "Tor v%s %srunning on %s with Libevent %s, " - "OpenSSL %s and Zlib %s.", version, bev_str, - get_uname(), - tor_libevent_get_version_str(), - crypto_openssl_get_version_str(), - tor_zlib_get_version_str()); - - log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! " - "Learn how to be safe at " - "https://www.torproject.org/download/download#warning"); - - if (strstr(version, "alpha") || strstr(version, "beta")) - log_notice(LD_GENERAL, "This version is not a stable Tor release. " - "Expect more bugs than usual."); - } - -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif - - if (network_init()<0) { - log_err(LD_BUG,"Error initializing network; exiting."); - return -1; - } - atexit(exit_function); - - if (options_init_from_torrc(argc,argv) < 0) { - log_err(LD_CONFIG,"Reading config failed--see warnings above."); - return -1; - } - -#ifndef _WIN32 - if (geteuid()==0) - log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, " - "and you probably shouldn't."); -#endif - - if (crypto_global_init(get_options()->HardwareAccel, - get_options()->AccelName, - get_options()->AccelDir)) { - log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); - return -1; - } - stream_choice_seed_weak_rng(); - - return 0; -} - -/** A lockfile structure, used to prevent two Tors from messing with the - * data directory at once. If this variable is non-NULL, we're holding - * the lockfile. */ -static tor_lockfile_t *lockfile = NULL; - -/** Try to grab the lock file described in options, if we do not - * already have it. If err_if_locked is true, warn if somebody else is - * holding the lock, and exit if we can't get it after waiting. Otherwise, - * return -1 if we can't get the lockfile. Return 0 on success. - */ -int -try_locking(const or_options_t *options, int err_if_locked) -{ - if (lockfile) - return 0; - else { - char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL); - int already_locked = 0; - tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); - tor_free(fname); - if (!lf) { - if (err_if_locked && already_locked) { - int r; - log_warn(LD_GENERAL, "It looks like another Tor process is running " - "with the same data directory. Waiting 5 seconds to see " - "if it goes away."); -#ifndef _WIN32 - sleep(5); -#else - Sleep(5000); -#endif - r = try_locking(options, 0); - if (r<0) { - log_err(LD_GENERAL, "No, it's still there. Exiting."); - exit(0); - } - return r; - } - return -1; - } - lockfile = lf; - return 0; - } -} - -/** Return true iff we've successfully acquired the lock file. */ -int -have_lockfile(void) -{ - return lockfile != NULL; -} - -/** If we have successfully acquired the lock file, release it. */ -void -release_lockfile(void) -{ - if (lockfile) { - tor_lockfile_unlock(lockfile); - lockfile = NULL; - } -} - -/** Free all memory that we might have allocated somewhere. - * If postfork, we are a worker process and we want to free - * only the parts of memory that we won't touch. If !postfork, - * Tor is shutting down and we should free everything. - * - * Helps us find the real leaks with dmalloc and the like. Also valgrind - * should then report 0 reachable in its leak report (in an ideal world -- - * in practice libevent, SSL, libc etc never quite free everything). */ -void -tor_free_all(int postfork) -{ - if (!postfork) { - evdns_shutdown(1); - } - geoip_free_all(); - dirvote_free_all(); - routerlist_free_all(); - networkstatus_free_all(); - addressmap_free_all(); - dirserv_free_all(); - rend_service_free_all(); - rend_cache_free_all(); - rend_service_authorization_free_all(); - rep_hist_free_all(); - dns_free_all(); - clear_pending_onions(); - circuit_free_all(); - entry_guards_free_all(); - pt_free_all(); - channel_tls_free_all(); - channel_free_all(); - connection_free_all(); - buf_shrink_freelists(1); - memarea_clear_freelist(); - nodelist_free_all(); - microdesc_free_all(); - ext_orport_free_all(); - control_free_all(); - if (!postfork) { - config_free_all(); - or_state_free_all(); - router_free_all(); - policies_free_all(); - } - free_cell_pool(); - if (!postfork) { - tor_tls_free_all(); - } - /* stuff in onion_main.c */ - - smartlist_free(connection_array); - smartlist_free(closeable_connection_lst); - smartlist_free(active_linked_connection_lst); - periodic_timer_free(second_timer); -#ifndef USE_BUFFEREVENTS - periodic_timer_free(refill_timer); -#endif - - if (!postfork) { - release_lockfile(); - } - /* Stuff in util.c and address.c*/ - if (!postfork) { - escaped(NULL); - esc_router_info(NULL); - logs_free_all(); /* free log strings. do this last so logs keep working. */ - } -} - -/** Do whatever cleanup is necessary before shutting Tor down. */ -void -tor_cleanup(void) -{ - const or_options_t *options = get_options(); - if (options->command == CMD_RUN_TOR) { - time_t now = time(NULL); - /* Remove our pid file. We don't care if there was an error when we - * unlink, nothing we could do about it anyways. */ - if (options->PidFile) - unlink(options->PidFile); - if (options->ControlPortWriteToFile) - unlink(options->ControlPortWriteToFile); - if (accounting_is_enabled(options)) - accounting_record_bandwidth_usage(now, get_or_state()); - or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */ - or_state_save(now); - if (authdir_mode_tests_reachability(options)) - rep_hist_record_mtbf_data(now, 0); - } -#ifdef USE_DMALLOC - dmalloc_log_stats(); -#endif - tor_free_all(0); /* We could move tor_free_all back into the ifdef below - later, if it makes shutdown unacceptably slow. But for - now, leave it here: it's helped us catch bugs in the - past. */ - crypto_global_cleanup(); -#ifdef USE_DMALLOC - dmalloc_log_unfreed(); - dmalloc_shutdown(); -#endif -} - -/** Read/create keys as needed, and echo our fingerprint to stdout. */ -static int -do_list_fingerprint(void) -{ - char buf[FINGERPRINT_LEN+1]; - crypto_pk_t *k; - const char *nickname = get_options()->Nickname; - if (!server_mode(get_options())) { - log_err(LD_GENERAL, - "Clients don't have long-term identity keys. Exiting."); - return -1; - } - tor_assert(nickname); - if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; can't display fingerprint"); - return -1; - } - if (!(k = get_server_identity_key())) { - log_err(LD_GENERAL,"Error: missing identity key."); - return -1; - } - if (crypto_pk_get_fingerprint(k, buf, 1)<0) { - log_err(LD_BUG, "Error computing fingerprint"); - return -1; - } - printf("%s %s\n", nickname, buf); - return 0; -} - -/** Entry point for password hashing: take the desired password from - * the command line, and print its salted hash to stdout. **/ -static void -do_hash_password(void) -{ - - char output[256]; - char key[S2K_SPECIFIER_LEN+DIGEST_LEN]; - - crypto_rand(key, S2K_SPECIFIER_LEN-1); - key[S2K_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */ - secret_to_key(key+S2K_SPECIFIER_LEN, DIGEST_LEN, - get_options()->command_arg, strlen(get_options()->command_arg), - key); - base16_encode(output, sizeof(output), key, sizeof(key)); - printf("16:%s\n",output); -} - -/** Entry point for configuration dumping: write the configuration to - * stdout. */ -static int -do_dump_config(void) -{ - const or_options_t *options = get_options(); - const char *arg = options->command_arg; - int how; - char *opts; - if (!strcmp(arg, "short")) { - how = OPTIONS_DUMP_MINIMAL; - } else if (!strcmp(arg, "non-builtin")) { - how = OPTIONS_DUMP_DEFAULTS; - } else if (!strcmp(arg, "full")) { - how = OPTIONS_DUMP_ALL; - } else { - printf("%s is not a recognized argument to --dump-config. " - "Please select 'short', 'non-builtin', or 'full'", arg); - return -1; - } - - opts = options_dump(options, how); - printf("%s", opts); - tor_free(opts); - - return 0; -} - -#if defined (WINCE) -int -find_flashcard_path(PWCHAR path, size_t size) -{ - WIN32_FIND_DATA d = {0}; - HANDLE h = NULL; - - if (!path) - return -1; - - h = FindFirstFlashCard(&d); - if (h == INVALID_HANDLE_VALUE) - return -1; - - if (wcslen(d.cFileName) == 0) { - FindClose(h); - return -1; - } - - wcsncpy(path,d.cFileName,size); - FindClose(h); - return 0; -} -#endif - -static void -init_addrinfo(void) -{ - char hname[256]; - - // host name to sandbox - gethostname(hname, sizeof(hname)); - sandbox_add_addrinfo(hname); -} - -static sandbox_cfg_t* -sandbox_init_filter(void) -{ - sandbox_cfg_t *cfg = sandbox_cfg_new(); - - sandbox_cfg_allow_openat_filename(&cfg, - get_datadir_fname("cached-status"), 1); - - sandbox_cfg_allow_open_filename_array(&cfg, - get_datadir_fname("cached-certs"), 1, - get_datadir_fname("cached-certs.tmp"), 1, - get_datadir_fname("cached-consensus"), 1, - get_datadir_fname("unverified-consensus"), 1, - get_datadir_fname("unverified-consensus.tmp"), 1, - get_datadir_fname("cached-microdesc-consensus"), 1, - get_datadir_fname("cached-microdesc-consensus.tmp"), 1, - get_datadir_fname("cached-microdescs"), 1, - get_datadir_fname("cached-microdescs.tmp"), 1, - get_datadir_fname("cached-microdescs.new"), 1, - get_datadir_fname("cached-microdescs.new.tmp"), 1, - get_datadir_fname("unverified-microdesc-consensus"), 1, - get_datadir_fname("cached-descriptors"), 1, - get_datadir_fname("cached-descriptors.new"), 1, - get_datadir_fname("cached-descriptors.tmp"), 1, - get_datadir_fname("cached-descriptors.new.tmp"), 1, - get_datadir_fname("cached-descriptors.tmp.tmp"), 1, - get_datadir_fname("cached-extrainfo"), 1, - get_datadir_fname("state.tmp"), 1, - get_datadir_fname("unparseable-desc.tmp"), 1, - get_datadir_fname("unparseable-desc"), 1, - "/dev/srandom", 0, - "/dev/urandom", 0, - "/dev/random", 0, - NULL, 0 - ); - - sandbox_cfg_allow_stat_filename_array(&cfg, - get_datadir_fname(NULL), 1, - get_datadir_fname("lock"), 1, - get_datadir_fname("state"), 1, - get_datadir_fname("router-stability"), 1, - get_datadir_fname("cached-extrainfo.new"), 1, - NULL, 0 - ); - - // orport - if (server_mode(get_options())) { - sandbox_cfg_allow_open_filename_array(&cfg, - get_datadir_fname2("keys", "secret_id_key"), 1, - get_datadir_fname2("keys", "secret_onion_key"), 1, - get_datadir_fname2("keys", "secret_onion_key_ntor"), 1, - get_datadir_fname2("keys", "secret_onion_key_ntor.tmp"), 1, - get_datadir_fname2("keys", "secret_id_key.old"), 1, - get_datadir_fname2("keys", "secret_onion_key.old"), 1, - get_datadir_fname2("keys", "secret_onion_key_ntor.old"), 1, - get_datadir_fname2("keys", "secret_onion_key.tmp"), 1, - get_datadir_fname2("keys", "secret_id_key.tmp"), 1, - get_datadir_fname("fingerprint"), 1, - get_datadir_fname("fingerprint.tmp"), 1, - get_datadir_fname("cached-consensus"), 1, - get_datadir_fname("cached-consensus.tmp"), 1, - "/etc/resolv.conf", 0, - NULL, 0 - ); - - sandbox_cfg_allow_stat_filename_array(&cfg, - get_datadir_fname("keys"), 1, - get_datadir_fname("stats/dirreq-stats"), 1, - NULL, 0 - ); - } - - sandbox_cfg_allow_execve(&cfg, "/usr/local/bin/tor"); - - init_addrinfo(); - - return cfg; -} - -/** Main entry point for the Tor process. Called from main(). */ -/* This function is distinct from main() only so we can link onion_main.c into - * the unittest binary without conflicting with the unittests' main. */ -int -tor_main(int argc, char *argv[]) -{ - int result = 0; -#if defined (WINCE) - WCHAR path [MAX_PATH] = {0}; - WCHAR fullpath [MAX_PATH] = {0}; - PWCHAR p = NULL; - FILE* redir = NULL; - FILE* redirdbg = NULL; - - // this is to facilitate debugging by opening - // a file on a folder shared by the wm emulator. - // if no flashcard (real or emulated) is present, - // log files will be written in the root folder - if (find_flashcard_path(path,MAX_PATH) == -1) { - redir = _wfreopen( L"\\stdout.log", L"w", stdout ); - redirdbg = _wfreopen( L"\\stderr.log", L"w", stderr ); - } else { - swprintf(fullpath,L"\\%s\\tor",path); - CreateDirectory(fullpath,NULL); - - swprintf(fullpath,L"\\%s\\tor\\stdout.log",path); - redir = _wfreopen( fullpath, L"w", stdout ); - - swprintf(fullpath,L"\\%s\\tor\\stderr.log",path); - redirdbg = _wfreopen( fullpath, L"w", stderr ); - } -#endif - -#ifdef _WIN32 - /* Call SetProcessDEPPolicy to permanently enable DEP. - The function will not resolve on earlier versions of Windows, - and failure is not dangerous. */ - HMODULE hMod = GetModuleHandleA("Kernel32.dll"); - if (hMod) { - typedef BOOL (WINAPI *PSETDEP)(DWORD); - PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod, - "SetProcessDEPPolicy"); - if (setdeppolicy) setdeppolicy(1); /* PROCESS_DEP_ENABLE */ - } -#endif - - configure_backtrace_handler(get_version()); - - update_approx_time(time(NULL)); - tor_threads_init(); - init_logging(); -#ifdef USE_DMALLOC - { - /* Instruct OpenSSL to use our internal wrappers for malloc, - realloc and free. */ - int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); - tor_assert(r); - } -#endif -#ifdef NT_SERVICE - { - int done = 0; - result = nt_service_parse_options(argc, argv, &done); - if (done) return result; - } -#endif - if (tor_init(argc, argv)<0) - return -1; - - if (get_options()->Sandbox) { - sandbox_cfg_t* cfg = sandbox_init_filter(); - - if (sandbox_init(cfg)) { - log_err(LD_BUG,"Failed to create syscall sandbox filter"); - return -1; - } - - // registering libevent rng -#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE - evutil_secure_rng_set_urandom_device_file( - (char*) sandbox_intern_string("/dev/urandom")); -#endif - } - - switch (get_options()->command) { - case CMD_RUN_TOR: -#ifdef NT_SERVICE - nt_service_set_state(SERVICE_RUNNING); -#endif - result = do_main_loop(); - break; - case CMD_LIST_FINGERPRINT: - result = do_list_fingerprint(); - break; - case CMD_HASH_PASSWORD: - do_hash_password(); - result = 0; - break; - case CMD_VERIFY_CONFIG: - printf("Configuration was valid\n"); - result = 0; - break; - case CMD_DUMP_CONFIG: - result = do_dump_config(); - break; - case CMD_RUN_UNITTESTS: /* only set by test.c */ - default: - log_warn(LD_BUG,"Illegal command number %d: internal error.", - get_options()->command); - result = -1; - } - tor_cleanup(); - return result; -} - diff --git a/src/tor/onion_main.h b/src/tor/onion_main.h deleted file mode 100644 index 1746ce8..0000000 --- a/src/tor/onion_main.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_main.h - * \brief Header file for onion_main.c. - **/ - -#ifndef TOR_MAIN_H -#define TOR_MAIN_H - -#ifdef __cplusplus -extern "C" { -#endif - -extern int can_complete_circuit; - -int connection_add_impl(connection_t *conn, int is_connecting); -#define connection_add(conn) connection_add_impl((conn), 0) -#define connection_add_connecting(conn) connection_add_impl((conn), 1) -int connection_remove(connection_t *conn); -void connection_unregister_events(connection_t *conn); -int connection_in_array(connection_t *conn); -void add_connection_to_closeable_list(connection_t *conn); -int connection_is_on_closeable_list(connection_t *conn); - -smartlist_t *get_connection_array(void); -uint64_t get_bytes_read(void); -uint64_t get_bytes_written(void); - -/** Bitmask for events that we can turn on and off with - * connection_watch_events. */ -typedef enum watchable_events { - /* Yes, it is intentional that these match Libevent's EV_READ and EV_WRITE */ - READ_EVENT=0x02, /**< We want to know when a connection is readable */ - WRITE_EVENT=0x04 /**< We want to know when a connection is writable */ -} watchable_events_t; -void connection_watch_events(connection_t *conn, watchable_events_t events); -int connection_is_reading(connection_t *conn); -MOCK_DECL(void,connection_stop_reading,(connection_t *conn)); -MOCK_DECL(void,connection_start_reading,(connection_t *conn)); - -int connection_is_writing(connection_t *conn); -MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); -MOCK_DECL(void,connection_start_writing,(connection_t *conn)); - -void connection_stop_reading_from_linked_conn(connection_t *conn); - -void directory_all_unreachable(time_t now); -void directory_info_has_arrived(time_t now, int from_cache); - -void ip_address_changed(int at_interface); -void dns_servers_relaunch_checks(void); - -long get_uptime(void); -unsigned get_signewnym_epoch(void); - -void handle_signals(int is_parent); -void process_signal(uintptr_t sig); - -int try_locking(const or_options_t *options, int err_if_locked); -int have_lockfile(void); -void release_lockfile(void); - -void tor_cleanup(void); -void tor_free_all(int postfork); - -int tor_main(int argc, char *argv[]); - -int do_main_loop(void); -int tor_init(int argc, char **argv); - -#ifdef MAIN_PRIVATE -STATIC void init_connection_lists(void); -STATIC void close_closeable_connections(void); -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/src/tor/onion_ntor.c b/src/tor/onion_ntor.c deleted file mode 100644 index e37ce9e..0000000 --- a/src/tor/onion_ntor.c +++ /dev/null @@ -1,295 +0,0 @@ -/* Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "orconfig.h" - -#include "crypto.h" -#define ONION_NTOR_PRIVATE -#include "onion_ntor.h" -#include "torlog.h" -#include "tor_util.h" - -/** Free storage held in an ntor handshake state. */ -void -ntor_handshake_state_free(ntor_handshake_state_t *state) -{ - if (!state) - return; - memwipe(state, 0, sizeof(*state)); - tor_free(state); -} - -/** Convenience function to represent HMAC_SHA256 as our instantiation of - * ntor's "tweaked hash'. Hash the inp_len bytes at inp into - * a DIGEST256_LEN-byte digest at out, with the hash changing - * depending on the value of tweak. */ -static void -h_tweak(uint8_t *out, - const uint8_t *inp, size_t inp_len, - const char *tweak) -{ - size_t tweak_len = strlen(tweak); - crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len); -} - -/** Wrapper around a set of tweak-values for use with the ntor handshake. */ -typedef struct tweakset_t { - const char *t_mac; - const char *t_key; - const char *t_verify; - const char *m_expand; -} tweakset_t; - -/** The tweaks to be used with our handshake. */ -const tweakset_t proto1_tweaks = { -#define PROTOID "ntor-curve25519-sha256-1" -#define PROTOID_LEN 24 - PROTOID ":mac", - PROTOID ":key_extract", - PROTOID ":verify", - PROTOID ":key_expand" -}; - -/** Convenience macro: copy len bytes from inp to ptr, - * and advance ptr by the number of bytes copied. */ -#define APPEND(ptr, inp, len) \ - STMT_BEGIN { \ - memcpy(ptr, (inp), (len)); \ - ptr += len; \ - } STMT_END - -/** - * Compute the first client-side step of the ntor handshake for communicating - * with a server whose DIGEST_LEN-byte server identity is router_id, - * and whose onion key is router_key. Store the NTOR_ONIONSKIN_LEN-byte - * message in onion_skin_out, and store the handshake state in - * *handshake_state_out. Return 0 on success, -1 on failure. - */ -int -onion_skin_ntor_create(const uint8_t *router_id, - const curve25519_public_key_t *router_key, - ntor_handshake_state_t **handshake_state_out, - uint8_t *onion_skin_out) -{ - ntor_handshake_state_t *state; - uint8_t *op; - - state = tor_malloc_zero(sizeof(ntor_handshake_state_t)); - - memcpy(state->router_id, router_id, DIGEST_LEN); - memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t)); - if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) { - tor_free(state); - return -1; - } - curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x); - - op = onion_skin_out; - APPEND(op, router_id, DIGEST_LEN); - APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN); - APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); - tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN); - - *handshake_state_out = state; - - return 0; -} - -#define SERVER_STR "Server" -#define SERVER_STR_LEN 6 - -#define SECRET_INPUT_LEN (CURVE25519_PUBKEY_LEN * 3 + \ - CURVE25519_OUTPUT_LEN * 2 + \ - DIGEST_LEN + PROTOID_LEN) -#define AUTH_INPUT_LEN (DIGEST256_LEN + DIGEST_LEN + \ - CURVE25519_PUBKEY_LEN*3 + \ - PROTOID_LEN + SERVER_STR_LEN) - -/** - * Perform the server side of an ntor handshake. Given an - * NTOR_ONIONSKIN_LEN-byte message in onion_skin, our own identity - * fingerprint as my_node_id, and an associative array mapping public - * onion keys to curve25519_keypair_t in private_keys, attempt to - * perform the handshake. Use junk_keys if present if the handshake - * indicates an unrecognized public key. Write an NTOR_REPLY_LEN-byte - * message to send back to the client into handshake_reply_out, and - * generate key_out_len bytes of key material in key_out. Return - * 0 on success, -1 on failure. - */ -int -onion_skin_ntor_server_handshake(const uint8_t *onion_skin, - const di_digest256_map_t *private_keys, - const curve25519_keypair_t *junk_keys, - const uint8_t *my_node_id, - uint8_t *handshake_reply_out, - uint8_t *key_out, - size_t key_out_len) -{ - const tweakset_t *T = &proto1_tweaks; - /* Sensitive stack-allocated material. Kept in an anonymous struct to make - * it easy to wipe. */ - struct { - uint8_t secret_input[SECRET_INPUT_LEN]; - uint8_t auth_input[AUTH_INPUT_LEN]; - curve25519_public_key_t pubkey_X; - curve25519_secret_key_t seckey_y; - curve25519_public_key_t pubkey_Y; - uint8_t verify[DIGEST256_LEN]; - } s; - uint8_t *si = s.secret_input, *ai = s.auth_input; - const curve25519_keypair_t *keypair_bB; - int bad; - - /* Decode the onion skin */ - /* XXXX Does this possible early-return business threaten our security? */ - if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN)) - return -1; - /* Note that on key-not-found, we go through with this operation anyway, - * using "junk_keys". This will result in failed authentication, but won't - * leak whether we recognized the key. */ - keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN, - (void*)junk_keys); - if (!keypair_bB) - return -1; - - memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN, - CURVE25519_PUBKEY_LEN); - - /* Make y, Y */ - curve25519_secret_key_generate(&s.seckey_y, 0); - curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y); - - /* NOTE: If we ever use a group other than curve25519, or a different - * representation for its points, we may need to perform different or - * additional checks on X here and on Y in the client handshake, or lose our - * security properties. What checks we need would depend on the properties - * of the group and its representation. - * - * In short: if you use anything other than curve25519, this aspect of the - * code will need to be reconsidered carefully. */ - - /* build secret_input */ - curve25519_handshake(si, &s.seckey_y, &s.pubkey_X); - bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); - si += CURVE25519_OUTPUT_LEN; - curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X); - bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); - si += CURVE25519_OUTPUT_LEN; - - APPEND(si, my_node_id, DIGEST_LEN); - APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, PROTOID, PROTOID_LEN); - tor_assert(si == s.secret_input + sizeof(s.secret_input)); - - /* Compute hashes of secret_input */ - h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify); - - /* Compute auth_input */ - APPEND(ai, s.verify, DIGEST256_LEN); - APPEND(ai, my_node_id, DIGEST_LEN); - APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, PROTOID, PROTOID_LEN); - APPEND(ai, SERVER_STR, SERVER_STR_LEN); - tor_assert(ai == s.auth_input + sizeof(s.auth_input)); - - /* Build the reply */ - memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); - h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN, - s.auth_input, sizeof(s.auth_input), - T->t_mac); - - /* Generate the key material */ - crypto_expand_key_material_rfc5869_sha256( - s.secret_input, sizeof(s.secret_input), - (const uint8_t*)T->t_key, strlen(T->t_key), - (const uint8_t*)T->m_expand, strlen(T->m_expand), - key_out, key_out_len); - - /* Wipe all of our local state */ - memwipe(&s, 0, sizeof(s)); - - return bad ? -1 : 0; -} - -/** - * Perform the final client side of the ntor handshake, using the state in - * handshake_state and the server's NTOR_REPLY_LEN-byte reply in - * handshake_reply. Generate key_out_len bytes of key material - * in key_out. Return 0 on success, -1 on failure. - */ -int -onion_skin_ntor_client_handshake( - const ntor_handshake_state_t *handshake_state, - const uint8_t *handshake_reply, - uint8_t *key_out, - size_t key_out_len) -{ - const tweakset_t *T = &proto1_tweaks; - /* Sensitive stack-allocated material. Kept in an anonymous struct to make - * it easy to wipe. */ - struct { - curve25519_public_key_t pubkey_Y; - uint8_t secret_input[SECRET_INPUT_LEN]; - uint8_t verify[DIGEST256_LEN]; - uint8_t auth_input[AUTH_INPUT_LEN]; - uint8_t auth[DIGEST256_LEN]; - } s; - uint8_t *ai = s.auth_input, *si = s.secret_input; - const uint8_t *auth_candidate; - int bad; - - /* Decode input */ - memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN); - auth_candidate = handshake_reply + CURVE25519_PUBKEY_LEN; - - /* See note in server_handshake above about checking points. The - * circumstances under which we'd need to check Y for membership are - * different than those under which we'd be checking X. */ - - /* Compute secret_input */ - curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y); - bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); - si += CURVE25519_OUTPUT_LEN; - curve25519_handshake(si, &handshake_state->seckey_x, - &handshake_state->pubkey_B); - bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); - si += CURVE25519_OUTPUT_LEN; - APPEND(si, handshake_state->router_id, DIGEST_LEN); - APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); - APPEND(si, PROTOID, PROTOID_LEN); - tor_assert(si == s.secret_input + sizeof(s.secret_input)); - - /* Compute verify from secret_input */ - h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify); - - /* Compute auth_input */ - APPEND(ai, s.verify, DIGEST256_LEN); - APPEND(ai, handshake_state->router_id, DIGEST_LEN); - APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); - APPEND(ai, PROTOID, PROTOID_LEN); - APPEND(ai, SERVER_STR, SERVER_STR_LEN); - tor_assert(ai == s.auth_input + sizeof(s.auth_input)); - - /* Compute auth */ - h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac); - - bad |= tor_memneq(s.auth, auth_candidate, DIGEST256_LEN); - - crypto_expand_key_material_rfc5869_sha256( - s.secret_input, sizeof(s.secret_input), - (const uint8_t*)T->t_key, strlen(T->t_key), - (const uint8_t*)T->m_expand, strlen(T->m_expand), - key_out, key_out_len); - - memwipe(&s, 0, sizeof(s)); - return bad ? -1 : 0; -} - diff --git a/src/tor/onion_ntor.h b/src/tor/onion_ntor.h deleted file mode 100644 index c942e6e..0000000 --- a/src/tor/onion_ntor.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) 2012-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#ifndef TOR_ONION_NTOR_H -#define TOR_ONION_NTOR_H - -#include "torint.h" -#include "crypto_curve25519.h" -#include "di_ops.h" - -/** State to be maintained by a client between sending an ntor onionskin - * and receiving a reply. */ -typedef struct ntor_handshake_state_t ntor_handshake_state_t; - -/** Length of an ntor onionskin, as sent from the client to server. */ -#define NTOR_ONIONSKIN_LEN 84 -/** Length of an ntor reply, as sent from server to client. */ -#define NTOR_REPLY_LEN 64 - -#ifdef CURVE25519_ENABLED -void ntor_handshake_state_free(ntor_handshake_state_t *state); - -int onion_skin_ntor_create(const uint8_t *router_id, - const curve25519_public_key_t *router_key, - ntor_handshake_state_t **handshake_state_out, - uint8_t *onion_skin_out); - -int onion_skin_ntor_server_handshake(const uint8_t *onion_skin, - const di_digest256_map_t *private_keys, - const curve25519_keypair_t *junk_keypair, - const uint8_t *my_node_id, - uint8_t *handshake_reply_out, - uint8_t *key_out, - size_t key_out_len); - -int onion_skin_ntor_client_handshake( - const ntor_handshake_state_t *handshake_state, - const uint8_t *handshake_reply, - uint8_t *key_out, - size_t key_out_len); - -#ifdef ONION_NTOR_PRIVATE - -/** Storage held by a client while waiting for an ntor reply from a server. */ -struct ntor_handshake_state_t { - /** Identity digest of the router we're talking to. */ - uint8_t router_id[DIGEST_LEN]; - /** Onion key of the router we're talking to. */ - curve25519_public_key_t pubkey_B; - - /** - * Short-lived keypair for use with this handshake. - * @{ */ - curve25519_secret_key_t seckey_x; - curve25519_public_key_t pubkey_X; - /** @} */ -}; -#endif - -#endif - -#endif - diff --git a/src/tor/onion_tap.c b/src/tor/onion_tap.c deleted file mode 100644 index 3782e75..0000000 --- a/src/tor/onion_tap.c +++ /dev/null @@ -1,218 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_tap.c - * \brief Functions to implement the original Tor circuit extension handshake - * (a.k.a TAP). - * - * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the - * Security of the Tor Authentication Protocol". (Spoiler: it's secure, but - * its security is kind of fragile and implementation dependent. Never modify - * this implementation without reading and understanding that paper at least.) - **/ - -#include "or.h" -#include "config.h" -#include "onion_tap.h" -#include "rephist.h" - -/*----------------------------------------------------------------------*/ - -/** Given a router's 128 byte public key, - * stores the following in onion_skin_out: - * - [42 bytes] OAEP padding - * - [16 bytes] Symmetric key for encrypting blob past RSA - * - [70 bytes] g^x part 1 (inside the RSA) - * - [58 bytes] g^x part 2 (symmetrically encrypted) - * - * Stores the DH private key into handshake_state_out for later completion - * of the handshake. - * - * The meeting point/cookies and auth are zeroed out for now. - */ -int -onion_skin_TAP_create(crypto_pk_t *dest_router_key, - crypto_dh_t **handshake_state_out, - char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */ -{ - char challenge[DH_KEY_LEN]; - crypto_dh_t *dh = NULL; - int dhbytes, pkbytes; - - tor_assert(dest_router_key); - tor_assert(handshake_state_out); - tor_assert(onion_skin_out); - *handshake_state_out = NULL; - memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN); - - if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT))) - goto err; - - dhbytes = crypto_dh_get_bytes(dh); - pkbytes = (int) crypto_pk_keysize(dest_router_key); - tor_assert(dhbytes == 128); - tor_assert(pkbytes == 128); - - if (crypto_dh_get_public(dh, challenge, dhbytes)) - goto err; - - note_crypto_pk_op(ENC_ONIONSKIN); - - /* set meeting point, meeting cookie, etc here. Leave zero for now. */ - if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out, - TAP_ONIONSKIN_CHALLENGE_LEN, - challenge, DH_KEY_LEN, - PK_PKCS1_OAEP_PADDING, 1)<0) - goto err; - - memwipe(challenge, 0, sizeof(challenge)); - *handshake_state_out = dh; - - return 0; - err: - memwipe(challenge, 0, sizeof(challenge)); - if (dh) crypto_dh_free(dh); - return -1; -} - -/** Given an encrypted DH public key as generated by onion_skin_create, - * and the private key for this onion router, generate the reply (128-byte - * DH plus the first 20 bytes of shared key material), and store the - * next key_out_len bytes of key material in key_out. - */ -int -onion_skin_TAP_server_handshake( - /*TAP_ONIONSKIN_CHALLENGE_LEN*/ - const char *onion_skin, - crypto_pk_t *private_key, - crypto_pk_t *prev_private_key, - /*TAP_ONIONSKIN_REPLY_LEN*/ - char *handshake_reply_out, - char *key_out, - size_t key_out_len) -{ - char challenge[TAP_ONIONSKIN_CHALLENGE_LEN]; - crypto_dh_t *dh = NULL; - ssize_t len; - char *key_material=NULL; - size_t key_material_len=0; - int i; - crypto_pk_t *k; - - len = -1; - for (i=0;i<2;++i) { - k = i==0?private_key:prev_private_key; - if (!k) - break; - note_crypto_pk_op(DEC_ONIONSKIN); - len = crypto_pk_private_hybrid_decrypt(k, challenge, - TAP_ONIONSKIN_CHALLENGE_LEN, - onion_skin, - TAP_ONIONSKIN_CHALLENGE_LEN, - PK_PKCS1_OAEP_PADDING,0); - if (len>0) - break; - } - if (len<0) { - log_info(LD_PROTOCOL, - "Couldn't decrypt onionskin: client may be using old onion key"); - goto err; - } else if (len != DH_KEY_LEN) { - log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld", - (long)len); - goto err; - } - - dh = crypto_dh_new(DH_TYPE_CIRCUIT); - if (!dh) { - log_warn(LD_BUG, "Couldn't allocate DH key"); - goto err; - } - if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) { - log_info(LD_GENERAL, "crypto_dh_get_public failed."); - goto err; - } - - key_material_len = DIGEST_LEN+key_out_len; - key_material = tor_malloc(key_material_len); - len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge, - DH_KEY_LEN, key_material, - key_material_len); - if (len < 0) { - log_info(LD_GENERAL, "crypto_dh_compute_secret failed."); - goto err; - } - - /* send back H(K|0) as proof that we learned K. */ - memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN); - - /* use the rest of the key material for our shared keys, digests, etc */ - memcpy(key_out, key_material+DIGEST_LEN, key_out_len); - - memwipe(challenge, 0, sizeof(challenge)); - memwipe(key_material, 0, key_material_len); - tor_free(key_material); - crypto_dh_free(dh); - return 0; - err: - memwipe(challenge, 0, sizeof(challenge)); - if (key_material) { - memwipe(key_material, 0, key_material_len); - tor_free(key_material); - } - if (dh) crypto_dh_free(dh); - - return -1; -} - -/** Finish the client side of the DH handshake. - * Given the 128 byte DH reply + 20 byte hash as generated by - * onion_skin_server_handshake and the handshake state generated by - * onion_skin_create, verify H(K) with the first 20 bytes of shared - * key material, then generate key_out_len more bytes of shared key - * material and store them in key_out. - * - * After the invocation, call crypto_dh_free on handshake_state. - */ -int -onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state, - const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */ - char *key_out, - size_t key_out_len) -{ - ssize_t len; - char *key_material=NULL; - size_t key_material_len; - tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN); - - key_material_len = DIGEST_LEN + key_out_len; - key_material = tor_malloc(key_material_len); - len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state, - handshake_reply, DH_KEY_LEN, key_material, - key_material_len); - if (len < 0) - goto err; - - if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) { - /* H(K) does *not* match. Something fishy. */ - log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. " - "Bug or attack."); - goto err; - } - - /* use the rest of the key material for our shared keys, digests, etc */ - memcpy(key_out, key_material+DIGEST_LEN, key_out_len); - - memwipe(key_material, 0, key_material_len); - tor_free(key_material); - return 0; - err: - memwipe(key_material, 0, key_material_len); - tor_free(key_material); - return -1; -} - diff --git a/src/tor/onion_tap.h b/src/tor/onion_tap.h deleted file mode 100644 index b978b66..0000000 --- a/src/tor/onion_tap.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file onion_tap.h - * \brief Header file for onion_tap.c. - **/ - -#ifndef TOR_ONION_TAP_H -#define TOR_ONION_TAP_H - -#define TAP_ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\ - CIPHER_KEY_LEN+\ - DH_KEY_LEN) -#define TAP_ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN) - -int onion_skin_TAP_create(crypto_pk_t *router_key, - crypto_dh_t **handshake_state_out, - char *onion_skin_out); - -int onion_skin_TAP_server_handshake(const char *onion_skin, - crypto_pk_t *private_key, - crypto_pk_t *prev_private_key, - char *handshake_reply_out, - char *key_out, - size_t key_out_len); - -int onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state, - const char *handshake_reply, - char *key_out, - size_t key_out_len); - -#endif - diff --git a/src/tor/onion_v3.cpp b/src/tor/onion_v3.cpp index 41f0e14..31757cf 100644 --- a/src/tor/onion_v3.cpp +++ b/src/tor/onion_v3.cpp @@ -1241,7 +1241,7 @@ bool CTorV3Manager::RegisterAsSeederNode(const std::string& onionAddress, int po int announcementsSent = 0; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { try { // Send seeder announcement to connected peers pnode->PushMessage("seeder", onionAddress, port); @@ -1301,7 +1301,7 @@ bool CTorV3Manager::ConnectToOnionPeer(const std::string& onionAddr, int port) // Check if already connected to this peer { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { std::string nodeAddr = pnode->addr.ToString(); if (nodeAddr.find(onionAddr) != std::string::npos) { printf("Already connected to onion peer %s\n", onionAddr.c_str()); @@ -1363,7 +1363,7 @@ std::vector CTorV3Manager::GetOnionPeers() try { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { std::string addr = pnode->addr.ToString(); if (addr.find(".onion") != std::string::npos) { // Include connection status and timing information @@ -1577,7 +1577,7 @@ bool CTorV3Manager::ConnectToSeederNode(const std::string& seederAddress) // Check if already connected { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (pnode->addr.ToString().find(onionAddr) != std::string::npos) { printf("Already connected to seeder %s\n", onionAddr.c_str()); // Update last seen time for this seeder @@ -1887,7 +1887,7 @@ void CTorV3Manager::DiscoverAdditionalPeers() // Request peer lists from connected onion nodes { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { std::string addr = pnode->addr.ToString(); if (addr.find(".onion") != std::string::npos && pnode->fSuccessfullyConnected) { // Request additional peer information @@ -2004,7 +2004,7 @@ void CTorV3Manager::RequestSeederListFromPeers() printf("Requesting seeder lists from connected peers...\n"); LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { // Request seeder list from each connected peer pnode->PushMessage("getseederlist"); } @@ -2017,7 +2017,7 @@ void CTorV3Manager::BroadcastSeederList() std::vector seederList = GetKnownSeederNodes(); LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { // Send our seeder list to requesting peers pnode->PushMessage("seederlist", seederList); } @@ -2075,7 +2075,7 @@ void CTorV3Manager::UpdateSeederReputation(const std::string& seederAddress, boo void CTorV3Manager::RequestSeederListFromPeer(const std::string& peerAddress) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (pnode->addr.ToString().find(peerAddress) != std::string::npos) { try { pnode->PushMessage("getseederlist"); diff --git a/src/tor/onion_v3.h b/src/tor/onion_v3.h index 5c46822..253a5bd 100644 --- a/src/tor/onion_v3.h +++ b/src/tor/onion_v3.h @@ -8,7 +8,6 @@ #include #include #include -#include "compat.h" // Forward declarations class CNode; diff --git a/src/tor/or.h b/src/tor/or.h deleted file mode 100644 index e28f82c..0000000 --- a/src/tor/or.h +++ /dev/null @@ -1,5044 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file or.h - * \brief Master header file for Tor-specific functionality. - **/ - -#ifndef TOR_OR_H -#define TOR_OR_H - -#include "orconfig.h" - -#ifdef __COVERITY__ -/* If we're building for a static analysis, turn on all the off-by-default - * features. */ -#ifndef INSTRUMENT_DOWNLOADS -#define INSTRUMENT_DOWNLOADS 1 -#endif -#endif - -#ifdef _WIN32 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#define WIN32_LEAN_AND_MEAN -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SIGNAL_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_SYS_PARAM_H -#include /* FreeBSD needs this to know what version it is */ -#endif -#include "torint.h" -#ifdef HAVE_SYS_WAIT_H -#include -#endif -#ifdef HAVE_SYS_FCNTL_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_ASSERT_H -#include -#endif -#ifdef HAVE_TIME_H -#include -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#endif - -#ifdef USE_BUFFEREVENTS -#include -#include -#include -#endif - -#include "crypto.h" -#include "tortls.h" -#include "torlog.h" -#include "container.h" -#include "torgzip.h" -#include "address.h" -#include "compat_libevent.h" -#include "ht.h" -#include "replaycache.h" -#include "crypto_curve25519.h" -#include "tor_queue.h" - -/* These signals are defined to help handle_control_signal work. - */ -#ifndef SIGHUP -#define SIGHUP 1 -#endif -#ifndef SIGINT -#define SIGINT 2 -#endif -#ifndef SIGUSR1 -#define SIGUSR1 10 -#endif -#ifndef SIGUSR2 -#define SIGUSR2 12 -#endif -#ifndef SIGTERM -#define SIGTERM 15 -#endif -/* Controller signals start at a high number so we don't - * conflict with system-defined signals. */ -#define SIGNEWNYM 129 -#define SIGCLEARDNSCACHE 130 - -#if (SIZEOF_CELL_T != 0) -/* On Irix, stdlib.h defines a cell_t type, so we need to make sure - * that our stuff always calls cell_t something different. */ -#define cell_t tor_cell_t -#endif - -#ifdef ENABLE_TOR2WEB_MODE -#define NON_ANONYMOUS_MODE_ENABLED 1 -#endif - -/** Length of longest allowable configured nickname. */ -#define MAX_NICKNAME_LEN 19 -/** Length of a router identity encoded as a hexadecimal digest, plus - * possible dollar sign. */ -#define MAX_HEX_NICKNAME_LEN (HEX_DIGEST_LEN+1) -/** Maximum length of verbose router identifier: dollar sign, hex ID digest, - * equal sign or tilde, nickname. */ -#define MAX_VERBOSE_NICKNAME_LEN (1+HEX_DIGEST_LEN+1+MAX_NICKNAME_LEN) - -/** Maximum size, in bytes, for resized buffers. */ -#define MAX_BUF_SIZE ((1<<24)-1) /* 16MB-1 */ -/** Maximum size, in bytes, for any directory object that we've downloaded. */ -#define MAX_DIR_DL_SIZE MAX_BUF_SIZE - -/** For HTTP parsing: Maximum number of bytes we'll accept in the headers - * of an HTTP request or response. */ -#define MAX_HEADERS_SIZE 50000 -/** Maximum size, in bytes, for any directory object that we're accepting - * as an upload. */ -#define MAX_DIR_UL_SIZE MAX_BUF_SIZE - -/** Maximum size, in bytes, of a single router descriptor uploaded to us - * as a directory authority. Caches and clients fetch whatever descriptors - * the authorities tell them to fetch, and don't care about size. */ -#define MAX_DESCRIPTOR_UPLOAD_SIZE 20000 - -/** Maximum size of a single extrainfo document, as above. */ -#define MAX_EXTRAINFO_UPLOAD_SIZE 50000 - -/** How long do we keep DNS cache entries before purging them (regardless of - * their TTL)? */ -#define MAX_DNS_ENTRY_AGE (30*60) -/** How long do we cache/tell clients to cache DNS records when no TTL is - * known? */ -#define DEFAULT_DNS_TTL (30*60) -/** How long can a TTL be before we stop believing it? */ -#define MAX_DNS_TTL (3*60*60) -/** How small can a TTL be before we stop believing it? Provides rudimentary - * pinning. */ -#define MIN_DNS_TTL 60 - -/** How often do we rotate onion keys? */ -#define MIN_ONION_KEY_LIFETIME (7*24*60*60) -/** How often do we rotate TLS contexts? */ -#define MAX_SSL_KEY_LIFETIME_INTERNAL (2*60*60) - -/** How old do we allow a router to get before removing it - * from the router list? In seconds. */ -#define ROUTER_MAX_AGE (60*60*48) -/** How old can a router get before we (as a server) will no longer - * consider it live? In seconds. */ -#define ROUTER_MAX_AGE_TO_PUBLISH (60*60*24) -/** How old do we let a saved descriptor get before force-removing it? */ -#define OLD_ROUTER_DESC_MAX_AGE (60*60*24*5) - -/** Possible rules for generating circuit IDs on an OR connection. */ -typedef enum { - CIRC_ID_TYPE_LOWER=0, /**< Pick from 0..1<<15-1. */ - CIRC_ID_TYPE_HIGHER=1, /**< Pick from 1<<15..1<<16-1. */ - /** The other side of a connection is an OP: never create circuits to it, - * and let it use any circuit ID it wants. */ - CIRC_ID_TYPE_NEITHER=2 -} circ_id_type_t; - -#define CONN_TYPE_MIN_ 3 -/** Type for sockets listening for OR connections. */ -#define CONN_TYPE_OR_LISTENER 3 -/** A bidirectional TLS connection transmitting a sequence of cells. - * May be from an OR to an OR, or from an OP to an OR. */ -#define CONN_TYPE_OR 4 -/** A TCP connection from an onion router to a stream's destination. */ -#define CONN_TYPE_EXIT 5 -/** Type for sockets listening for SOCKS connections. */ -#define CONN_TYPE_AP_LISTENER 6 -/** A SOCKS proxy connection from the user application to the onion - * proxy. */ -#define CONN_TYPE_AP 7 -/** Type for sockets listening for HTTP connections to the directory server. */ -#define CONN_TYPE_DIR_LISTENER 8 -/** Type for HTTP connections to the directory server. */ -#define CONN_TYPE_DIR 9 -/** Connection from the main process to a CPU worker process. */ -#define CONN_TYPE_CPUWORKER 10 -/** Type for listening for connections from user interface process. */ -#define CONN_TYPE_CONTROL_LISTENER 11 -/** Type for connections from user interface process. */ -#define CONN_TYPE_CONTROL 12 -/** Type for sockets listening for transparent connections redirected by pf or - * netfilter. */ -#define CONN_TYPE_AP_TRANS_LISTENER 13 -/** Type for sockets listening for transparent connections redirected by - * natd. */ -#define CONN_TYPE_AP_NATD_LISTENER 14 -/** Type for sockets listening for DNS requests. */ -#define CONN_TYPE_AP_DNS_LISTENER 15 - -/** Type for connections from the Extended ORPort. */ -#define CONN_TYPE_EXT_OR 16 -/** Type for sockets listening for Extended ORPort connections. */ -#define CONN_TYPE_EXT_OR_LISTENER 17 - -#define CONN_TYPE_MAX_ 17 -/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in - * connection_t. */ - -/* Proxy client types */ -#define PROXY_NONE 0 -#define PROXY_CONNECT 1 -#define PROXY_SOCKS4 2 -#define PROXY_SOCKS5 3 -/* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type - * field in or_connection_t */ - -/* Pluggable transport proxy type. Don't use this in or_connection_t, - * instead use the actual underlying proxy type (see above). */ -#define PROXY_PLUGGABLE 4 - -/* Proxy client handshake states */ -/* We use a proxy but we haven't even connected to it yet. */ -#define PROXY_INFANT 1 -/* We use an HTTP proxy and we've sent the CONNECT command. */ -#define PROXY_HTTPS_WANT_CONNECT_OK 2 -/* We use a SOCKS4 proxy and we've sent the CONNECT command. */ -#define PROXY_SOCKS4_WANT_CONNECT_OK 3 -/* We use a SOCKS5 proxy and we try to negotiate without - any authentication . */ -#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 4 -/* We use a SOCKS5 proxy and we try to negotiate with - Username/Password authentication . */ -#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 5 -/* We use a SOCKS5 proxy and we just sent our credentials. */ -#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 6 -/* We use a SOCKS5 proxy and we just sent our CONNECT command. */ -#define PROXY_SOCKS5_WANT_CONNECT_OK 7 -/* We use a proxy and we CONNECTed successfully!. */ -#define PROXY_CONNECTED 8 - -/** True iff x is an edge connection. */ -#define CONN_IS_EDGE(x) \ - ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP) - -/** State for any listener connection. */ -#define LISTENER_STATE_READY 0 - -#define CPUWORKER_STATE_MIN_ 1 -/** State for a connection to a cpuworker process that's idle. */ -#define CPUWORKER_STATE_IDLE 1 -/** State for a connection to a cpuworker process that's processing a - * handshake. */ -#define CPUWORKER_STATE_BUSY_ONION 2 -#define CPUWORKER_STATE_MAX_ 2 - -#define CPUWORKER_TASK_ONION CPUWORKER_STATE_BUSY_ONION -#define CPUWORKER_TASK_SHUTDOWN 255 - -#define OR_CONN_STATE_MIN_ 1 -/** State for a connection to an OR: waiting for connect() to finish. */ -#define OR_CONN_STATE_CONNECTING 1 -/** State for a connection to an OR: waiting for proxy handshake to complete */ -#define OR_CONN_STATE_PROXY_HANDSHAKING 2 -/** State for an OR connection client: SSL is handshaking, not done - * yet. */ -#define OR_CONN_STATE_TLS_HANDSHAKING 3 -/** State for a connection to an OR: We're doing a second SSL handshake for - * renegotiation purposes. (V2 handshake only.) */ -#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4 -/** State for a connection at an OR: We're waiting for the client to - * renegotiate (to indicate a v2 handshake) or send a versions cell (to - * indicate a v3 handshake) */ -#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5 -/** State for an OR connection: We're done with our SSL handshake, we've done - * renegotiation, but we haven't yet negotiated link protocol versions and - * sent a netinfo cell. */ -#define OR_CONN_STATE_OR_HANDSHAKING_V2 6 -/** State for an OR connection: We're done with our SSL handshake, but we - * haven't yet negotiated link protocol versions, done a V3 handshake, and - * sent a netinfo cell. */ -#define OR_CONN_STATE_OR_HANDSHAKING_V3 7 -/** State for an OR connection: Ready to send/receive cells. */ -#define OR_CONN_STATE_OPEN 8 -#define OR_CONN_STATE_MAX_ 8 - -/** States of the Extended ORPort protocol. Be careful before changing - * the numbers: they matter. */ -#define EXT_OR_CONN_STATE_MIN_ 1 -/** Extended ORPort authentication is waiting for the authentication - * type selected by the client. */ -#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1 -/** Extended ORPort authentication is waiting for the client nonce. */ -#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2 -/** Extended ORPort authentication is waiting for the client hash. */ -#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3 -#define EXT_OR_CONN_STATE_AUTH_MAX 3 -/** Authentication finished and the Extended ORPort is now accepting - * traffic. */ -#define EXT_OR_CONN_STATE_OPEN 4 -/** Extended ORPort is flushing its last messages and preparing to - * start accepting OR connections. */ -#define EXT_OR_CONN_STATE_FLUSHING 5 -#define EXT_OR_CONN_STATE_MAX_ 5 - -#define EXIT_CONN_STATE_MIN_ 1 -/** State for an exit connection: waiting for response from DNS farm. */ -#define EXIT_CONN_STATE_RESOLVING 1 -/** State for an exit connection: waiting for connect() to finish. */ -#define EXIT_CONN_STATE_CONNECTING 2 -/** State for an exit connection: open and ready to transmit data. */ -#define EXIT_CONN_STATE_OPEN 3 -/** State for an exit connection: waiting to be removed. */ -#define EXIT_CONN_STATE_RESOLVEFAILED 4 -#define EXIT_CONN_STATE_MAX_ 4 - -/* The AP state values must be disjoint from the EXIT state values. */ -#define AP_CONN_STATE_MIN_ 5 -/** State for a SOCKS connection: waiting for SOCKS request. */ -#define AP_CONN_STATE_SOCKS_WAIT 5 -/** State for a SOCKS connection: got a y.onion URL; waiting to receive - * rendezvous descriptor. */ -#define AP_CONN_STATE_RENDDESC_WAIT 6 -/** The controller will attach this connection to a circuit; it isn't our - * job to do so. */ -#define AP_CONN_STATE_CONTROLLER_WAIT 7 -/** State for a SOCKS connection: waiting for a completed circuit. */ -#define AP_CONN_STATE_CIRCUIT_WAIT 8 -/** State for a SOCKS connection: sent BEGIN, waiting for CONNECTED. */ -#define AP_CONN_STATE_CONNECT_WAIT 9 -/** State for a SOCKS connection: sent RESOLVE, waiting for RESOLVED. */ -#define AP_CONN_STATE_RESOLVE_WAIT 10 -/** State for a SOCKS connection: ready to send and receive. */ -#define AP_CONN_STATE_OPEN 11 -/** State for a transparent natd connection: waiting for original - * destination. */ -#define AP_CONN_STATE_NATD_WAIT 12 -#define AP_CONN_STATE_MAX_ 12 - -/** True iff the AP_CONN_STATE_* value s means that the corresponding - * edge connection is not attached to any circuit. */ -#define AP_CONN_STATE_IS_UNATTACHED(s) \ - ((s) <= AP_CONN_STATE_CIRCUIT_WAIT || (s) == AP_CONN_STATE_NATD_WAIT) - -#define DIR_CONN_STATE_MIN_ 1 -/** State for connection to directory server: waiting for connect(). */ -#define DIR_CONN_STATE_CONNECTING 1 -/** State for connection to directory server: sending HTTP request. */ -#define DIR_CONN_STATE_CLIENT_SENDING 2 -/** State for connection to directory server: reading HTTP response. */ -#define DIR_CONN_STATE_CLIENT_READING 3 -/** State for connection to directory server: happy and finished. */ -#define DIR_CONN_STATE_CLIENT_FINISHED 4 -/** State for connection at directory server: waiting for HTTP request. */ -#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 5 -/** State for connection at directory server: sending HTTP response. */ -#define DIR_CONN_STATE_SERVER_WRITING 6 -#define DIR_CONN_STATE_MAX_ 6 - -/** True iff the purpose of conn means that it's a server-side - * directory connection. */ -#define DIR_CONN_IS_SERVER(conn) ((conn)->purpose == DIR_PURPOSE_SERVER) - -#define CONTROL_CONN_STATE_MIN_ 1 -/** State for a control connection: Authenticated and accepting v1 commands. */ -#define CONTROL_CONN_STATE_OPEN 1 -/** State for a control connection: Waiting for authentication; speaking - * protocol v1. */ -#define CONTROL_CONN_STATE_NEEDAUTH 2 -#define CONTROL_CONN_STATE_MAX_ 2 - -#define DIR_PURPOSE_MIN_ 3 -/** A connection to a directory server: download a rendezvous - * descriptor. */ -#define DIR_PURPOSE_FETCH_RENDDESC 3 -/** A connection to a directory server: set after a rendezvous - * descriptor is downloaded. */ -#define DIR_PURPOSE_HAS_FETCHED_RENDDESC 4 -/** A connection to a directory server: download one or more server - * descriptors. */ -#define DIR_PURPOSE_FETCH_SERVERDESC 6 -/** A connection to a directory server: download one or more extra-info - * documents. */ -#define DIR_PURPOSE_FETCH_EXTRAINFO 7 -/** A connection to a directory server: upload a server descriptor. */ -#define DIR_PURPOSE_UPLOAD_DIR 8 -/** A connection to a directory server: upload a rendezvous - * descriptor. */ -#define DIR_PURPOSE_UPLOAD_RENDDESC 9 -/** A connection to a directory server: upload a v3 networkstatus vote. */ -#define DIR_PURPOSE_UPLOAD_VOTE 10 -/** A connection to a directory server: upload a v3 consensus signature */ -#define DIR_PURPOSE_UPLOAD_SIGNATURES 11 -/** A connection to a directory server: download one or more v3 networkstatus - * votes. */ -#define DIR_PURPOSE_FETCH_STATUS_VOTE 12 -/** A connection to a directory server: download a v3 detached signatures - * object for a consensus. */ -#define DIR_PURPOSE_FETCH_DETACHED_SIGNATURES 13 -/** A connection to a directory server: download a v3 networkstatus - * consensus. */ -#define DIR_PURPOSE_FETCH_CONSENSUS 14 -/** A connection to a directory server: download one or more directory - * authority certificates. */ -#define DIR_PURPOSE_FETCH_CERTIFICATE 15 - -/** Purpose for connection at a directory server. */ -#define DIR_PURPOSE_SERVER 16 -/** A connection to a hidden service directory server: upload a v2 rendezvous - * descriptor. */ -#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 17 -/** A connection to a hidden service directory server: download a v2 rendezvous - * descriptor. */ -#define DIR_PURPOSE_FETCH_RENDDESC_V2 18 -/** A connection to a directory server: download a microdescriptor. */ -#define DIR_PURPOSE_FETCH_MICRODESC 19 -#define DIR_PURPOSE_MAX_ 19 - -/** True iff p is a purpose corresponding to uploading data to a - * directory server. */ -#define DIR_PURPOSE_IS_UPLOAD(p) \ - ((p)==DIR_PURPOSE_UPLOAD_DIR || \ - (p)==DIR_PURPOSE_UPLOAD_RENDDESC || \ - (p)==DIR_PURPOSE_UPLOAD_VOTE || \ - (p)==DIR_PURPOSE_UPLOAD_SIGNATURES) - -#define EXIT_PURPOSE_MIN_ 1 -/** This exit stream wants to do an ordinary connect. */ -#define EXIT_PURPOSE_CONNECT 1 -/** This exit stream wants to do a resolve (either normal or reverse). */ -#define EXIT_PURPOSE_RESOLVE 2 -#define EXIT_PURPOSE_MAX_ 2 - -/* !!!! If any connection purpose is ever over 31, we must grow the type - * field in connection_t. */ - -/** Circuit state: I'm the origin, still haven't done all my handshakes. */ -#define CIRCUIT_STATE_BUILDING 0 -/** Circuit state: Waiting to process the onionskin. */ -#define CIRCUIT_STATE_ONIONSKIN_PENDING 1 -/** Circuit state: I'd like to deliver a create, but my n_chan is still - * connecting. */ -#define CIRCUIT_STATE_CHAN_WAIT 2 -/** Circuit state: onionskin(s) processed, ready to send/receive cells. */ -#define CIRCUIT_STATE_OPEN 3 - -#define CIRCUIT_PURPOSE_MIN_ 1 - -/* these circuits were initiated elsewhere */ -#define CIRCUIT_PURPOSE_OR_MIN_ 1 -/** OR-side circuit purpose: normal circuit, at OR. */ -#define CIRCUIT_PURPOSE_OR 1 -/** OR-side circuit purpose: At OR, from Bob, waiting for intro from Alices. */ -#define CIRCUIT_PURPOSE_INTRO_POINT 2 -/** OR-side circuit purpose: At OR, from Alice, waiting for Bob. */ -#define CIRCUIT_PURPOSE_REND_POINT_WAITING 3 -/** OR-side circuit purpose: At OR, both circuits have this purpose. */ -#define CIRCUIT_PURPOSE_REND_ESTABLISHED 4 -#define CIRCUIT_PURPOSE_OR_MAX_ 4 - -/* these circuits originate at this node */ - -/* here's how circ client-side purposes work: - * normal circuits are C_GENERAL. - * circuits that are c_introducing are either on their way to - * becoming open, or they are open and waiting for a - * suitable rendcirc before they send the intro. - * circuits that are c_introduce_ack_wait have sent the intro, - * but haven't gotten a response yet. - * circuits that are c_establish_rend are either on their way - * to becoming open, or they are open and have sent the - * establish_rendezvous cell but haven't received an ack. - * circuits that are c_rend_ready are open and have received a - * rend ack, but haven't heard from bob yet. if they have a - * buildstate->pending_final_cpath then they're expecting a - * cell from bob, else they're not. - * circuits that are c_rend_ready_intro_acked are open, and - * some intro circ has sent its intro and received an ack. - * circuits that are c_rend_joined are open, have heard from - * bob, and are talking to him. - */ -/** Client-side circuit purpose: Normal circuit, with cpath. */ -#define CIRCUIT_PURPOSE_C_GENERAL 5 -/** Client-side circuit purpose: at Alice, connecting to intro point. */ -#define CIRCUIT_PURPOSE_C_INTRODUCING 6 -/** Client-side circuit purpose: at Alice, sent INTRODUCE1 to intro point, - * waiting for ACK/NAK. */ -#define CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT 7 -/** Client-side circuit purpose: at Alice, introduced and acked, closing. */ -#define CIRCUIT_PURPOSE_C_INTRODUCE_ACKED 8 -/** Client-side circuit purpose: at Alice, waiting for ack. */ -#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 9 -/** Client-side circuit purpose: at Alice, waiting for Bob. */ -#define CIRCUIT_PURPOSE_C_REND_READY 10 -/** Client-side circuit purpose: at Alice, waiting for Bob, INTRODUCE - * has been acknowledged. */ -#define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11 -/** Client-side circuit purpose: at Alice, rendezvous established. */ -#define CIRCUIT_PURPOSE_C_REND_JOINED 12 -/** This circuit is used for build time measurement only */ -#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13 -#define CIRCUIT_PURPOSE_C_MAX_ 13 -/** Hidden-service-side circuit purpose: at Bob, waiting for introductions. */ -#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14 -/** Hidden-service-side circuit purpose: at Bob, successfully established - * intro. */ -#define CIRCUIT_PURPOSE_S_INTRO 15 -/** Hidden-service-side circuit purpose: at Bob, connecting to rend point. */ -#define CIRCUIT_PURPOSE_S_CONNECT_REND 16 -/** Hidden-service-side circuit purpose: at Bob, rendezvous established. */ -#define CIRCUIT_PURPOSE_S_REND_JOINED 17 -/** A testing circuit; not meant to be used for actual traffic. */ -#define CIRCUIT_PURPOSE_TESTING 18 -/** A controller made this circuit and Tor should not use it. */ -#define CIRCUIT_PURPOSE_CONTROLLER 19 -/** This circuit is used for path bias probing only */ -#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 20 -#define CIRCUIT_PURPOSE_MAX_ 20 -/** A catch-all for unrecognized purposes. Currently we don't expect - * to make or see any circuits with this purpose. */ -#define CIRCUIT_PURPOSE_UNKNOWN 255 - -/** True iff the circuit purpose p is for a circuit that - * originated at this node. */ -#define CIRCUIT_PURPOSE_IS_ORIGIN(p) ((p)>CIRCUIT_PURPOSE_OR_MAX_) -/** True iff the circuit purpose p is for a circuit that originated - * here to serve as a client. (Hidden services don't count here.) */ -#define CIRCUIT_PURPOSE_IS_CLIENT(p) \ - ((p)> CIRCUIT_PURPOSE_OR_MAX_ && \ - (p)<=CIRCUIT_PURPOSE_C_MAX_) -/** True iff the circuit_t c is actually an origin_circuit_t. */ -#define CIRCUIT_IS_ORIGIN(c) (CIRCUIT_PURPOSE_IS_ORIGIN((c)->purpose)) -/** True iff the circuit purpose p is for an established rendezvous - * circuit. */ -#define CIRCUIT_PURPOSE_IS_ESTABLISHED_REND(p) \ - ((p) == CIRCUIT_PURPOSE_C_REND_JOINED || \ - (p) == CIRCUIT_PURPOSE_S_REND_JOINED) -/** True iff the circuit_t c is actually an or_circuit_t */ -#define CIRCUIT_IS_ORCIRC(c) (((circuit_t *)(c))->magic == OR_CIRCUIT_MAGIC) - -/** How many circuits do we want simultaneously in-progress to handle - * a given stream? */ -#define MIN_CIRCUITS_HANDLING_STREAM 2 - -/* These RELAY_COMMAND constants define values for relay cell commands, and -* must match those defined in tor-spec.txt. */ -#define RELAY_COMMAND_BEGIN 1 -#define RELAY_COMMAND_DATA 2 -#define RELAY_COMMAND_END 3 -#define RELAY_COMMAND_CONNECTED 4 -#define RELAY_COMMAND_SENDME 5 -#define RELAY_COMMAND_EXTEND 6 -#define RELAY_COMMAND_EXTENDED 7 -#define RELAY_COMMAND_TRUNCATE 8 -#define RELAY_COMMAND_TRUNCATED 9 -#define RELAY_COMMAND_DROP 10 -#define RELAY_COMMAND_RESOLVE 11 -#define RELAY_COMMAND_RESOLVED 12 -#define RELAY_COMMAND_BEGIN_DIR 13 -#define RELAY_COMMAND_EXTEND2 14 -#define RELAY_COMMAND_EXTENDED2 15 - -#define RELAY_COMMAND_ESTABLISH_INTRO 32 -#define RELAY_COMMAND_ESTABLISH_RENDEZVOUS 33 -#define RELAY_COMMAND_INTRODUCE1 34 -#define RELAY_COMMAND_INTRODUCE2 35 -#define RELAY_COMMAND_RENDEZVOUS1 36 -#define RELAY_COMMAND_RENDEZVOUS2 37 -#define RELAY_COMMAND_INTRO_ESTABLISHED 38 -#define RELAY_COMMAND_RENDEZVOUS_ESTABLISHED 39 -#define RELAY_COMMAND_INTRODUCE_ACK 40 - -/* Reasons why an OR connection is closed. */ -#define END_OR_CONN_REASON_DONE 1 -#define END_OR_CONN_REASON_REFUSED 2 /* connection refused */ -#define END_OR_CONN_REASON_OR_IDENTITY 3 -#define END_OR_CONN_REASON_CONNRESET 4 /* connection reset by peer */ -#define END_OR_CONN_REASON_TIMEOUT 5 -#define END_OR_CONN_REASON_NO_ROUTE 6 /* no route to host/net */ -#define END_OR_CONN_REASON_IO_ERROR 7 /* read/write error */ -#define END_OR_CONN_REASON_RESOURCE_LIMIT 8 /* sockets, buffers, etc */ -#define END_OR_CONN_REASON_MISC 9 - -/* Reasons why we (or a remote OR) might close a stream. See tor-spec.txt for - * documentation of these. The values must match. */ -#define END_STREAM_REASON_MISC 1 -#define END_STREAM_REASON_RESOLVEFAILED 2 -#define END_STREAM_REASON_CONNECTREFUSED 3 -#define END_STREAM_REASON_EXITPOLICY 4 -#define END_STREAM_REASON_DESTROY 5 -#define END_STREAM_REASON_DONE 6 -#define END_STREAM_REASON_TIMEOUT 7 -#define END_STREAM_REASON_NOROUTE 8 -#define END_STREAM_REASON_HIBERNATING 9 -#define END_STREAM_REASON_INTERNAL 10 -#define END_STREAM_REASON_RESOURCELIMIT 11 -#define END_STREAM_REASON_CONNRESET 12 -#define END_STREAM_REASON_TORPROTOCOL 13 -#define END_STREAM_REASON_NOTDIRECTORY 14 -#define END_STREAM_REASON_ENTRYPOLICY 15 - -/* These high-numbered end reasons are not part of the official spec, - * and are not intended to be put in relay end cells. They are here - * to be more informative when sending back socks replies to the - * application. */ -/* XXXX 256 is no longer used; feel free to reuse it. */ -/** We were unable to attach the connection to any circuit at all. */ -/* XXXX the ways we use this one don't make a lot of sense. */ -#define END_STREAM_REASON_CANT_ATTACH 257 -/** We can't connect to any directories at all, so we killed our streams - * before they can time out. */ -#define END_STREAM_REASON_NET_UNREACHABLE 258 -/** This is a SOCKS connection, and the client used (or misused) the SOCKS - * protocol in a way we couldn't handle. */ -#define END_STREAM_REASON_SOCKSPROTOCOL 259 -/** This is a transparent proxy connection, but we can't extract the original - * target address:port. */ -#define END_STREAM_REASON_CANT_FETCH_ORIG_DEST 260 -/** This is a connection on the NATD port, and the destination IP:Port was - * either ill-formed or out-of-range. */ -#define END_STREAM_REASON_INVALID_NATD_DEST 261 -/** The target address is in a private network (like 127.0.0.1 or 10.0.0.1); - * you don't want to do that over a randomly chosen exit */ -#define END_STREAM_REASON_PRIVATE_ADDR 262 - -/** Bitwise-and this value with endreason to mask out all flags. */ -#define END_STREAM_REASON_MASK 511 - -/** Bitwise-or this with the argument to control_event_stream_status - * to indicate that the reason came from an END cell. */ -#define END_STREAM_REASON_FLAG_REMOTE 512 -/** Bitwise-or this with the argument to control_event_stream_status - * to indicate that we already sent a CLOSED stream event. */ -#define END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED 1024 -/** Bitwise-or this with endreason to indicate that we already sent - * a socks reply, and no further reply needs to be sent from - * connection_mark_unattached_ap(). */ -#define END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED 2048 - -/** Reason for remapping an AP connection's address: we have a cached - * answer. */ -#define REMAP_STREAM_SOURCE_CACHE 1 -/** Reason for remapping an AP connection's address: the exit node told us an - * answer. */ -#define REMAP_STREAM_SOURCE_EXIT 2 - -/* 'type' values to use in RESOLVED cells. Specified in tor-spec.txt. */ -#define RESOLVED_TYPE_HOSTNAME 0 -#define RESOLVED_TYPE_IPV4 4 -#define RESOLVED_TYPE_IPV6 6 -#define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0 -#define RESOLVED_TYPE_ERROR 0xF1 - -/* Negative reasons are internal: we never send them in a DESTROY or TRUNCATE - * call; they only go to the controller for tracking */ -/** Our post-timeout circuit time measurement period expired. - * We must give up now */ -#define END_CIRC_REASON_MEASUREMENT_EXPIRED -3 - -/** We couldn't build a path for this circuit. */ -#define END_CIRC_REASON_NOPATH -2 -/** Catch-all "other" reason for closing origin circuits. */ -#define END_CIRC_AT_ORIGIN -1 - -/* Reasons why we (or a remote OR) might close a circuit. See tor-spec.txt for - * documentation of these. */ -#define END_CIRC_REASON_MIN_ 0 -#define END_CIRC_REASON_NONE 0 -#define END_CIRC_REASON_TORPROTOCOL 1 -#define END_CIRC_REASON_INTERNAL 2 -#define END_CIRC_REASON_REQUESTED 3 -#define END_CIRC_REASON_HIBERNATING 4 -#define END_CIRC_REASON_RESOURCELIMIT 5 -#define END_CIRC_REASON_CONNECTFAILED 6 -#define END_CIRC_REASON_OR_IDENTITY 7 -#define END_CIRC_REASON_CHANNEL_CLOSED 8 -#define END_CIRC_REASON_FINISHED 9 -#define END_CIRC_REASON_TIMEOUT 10 -#define END_CIRC_REASON_DESTROYED 11 -#define END_CIRC_REASON_NOSUCHSERVICE 12 -#define END_CIRC_REASON_MAX_ 12 - -/** Bitwise-OR this with the argument to circuit_mark_for_close() or - * control_event_circuit_status() to indicate that the reason was - * passed through from a destroy or truncate cell. */ -#define END_CIRC_REASON_FLAG_REMOTE 512 - -/** Length of 'y' portion of 'y.onion' URL. */ -#define REND_SERVICE_ID_LEN_BASE32 16 - -/** Length of 'y.onion' including '.onion' URL. */ -#define REND_SERVICE_ADDRESS_LEN (16+1+5) - -/** Length of a binary-encoded rendezvous service ID. */ -#define REND_SERVICE_ID_LEN 10 - -/** Time period for which a v2 descriptor will be valid. */ -#define REND_TIME_PERIOD_V2_DESC_VALIDITY (24*60*60) - -/** Time period within which two sets of v2 descriptors will be uploaded in - * parallel. */ -#define REND_TIME_PERIOD_OVERLAPPING_V2_DESCS (60*60) - -/** Number of non-consecutive replicas (i.e. distributed somewhere - * in the ring) for a descriptor. */ -#define REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS 2 - -/** Number of consecutive replicas for a descriptor. */ -#define REND_NUMBER_OF_CONSECUTIVE_REPLICAS 3 - -/** Length of v2 descriptor ID (32 base32 chars = 160 bits). */ -#define REND_DESC_ID_V2_LEN_BASE32 32 - -/** Length of the base32-encoded secret ID part of versioned hidden service - * descriptors. */ -#define REND_SECRET_ID_PART_LEN_BASE32 32 - -/** Length of the base32-encoded hash of an introduction point's - * identity key. */ -#define REND_INTRO_POINT_ID_LEN_BASE32 32 - -/** Length of the descriptor cookie that is used for client authorization - * to hidden services. */ -#define REND_DESC_COOKIE_LEN 16 - -/** Length of the base64-encoded descriptor cookie that is used for - * exchanging client authorization between hidden service and client. */ -#define REND_DESC_COOKIE_LEN_BASE64 22 - -/** Length of client identifier in encrypted introduction points for hidden - * service authorization type 'basic'. */ -#define REND_BASIC_AUTH_CLIENT_ID_LEN 4 - -/** Multiple of the number of clients to which the real number of clients - * is padded with fake clients for hidden service authorization type - * 'basic'. */ -#define REND_BASIC_AUTH_CLIENT_MULTIPLE 16 - -/** Length of client entry consisting of client identifier and encrypted - * session key for hidden service authorization type 'basic'. */ -#define REND_BASIC_AUTH_CLIENT_ENTRY_LEN (REND_BASIC_AUTH_CLIENT_ID_LEN \ - + CIPHER_KEY_LEN) - -/** Maximum size of v2 hidden service descriptors. */ -#define REND_DESC_MAX_SIZE (20 * 1024) - -/** Legal characters for use in authorized client names for a hidden - * service. */ -#define REND_LEGAL_CLIENTNAME_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-_" - -/** Maximum length of authorized client names for a hidden service. */ -#define REND_CLIENTNAME_MAX_LEN 16 - -/** Length of the rendezvous cookie that is used to connect circuits at the - * rendezvous point. */ -#define REND_COOKIE_LEN DIGEST_LEN - -/** Client authorization type that a hidden service performs. */ -typedef enum rend_auth_type_t { - REND_NO_AUTH = 0, - REND_BASIC_AUTH = 1, - REND_STEALTH_AUTH = 2, -} rend_auth_type_t; - -/** Client-side configuration of authorization for a hidden service. */ -typedef struct rend_service_authorization_t { - char descriptor_cookie[REND_DESC_COOKIE_LEN]; - char onion_address[REND_SERVICE_ADDRESS_LEN+1]; - rend_auth_type_t auth_type; -} rend_service_authorization_t; - -/** Client- and server-side data that is used for hidden service connection - * establishment. Not all fields contain data depending on where this struct - * is used. */ -typedef struct rend_data_t { - /** Onion address (without the .onion part) that a client requests. */ - char onion_address[REND_SERVICE_ID_LEN_BASE32+1]; - - /** (Optional) descriptor cookie that is used by a client. */ - char descriptor_cookie[REND_DESC_COOKIE_LEN]; - - /** Authorization type for accessing a service used by a client. */ - rend_auth_type_t auth_type; - - /** Hash of the hidden service's PK used by a service. */ - char rend_pk_digest[DIGEST_LEN]; - - /** Rendezvous cookie used by both, client and service. */ - char rend_cookie[REND_COOKIE_LEN]; -} rend_data_t; - -/** Time interval for tracking replays of DH public keys received in - * INTRODUCE2 cells. Used only to avoid launching multiple - * simultaneous attempts to connect to the same rendezvous point. */ -#define REND_REPLAY_TIME_INTERVAL (5 * 60) - -/** Used to indicate which way a cell is going on a circuit. */ -typedef enum { - CELL_DIRECTION_IN=1, /**< The cell is moving towards the origin. */ - CELL_DIRECTION_OUT=2, /**< The cell is moving away from the origin. */ -} cell_direction_t; - -/** Initial value for both sides of a circuit transmission window when the - * circuit is initialized. Measured in cells. */ -#define CIRCWINDOW_START 1000 -#define CIRCWINDOW_START_MIN 100 -#define CIRCWINDOW_START_MAX 1000 -/** Amount to increment a circuit window when we get a circuit SENDME. */ -#define CIRCWINDOW_INCREMENT 100 -/** Initial value on both sides of a stream transmission window when the - * stream is initialized. Measured in cells. */ -#define STREAMWINDOW_START 500 -/** Amount to increment a stream window when we get a stream SENDME. */ -#define STREAMWINDOW_INCREMENT 50 - -/** Maximum number of queued cells on a circuit for which we are the - * midpoint before we give up and kill it. This must be >= circwindow - * to avoid killing innocent circuits, and >= circwindow*2 to give - * leaky-pipe a chance of working someday. The ORCIRC_MAX_MIDDLE_KILL_THRESH - * ratio controls the margin of error between emitting a warning and - * killing the circuit. - */ -#define ORCIRC_MAX_MIDDLE_CELLS (CIRCWINDOW_START_MAX*2) -/** Ratio of hard (circuit kill) to soft (warning) thresholds for the - * ORCIRC_MAX_MIDDLE_CELLS tests. - */ -#define ORCIRC_MAX_MIDDLE_KILL_THRESH (1.1f) - -/* Cell commands. These values are defined in tor-spec.txt. */ -#define CELL_PADDING 0 -#define CELL_CREATE 1 -#define CELL_CREATED 2 -#define CELL_RELAY 3 -#define CELL_DESTROY 4 -#define CELL_CREATE_FAST 5 -#define CELL_CREATED_FAST 6 -#define CELL_VERSIONS 7 -#define CELL_NETINFO 8 -#define CELL_RELAY_EARLY 9 -#define CELL_CREATE2 10 -#define CELL_CREATED2 11 - -#define CELL_VPADDING 128 -#define CELL_CERTS 129 -#define CELL_AUTH_CHALLENGE 130 -#define CELL_AUTHENTICATE 131 -#define CELL_AUTHORIZE 132 -#define CELL_COMMAND_MAX_ 132 - -/** How long to test reachability before complaining to the user. */ -#define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60) - -/** Legal characters in a nickname. */ -#define LEGAL_NICKNAME_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - -/** Name to use in client TLS certificates if no nickname is given. Once - * Tor 0.1.2.x is obsolete, we can remove this. */ -#define DEFAULT_CLIENT_NICKNAME "client" - -/** Name chosen by routers that don't configure nicknames */ -#define UNNAMED_ROUTER_NICKNAME "Unnamed" - -/** Number of bytes in a SOCKS4 header. */ -#define SOCKS4_NETWORK_LEN 8 - -/* - * Relay payload: - * Relay command [1 byte] - * Recognized [2 bytes] - * Stream ID [2 bytes] - * Partial SHA-1 [4 bytes] - * Length [2 bytes] - * Relay payload [498 bytes] - */ - -/** Number of bytes in a cell, minus cell header. */ -#define CELL_PAYLOAD_SIZE 509 -/** Number of bytes in a cell transmitted over the network, in the longest - * form */ -#define CELL_MAX_NETWORK_SIZE 514 - -/** Maximum length of a header on a variable-length cell. */ -#define VAR_CELL_MAX_HEADER_SIZE 7 - -static int get_cell_network_size(int wide_circ_ids); -static INLINE int get_cell_network_size(int wide_circ_ids) -{ - return wide_circ_ids ? CELL_MAX_NETWORK_SIZE : CELL_MAX_NETWORK_SIZE - 2; -} -static int get_var_cell_header_size(int wide_circ_ids); -static INLINE int get_var_cell_header_size(int wide_circ_ids) -{ - return wide_circ_ids ? VAR_CELL_MAX_HEADER_SIZE : - VAR_CELL_MAX_HEADER_SIZE - 2; -} -static int get_circ_id_size(int wide_circ_ids); -static INLINE int get_circ_id_size(int wide_circ_ids) -{ - return wide_circ_ids ? 4 : 2; -} - -/** Number of bytes in a relay cell's header (not including general cell - * header). */ -#define RELAY_HEADER_SIZE (1+2+2+4+2) -/** Largest number of bytes that can fit in a relay cell payload. */ -#define RELAY_PAYLOAD_SIZE (CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE) - -/** Identifies a circuit on an or_connection */ -typedef uint32_t circid_t; -/** Identifies a stream on a circuit */ -typedef uint16_t streamid_t; - -/* channel_t typedef; struct channel_s is in channel.h */ - -typedef struct channel_s channel_t; - -/* channel_listener_t typedef; struct channel_listener_s is in channel.h */ - -typedef struct channel_listener_s channel_listener_t; - -/* channel states for channel_t */ - -typedef enum { - /* - * Closed state - channel is inactive - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSING - * Permitted transitions to: - * - CHANNEL_STATE_OPENING - */ - CHANNEL_STATE_CLOSED = 0, - /* - * Opening state - channel is trying to connect - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSED - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_OPEN - */ - CHANNEL_STATE_OPENING, - /* - * Open state - channel is active and ready for use - * - * Permitted transitions from: - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPENING - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_MAINT - */ - CHANNEL_STATE_OPEN, - /* - * Maintenance state - channel is temporarily offline for subclass specific - * maintenance activities such as TLS renegotiation. - * - * Permitted transitions from: - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_OPEN - */ - CHANNEL_STATE_MAINT, - /* - * Closing state - channel is shutting down - * - * Permitted transitions from: - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - CHANNEL_STATE_CLOSED, - * - CHANNEL_STATE_ERROR - */ - CHANNEL_STATE_CLOSING, - /* - * Error state - channel has experienced a permanent error - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPENING - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - None - */ - CHANNEL_STATE_ERROR, - /* - * Placeholder for maximum state value - */ - CHANNEL_STATE_LAST -} channel_state_t; - -/* channel listener states for channel_listener_t */ - -typedef enum { - /* - * Closed state - channel listener is inactive - * - * Permitted transitions from: - * - CHANNEL_LISTENER_STATE_CLOSING - * Permitted transitions to: - * - CHANNEL_LISTENER_STATE_LISTENING - */ - CHANNEL_LISTENER_STATE_CLOSED = 0, - /* - * Listening state - channel listener is listening for incoming - * connections - * - * Permitted transitions from: - * - CHANNEL_LISTENER_STATE_CLOSED - * Permitted transitions to: - * - CHANNEL_LISTENER_STATE_CLOSING - * - CHANNEL_LISTENER_STATE_ERROR - */ - CHANNEL_LISTENER_STATE_LISTENING, - /* - * Closing state - channel listener is shutting down - * - * Permitted transitions from: - * - CHANNEL_LISTENER_STATE_LISTENING - * Permitted transitions to: - * - CHANNEL_LISTENER_STATE_CLOSED, - * - CHANNEL_LISTENER_STATE_ERROR - */ - CHANNEL_LISTENER_STATE_CLOSING, - /* - * Error state - channel listener has experienced a permanent error - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_LISTENING - * Permitted transitions to: - * - None - */ - CHANNEL_LISTENER_STATE_ERROR, - /* - * Placeholder for maximum state value - */ - CHANNEL_LISTENER_STATE_LAST -} channel_listener_state_t; - -/* TLS channel stuff */ - -typedef struct channel_tls_s channel_tls_t; - -/* circuitmux_t typedef; struct circuitmux_s is in circuitmux.h */ - -typedef struct circuitmux_s circuitmux_t; - -/** Parsed onion routing cell. All communication between nodes - * is via cells. */ -typedef struct cell_t { - circid_t circ_id; /**< Circuit which received the cell. */ - uint8_t command; /**< Type of the cell: one of CELL_PADDING, CELL_CREATE, - * CELL_DESTROY, etc */ - uint8_t payload[CELL_PAYLOAD_SIZE]; /**< Cell body. */ -} cell_t; - -/** Parsed variable-length onion routing cell. */ -typedef struct var_cell_t { - /** Type of the cell: CELL_VERSIONS, etc. */ - uint8_t command; - /** Circuit thich received the cell */ - circid_t circ_id; - /** Number of bytes actually stored in payload */ - uint16_t payload_len; - /** Payload of this cell */ - uint8_t payload[FLEXIBLE_ARRAY_MEMBER]; -} var_cell_t; - -/** A parsed Extended ORPort message. */ -typedef struct ext_or_cmd_t { - uint16_t cmd; /** Command type */ - uint16_t len; /** Body length */ - char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */ -} ext_or_cmd_t; - -/** A cell as packed for writing to the network. */ -typedef struct packed_cell_t { - /** Next cell queued on this circuit. */ - TOR_SIMPLEQ_ENTRY(packed_cell_t) next; - char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */ - uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high - * bits truncated) when this cell was inserted. */ -} packed_cell_t; - -/* XXXX This next structure may be obsoleted by inserted_time in - * packed_cell_t */ - -/** Number of cells added to a circuit queue including their insertion - * time on 10 millisecond detail; used for buffer statistics. */ -typedef struct insertion_time_elem_t { - struct insertion_time_elem_t *next; /**< Next element in queue. */ - uint32_t insertion_time; /**< When were cells inserted (in 10 ms steps - * starting at 0:00 of the current day)? */ - unsigned counter; /**< How many cells were inserted? */ -} insertion_time_elem_t; - -/** Queue of insertion times. */ -typedef struct insertion_time_queue_t { - struct insertion_time_elem_t *first; /**< First element in queue. */ - struct insertion_time_elem_t *last; /**< Last element in queue. */ -} insertion_time_queue_t; - -/** Number of cells with the same command consecutively added to a circuit - * queue; used for cell statistics only if CELL_STATS events are enabled. */ -typedef struct insertion_command_elem_t { - struct insertion_command_elem_t *next; /**< Next element in queue. */ - /** Which command did these consecutively added cells have? */ - uint8_t command; - unsigned counter; /**< How many cells were inserted? */ -} insertion_command_elem_t; - -/** Queue of insertion commands. */ -typedef struct insertion_command_queue_t { - struct insertion_command_elem_t *first; /**< First element in queue. */ - struct insertion_command_elem_t *last; /**< Last element in queue. */ -} insertion_command_queue_t; - -/** A queue of cells on a circuit, waiting to be added to the - * or_connection_t's outbuf. */ -typedef struct cell_queue_t { - /** Linked list of packed_cell_t*/ - TOR_SIMPLEQ_HEAD(cell_simpleq, packed_cell_t) head; - int n; /**< The number of cells in the queue. */ - insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */ - /** Commands of inserted cells. */ - insertion_command_queue_t *insertion_commands; -} cell_queue_t; - -/** Beginning of a RELAY cell payload. */ -typedef struct { - uint8_t command; /**< The end-to-end relay command. */ - uint16_t recognized; /**< Used to tell whether cell is for us. */ - streamid_t stream_id; /**< Which stream is this cell associated with? */ - char integrity[4]; /**< Used to tell whether cell is corrupted. */ - uint16_t length; /**< How long is the payload body? */ -} relay_header_t; - -typedef struct buf_t buf_t; -typedef struct socks_request_t socks_request_t; -#ifdef USE_BUFFEREVENTS -#define generic_buffer_t struct evbuffer -#else -#define generic_buffer_t buf_t -#endif - -/* Values for connection_t.magic: used to make sure that downcasts (casts from -* connection_t to foo_connection_t) are safe. */ -#define BASE_CONNECTION_MAGIC 0x7C3C304Eu -#define OR_CONNECTION_MAGIC 0x7D31FF03u -#define EDGE_CONNECTION_MAGIC 0xF0374013u -#define ENTRY_CONNECTION_MAGIC 0xbb4a5703 -#define DIR_CONNECTION_MAGIC 0x9988ffeeu -#define CONTROL_CONNECTION_MAGIC 0x8abc765du -#define LISTENER_CONNECTION_MAGIC 0x1a1ac741u - -/** Description of a connection to another host or process, and associated - * data. - * - * A connection is named based on what it's connected to -- an "OR - * connection" has a Tor node on the other end, an "exit - * connection" has a website or other server on the other end, and an - * "AP connection" has an application proxy (and thus a user) on the - * other end. - * - * Every connection has a type and a state. Connections never change - * their type, but can go through many state changes in their lifetime. - * - * Every connection has two associated input and output buffers. - * Listeners don't use them. For non-listener connections, incoming - * data is appended to conn->inbuf, and outgoing data is taken from - * conn->outbuf. Connections differ primarily in the functions called - * to fill and drain these buffers. - */ -typedef struct connection_t { - uint32_t magic; /**< For memory debugging: must equal one of - * *_CONNECTION_MAGIC. */ - - uint8_t state; /**< Current state of this connection. */ - unsigned int type:5; /**< What kind of connection is this? */ - unsigned int purpose:5; /**< Only used for DIR and EXIT types currently. */ - - /* The next fields are all one-bit booleans. Some are only applicable to - * connection subtypes, but we hold them here anyway, to save space. - */ - unsigned int read_blocked_on_bw:1; /**< Boolean: should we start reading - * again once the bandwidth throttler allows it? */ - unsigned int write_blocked_on_bw:1; /**< Boolean: should we start writing - * again once the bandwidth throttler allows - * writes? */ - unsigned int hold_open_until_flushed:1; /**< Despite this connection's being - * marked for close, do we flush it - * before closing it? */ - unsigned int inbuf_reached_eof:1; /**< Boolean: did read() return 0 on this - * conn? */ - /** Set to 1 when we're inside connection_flushed_some to keep us from - * calling connection_handle_write() recursively. */ - unsigned int in_flushed_some:1; - /** True if connection_handle_write is currently running on this connection. - */ - unsigned int in_connection_handle_write:1; - - /* For linked connections: - */ - unsigned int linked:1; /**< True if there is, or has been, a linked_conn. */ - /** True iff we'd like to be notified about read events from the - * linked conn. */ - unsigned int reading_from_linked_conn:1; - /** True iff we're willing to write to the linked conn. */ - unsigned int writing_to_linked_conn:1; - /** True iff we're currently able to read on the linked conn, and our - * read_event should be made active with libevent. */ - unsigned int active_on_link:1; - /** True iff we've called connection_close_immediate() on this linked - * connection. */ - unsigned int linked_conn_is_closed:1; - - /** CONNECT/SOCKS proxy client handshake state (for outgoing connections). */ - unsigned int proxy_state:4; - - /** Our socket; set to TOR_INVALID_SOCKET if this connection is closed, - * or has no socket. */ - tor_socket_t s; - int conn_array_index; /**< Index into the global connection array. */ - - struct event *read_event; /**< Libevent event structure. */ - struct event *write_event; /**< Libevent event structure. */ - buf_t *inbuf; /**< Buffer holding data read over this connection. */ - buf_t *outbuf; /**< Buffer holding data to write over this connection. */ - size_t outbuf_flushlen; /**< How much data should we try to flush from the - * outbuf? */ - time_t timestamp_lastread; /**< When was the last time libevent said we could - * read? */ - time_t timestamp_lastwritten; /**< When was the last time libevent said we - * could write? */ - -#ifdef USE_BUFFEREVENTS - struct bufferevent *bufev; /**< A Libevent buffered IO structure. */ -#endif - - time_t timestamp_created; /**< When was this connection_t created? */ - - /* XXXX_IP6 make this IPv6-capable */ - int socket_family; /**< Address family of this connection's socket. Usually - * AF_INET, but it can also be AF_UNIX, or in the future - * AF_INET6 */ - tor_addr_t addr; /**< IP of the other side of the connection; used to - * identify routers, along with port. */ - uint16_t port; /**< If non-zero, port on the other end - * of the connection. */ - uint16_t marked_for_close; /**< Should we close this conn on the next - * iteration of the main loop? (If true, holds - * the line number where this connection was - * marked.) */ - const char *marked_for_close_file; /**< For debugging: in which file were - * we marked for close? */ - char *address; /**< FQDN (or IP) of the guy on the other end. - * strdup into this, because free_connection() frees it. */ - /** Another connection that's connected to this one in lieu of a socket. */ - struct connection_t *linked_conn; - - /** Unique identifier for this connection on this Tor instance. */ - uint64_t global_identifier; - - /** Bytes read since last call to control_event_conn_bandwidth_used(). - * Only used if we're configured to emit CONN_BW events. */ - uint32_t n_read_conn_bw; - - /** Bytes written since last call to control_event_conn_bandwidth_used(). - * Only used if we're configured to emit CONN_BW events. */ - uint32_t n_written_conn_bw; -} connection_t; - -/** Subtype of connection_t; used for a listener socket. */ -typedef struct listener_connection_t { - connection_t base_; - - /** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points - * to the evdns_server_port it uses to listen to and answer connections. */ - struct evdns_server_port *dns_server_port; - - /** @name Isolation parameters - * - * For an AP listener, these fields describe how to isolate streams that - * arrive on the listener. - * - * @{ - */ - /** The session group for this listener. */ - int session_group; - /** One or more ISO_ flags to describe how to isolate streams. */ - uint8_t isolation_flags; - /**@}*/ - /** For SOCKS connections only: If this is set, we will choose "no - * authentication" instead of "username/password" authentication if both - * are offered. Used as input to parse_socks. */ - unsigned int socks_prefer_no_auth : 1; - - /** For a SOCKS listeners, these fields describe whether we should - * allow IPv4 and IPv6 addresses from our exit nodes, respectively. - * - * @{ - */ - unsigned int socks_ipv4_traffic : 1; - unsigned int socks_ipv6_traffic : 1; - /** @} */ - /** For a socks listener: should we tell the exit that we prefer IPv6 - * addresses? */ - unsigned int socks_prefer_ipv6 : 1; - - /** For a socks listener: should we cache IPv4/IPv6 DNS information that - * exit nodes tell us? - * - * @{ */ - unsigned int cache_ipv4_answers : 1; - unsigned int cache_ipv6_answers : 1; - /** @} */ - /** For a socks listeners: if we find an answer in our client-side DNS cache, - * should we use it? - * - * @{ */ - unsigned int use_cached_ipv4_answers : 1; - unsigned int use_cached_ipv6_answers : 1; - /** @} */ - /** For socks listeners: When we can automap an address to IPv4 or IPv6, - * do we prefer IPv6? */ - unsigned int prefer_ipv6_virtaddr : 1; - -} listener_connection_t; - -/** Minimum length of the random part of an AUTH_CHALLENGE cell. */ -#define OR_AUTH_CHALLENGE_LEN 32 - -/** - * @name Certificate types for CERTS cells. - * - * These values are defined by the protocol, and affect how an X509 - * certificate in a CERTS cell is interpreted and used. - * - * @{ */ -/** A certificate that authenticates a TLS link key. The subject key - * must match the key used in the TLS handshake; it must be signed by - * the identity key. */ -#define OR_CERT_TYPE_TLS_LINK 1 -/** A self-signed identity certificate. The subject key must be a - * 1024-bit RSA key. */ -#define OR_CERT_TYPE_ID_1024 2 -/** A certificate that authenticates a key used in an AUTHENTICATE cell - * in the v3 handshake. The subject key must be a 1024-bit RSA key; it - * must be signed by the identity key */ -#define OR_CERT_TYPE_AUTH_1024 3 -/**@}*/ - -/** The one currently supported type of AUTHENTICATE cell. It contains - * a bunch of structures signed with an RSA1024 key. The signed - * structures include a HMAC using negotiated TLS secrets, and a digest - * of all cells sent or received before the AUTHENTICATE cell (including - * the random server-generated AUTH_CHALLENGE cell). - */ -#define AUTHTYPE_RSA_SHA256_TLSSECRET 1 - -/** The length of the part of the AUTHENTICATE cell body that the client and - * server can generate independently (when using RSA_SHA256_TLSSECRET). It - * contains everything except the client's timestamp, the client's randomly - * generated nonce, and the signature. */ -#define V3_AUTH_FIXED_PART_LEN (8+(32*6)) -/** The length of the part of the AUTHENTICATE cell body that the client - * signs. */ -#define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16) - -/** Stores flags and information related to the portion of a v2/v3 Tor OR - * connection handshake that happens after the TLS handshake is finished. - */ -typedef struct or_handshake_state_t { - /** When was the VERSIONS cell sent on this connection? Used to get - * an estimate of the skew in the returning NETINFO reply. */ - time_t sent_versions_at; - /** True iff we originated this connection */ - unsigned int started_here : 1; - /** True iff we have received and processed a VERSIONS cell. */ - unsigned int received_versions : 1; - /** True iff we have received and processed an AUTH_CHALLENGE cell */ - unsigned int received_auth_challenge : 1; - /** True iff we have received and processed a CERTS cell. */ - unsigned int received_certs_cell : 1; - /** True iff we have received and processed an AUTHENTICATE cell */ - unsigned int received_authenticate : 1; - - /* True iff we've received valid authentication to some identity. */ - unsigned int authenticated : 1; - - /* True iff we have sent a netinfo cell */ - unsigned int sent_netinfo : 1; - - /** True iff we should feed outgoing cells into digest_sent and - * digest_received respectively. - * - * From the server's side of the v3 handshake, we want to capture everything - * from the VERSIONS cell through and including the AUTH_CHALLENGE cell. - * From the client's, we want to capture everything from the VERSIONS cell - * through but *not* including the AUTHENTICATE cell. - * - * @{ */ - unsigned int digest_sent_data : 1; - unsigned int digest_received_data : 1; - /**@}*/ - - /** Identity digest that we have received and authenticated for our peer - * on this connection. */ - uint8_t authenticated_peer_id[DIGEST_LEN]; - - /** Digests of the cells that we have sent or received as part of a V3 - * handshake. Used for making and checking AUTHENTICATE cells. - * - * @{ - */ - crypto_digest_t *digest_sent; - crypto_digest_t *digest_received; - /** @} */ - - /** Certificates that a connection initiator sent us in a CERTS cell; we're - * holding on to them until we get an AUTHENTICATE cell. - * - * @{ - */ - /** The cert for the key that's supposed to sign the AUTHENTICATE cell */ - tor_cert_t *auth_cert; - /** A self-signed identity certificate */ - tor_cert_t *id_cert; - /**@}*/ -} or_handshake_state_t; - -/** Length of Extended ORPort connection identifier. */ -#define EXT_OR_CONN_ID_LEN DIGEST_LEN /* 20 */ - -/** Subtype of connection_t for an "OR connection" -- that is, one that speaks - * cells over TLS. */ -typedef struct or_connection_t { - connection_t base_; - - /** Hash of the public RSA key for the other side's identity key, or zeroes - * if the other side hasn't shown us a valid identity key. */ - char identity_digest[DIGEST_LEN]; - - /** Extended ORPort connection identifier. */ - char *ext_or_conn_id; - /** This is the ClientHash value we expect to receive from the - * client during the Extended ORPort authentication protocol. We - * compute it upon receiving the ClientNoce from the client, and we - * compare it with the acual ClientHash value sent by the - * client. */ - char *ext_or_auth_correct_client_hash; - /** String carrying the name of the pluggable transport - * (e.g. "obfs2") that is obfuscating this connection. If no - * pluggable transports are used, it's NULL. */ - char *ext_or_transport; - - char *nickname; /**< Nickname of OR on other side (if any). */ - - tor_tls_t *tls; /**< TLS connection state. */ - int tls_error; /**< Last tor_tls error code. */ - /** When we last used this conn for any client traffic. If not - * recent, we can rate limit it further. */ - - /* Channel using this connection */ - channel_tls_t *chan; - - tor_addr_t real_addr; /**< The actual address that this connection came from - * or went to. The addr field is prone to - * getting overridden by the address from the router - * descriptor matching identity_digest. */ - - /** Should this connection be used for extending circuits to the server - * matching the identity_digest field? Set to true if we're pretty - * sure we aren't getting MITMed, either because we're connected to an - * address listed in a server descriptor, or because an authenticated - * NETINFO cell listed the address we're connected to as recognized. */ - unsigned int is_canonical:1; - - /** True iff we have decided that the other end of this connection - * is a client. Connections with this flag set should never be used - * to satisfy an EXTEND request. */ - unsigned int is_connection_with_client:1; - /** True iff this is an outgoing connection. */ - unsigned int is_outgoing:1; - unsigned int proxy_type:2; /**< One of PROXY_NONE...PROXY_SOCKS5 */ - unsigned int wide_circ_ids:1; - uint16_t link_proto; /**< What protocol version are we using? 0 for - * "none negotiated yet." */ - - or_handshake_state_t *handshake_state; /**< If we are setting this connection - * up, state information to do so. */ - - time_t timestamp_lastempty; /**< When was the outbuf last completely empty?*/ - time_t timestamp_last_added_nonpadding; /** When did we last add a - * non-padding cell to the outbuf? */ - - /* bandwidth* and *_bucket only used by ORs in OPEN state: */ - int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */ - int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */ -#ifndef USE_BUFFEREVENTS - int read_bucket; /**< When this hits 0, stop receiving. Every second we - * add 'bandwidthrate' to this, capping it at - * bandwidthburst. (OPEN ORs only) */ - int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */ -#else - /** A rate-limiting configuration object to determine how this connection - * set its read- and write- limits. */ - /* XXXX we could share this among all connections. */ - struct ev_token_bucket_cfg *bucket_cfg; -#endif - - struct or_connection_t *next_with_same_id; /**< Next connection with same - * identity digest as this one. */ - /** Last emptied read token bucket in msec since midnight; only used if - * TB_EMPTY events are enabled. */ - uint32_t read_emptied_time; - /** Last emptied write token bucket in msec since midnight; only used if - * TB_EMPTY events are enabled. */ - uint32_t write_emptied_time; -} or_connection_t; - -/** Subtype of connection_t for an "edge connection" -- that is, an entry (ap) - * connection, or an exit. */ -typedef struct edge_connection_t { - connection_t base_; - - struct edge_connection_t *next_stream; /**< Points to the next stream at this - * edge, if any */ - int package_window; /**< How many more relay cells can I send into the - * circuit? */ - int deliver_window; /**< How many more relay cells can end at me? */ - - struct circuit_t *on_circuit; /**< The circuit (if any) that this edge - * connection is using. */ - - /** A pointer to which node in the circ this conn exits at. Set for AP - * connections and for hidden service exit connections. */ - struct crypt_path_t *cpath_layer; - /** What rendezvous service are we querying for (if an AP) or providing (if - * an exit)? */ - rend_data_t *rend_data; - - uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit - * connection. Exit connections only. */ - uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell - * for this connection */ - - streamid_t stream_id; /**< The stream ID used for this edge connection on its - * circuit */ - - /** The reason why this connection is closing; passed to the controller. */ - uint16_t end_reason; - - /** Bytes read since last call to control_event_stream_bandwidth_used() */ - uint32_t n_read; - - /** Bytes written since last call to control_event_stream_bandwidth_used() */ - uint32_t n_written; - - /** True iff this connection is for a DNS request only. */ - unsigned int is_dns_request:1; - /** True iff this connection is for a PTR DNS request. (exit only) */ - unsigned int is_reverse_dns_lookup:1; - - unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge - * connections. Set once we've set the stream end, - * and check in connection_about_to_close_connection(). - */ - /** True iff we've blocked reading until the circuit has fewer queued - * cells. */ - unsigned int edge_blocked_on_circ:1; - - /** Unique ID for directory requests; this used to be in connection_t, but - * that's going away and being used on channels instead. We still tag - * edge connections with dirreq_id from circuits, so it's copied here. */ - uint64_t dirreq_id; -} edge_connection_t; - -/** Subtype of edge_connection_t for an "entry connection" -- that is, a SOCKS - * connection, a DNS request, a TransPort connection or a NATD connection */ -typedef struct entry_connection_t { - edge_connection_t edge_; - - /** Nickname of planned exit node -- used with .exit support. */ - char *chosen_exit_name; - - socks_request_t *socks_request; /**< SOCKS structure describing request (AP - * only.) */ - - /* === Isolation related, AP only. === */ - /** AP only: based on which factors do we isolate this stream? */ - uint8_t isolation_flags; - /** AP only: what session group is this stream in? */ - int session_group; - /** AP only: The newnym epoch in which we created this connection. */ - unsigned nym_epoch; - /** AP only: The original requested address before we rewrote it. */ - char *original_dest_address; - /* Other fields to isolate on already exist. The ClientAddr is addr. The - ClientProtocol is a combination of type and socks_request-> - socks_version. SocksAuth is socks_request->username/password. - DestAddr is in socks_request->address. */ - - /** Number of times we've reassigned this application connection to - * a new circuit. We keep track because the timeout is longer if we've - * already retried several times. */ - uint8_t num_socks_retries; - - /** For AP connections only: buffer for data that we have sent - * optimistically, which we might need to re-send if we have to - * retry this connection. */ - generic_buffer_t *pending_optimistic_data; - /* For AP connections only: buffer for data that we previously sent - * optimistically which we are currently re-sending as we retry this - * connection. */ - generic_buffer_t *sending_optimistic_data; - - /** If this is a DNSPort connection, this field holds the pending DNS - * request that we're going to try to answer. */ - struct evdns_server_request *dns_server_request; - -#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10 - /** Number of times we've launched a circuit to handle this stream. If - * it gets too high, that could indicate an inconsistency between our - * "launch a circuit to handle this stream" logic and our "attach our - * stream to one of the available circuits" logic. */ - unsigned int num_circuits_launched:4; - - /** True iff this stream must attach to a one-hop circuit (e.g. for - * begin_dir). */ - unsigned int want_onehop:1; - /** True iff this stream should use a BEGIN_DIR relay command to establish - * itself rather than BEGIN (either via onehop or via a whole circuit). */ - unsigned int use_begindir:1; - - /** For AP connections only. If 1, and we fail to reach the chosen exit, - * stop requiring it. */ - unsigned int chosen_exit_optional:1; - /** For AP connections only. If non-zero, this exit node was picked as - * a result of the TrackHostExit, and the value decrements every time - * we fail to complete a circuit to our chosen exit -- if it reaches - * zero, abandon the associated mapaddress. */ - unsigned int chosen_exit_retries:3; - - /** True iff this is an AP connection that came from a transparent or - * NATd connection */ - unsigned int is_transparent_ap:1; - - /** For AP connections only: Set if this connection's target exit node - * allows optimistic data (that is, data sent on this stream before - * the exit has sent a CONNECTED cell) and we have chosen to use it. - */ - unsigned int may_use_optimistic_data : 1; - - /** Should we permit IPv4 and IPv6 traffic to use this connection? - * - * @{ */ - unsigned int ipv4_traffic_ok : 1; - unsigned int ipv6_traffic_ok : 1; - /** @} */ - /** Should we say we prefer IPv6 traffic? */ - unsigned int prefer_ipv6_traffic : 1; - - /** For a socks listener: should we cache IPv4/IPv6 DNS information that - * exit nodes tell us? - * - * @{ */ - unsigned int cache_ipv4_answers : 1; - unsigned int cache_ipv6_answers : 1; - /** @} */ - /** For a socks listeners: if we find an answer in our client-side DNS cache, - * should we use it? - * - * @{ */ - unsigned int use_cached_ipv4_answers : 1; - unsigned int use_cached_ipv6_answers : 1; - /** @} */ - /** For socks listeners: When we can automap an address to IPv4 or IPv6, - * do we prefer IPv6? */ - unsigned int prefer_ipv6_virtaddr : 1; - -} entry_connection_t; - -typedef enum { - DIR_SPOOL_NONE=0, DIR_SPOOL_SERVER_BY_DIGEST, DIR_SPOOL_SERVER_BY_FP, - DIR_SPOOL_EXTRA_BY_DIGEST, DIR_SPOOL_EXTRA_BY_FP, - DIR_SPOOL_CACHED_DIR, DIR_SPOOL_NETWORKSTATUS, - DIR_SPOOL_MICRODESC, /* NOTE: if we add another entry, add another bit. */ -} dir_spool_source_t; - -/** Subtype of connection_t for an "directory connection" -- that is, an HTTP - * connection to retrieve or serve directory material. */ -typedef struct dir_connection_t { - connection_t base_; - - /** Which 'resource' did we ask the directory for? This is typically the part - * of the URL string that defines, relative to the directory conn purpose, - * what thing we want. For example, in router descriptor downloads by - * descriptor digest, it contains "d/", then one ore more +-separated - * fingerprints. - **/ - char *requested_resource; - unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */ - - /* Used only for server sides of some dir connections, to implement - * "spooling" of directory material to the outbuf. Otherwise, we'd have - * to append everything to the outbuf in one enormous chunk. */ - /** What exactly are we spooling right now? */ - ENUM_BF(dir_spool_source_t) dir_spool_src : 3; - - /** If we're fetching descriptors, what router purpose shall we assign - * to them? */ - uint8_t router_purpose; - /** List of fingerprints for networkstatuses or descriptors to be spooled. */ - smartlist_t *fingerprint_stack; - /** A cached_dir_t object that we're currently spooling out */ - struct cached_dir_t *cached_dir; - /** The current offset into cached_dir. */ - off_t cached_dir_offset; - /** The zlib object doing on-the-fly compression for spooled data. */ - tor_zlib_state_t *zlib_state; - - /** What rendezvous service are we querying for? */ - rend_data_t *rend_data; - - char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for - * the directory server's signing key. */ - - /** Unique ID for directory requests; this used to be in connection_t, but - * that's going away and being used on channels instead. The dirserver still - * needs this for the incoming side, so it's moved here. */ - uint64_t dirreq_id; -} dir_connection_t; - -/** Subtype of connection_t for an connection to a controller. */ -typedef struct control_connection_t { - connection_t base_; - - uint32_t event_mask; /**< Bitfield: which events does this controller - * care about? */ - - /** True if we have sent a protocolinfo reply on this connection. */ - unsigned int have_sent_protocolinfo:1; - /** True if we have received a takeownership command on this - * connection. */ - unsigned int is_owning_control_connection:1; - - /** If we have sent an AUTHCHALLENGE reply on this connection and - * have not received a successful AUTHENTICATE command, points to - * the value which the client must send to authenticate itself; - * otherwise, NULL. */ - char *safecookie_client_hash; - - /** Amount of space allocated in incoming_cmd. */ - uint32_t incoming_cmd_len; - /** Number of bytes currently stored in incoming_cmd. */ - uint32_t incoming_cmd_cur_len; - /** A control command that we're reading from the inbuf, but which has not - * yet arrived completely. */ - char *incoming_cmd; -} control_connection_t; - -/** Cast a connection_t subtype pointer to a connection_t **/ -#define TO_CONN(c) (&(((c)->base_))) -/** Helper macro: Given a pointer to to.base_, of type from*, return &to. */ -#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_)) - -/** Cast a entry_connection_t subtype pointer to a edge_connection_t **/ -#define ENTRY_TO_EDGE_CONN(c) (&(((c))->edge_)) -/** Cast a entry_connection_t subtype pointer to a connection_t **/ -#define ENTRY_TO_CONN(c) (TO_CONN(ENTRY_TO_EDGE_CONN(c))) - -/** Convert a connection_t* to an or_connection_t*; assert if the cast is - * invalid. */ -static or_connection_t *TO_OR_CONN(connection_t *); -/** Convert a connection_t* to a dir_connection_t*; assert if the cast is - * invalid. */ -static dir_connection_t *TO_DIR_CONN(connection_t *); -/** Convert a connection_t* to an edge_connection_t*; assert if the cast is - * invalid. */ -static edge_connection_t *TO_EDGE_CONN(connection_t *); -/** Convert a connection_t* to an entry_connection_t*; assert if the cast is - * invalid. */ -static entry_connection_t *TO_ENTRY_CONN(connection_t *); -/** Convert a edge_connection_t* to an entry_connection_t*; assert if the cast - * is invalid. */ -static entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *); -/** Convert a connection_t* to an control_connection_t*; assert if the cast is - * invalid. */ -static control_connection_t *TO_CONTROL_CONN(connection_t *); -/** Convert a connection_t* to an listener_connection_t*; assert if the cast is - * invalid. */ -static listener_connection_t *TO_LISTENER_CONN(connection_t *); - -static INLINE or_connection_t *TO_OR_CONN(connection_t *c) -{ - tor_assert(c->magic == OR_CONNECTION_MAGIC); - return DOWNCAST(or_connection_t, c); -} -static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c) -{ - tor_assert(c->magic == DIR_CONNECTION_MAGIC); - return DOWNCAST(dir_connection_t, c); -} -static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c) -{ - tor_assert(c->magic == EDGE_CONNECTION_MAGIC || - c->magic == ENTRY_CONNECTION_MAGIC); - return DOWNCAST(edge_connection_t, c); -} -static INLINE entry_connection_t *TO_ENTRY_CONN(connection_t *c) -{ - tor_assert(c->magic == ENTRY_CONNECTION_MAGIC); - return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_.base_); -} -static INLINE entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c) -{ - tor_assert(c->base_.magic == ENTRY_CONNECTION_MAGIC); - return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_); -} -static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c) -{ - tor_assert(c->magic == CONTROL_CONNECTION_MAGIC); - return DOWNCAST(control_connection_t, c); -} -static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c) -{ - tor_assert(c->magic == LISTENER_CONNECTION_MAGIC); - return DOWNCAST(listener_connection_t, c); -} - -/* Conditional macros to help write code that works whether bufferevents are - disabled or not. - - We can't just write: - if (conn->bufev) { - do bufferevent stuff; - } else { - do other stuff; - } - because the bufferevent stuff won't even compile unless we have a fairly - new version of Libevent. Instead, we say: - IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } ); - or: - IF_HAS_BUFFEREVENT(conn, { - do bufferevent stuff; - }) ELSE_IF_NO_BUFFEREVENT { - do non-bufferevent stuff; - } - If we're compiling with bufferevent support, then the macros expand more or - less to: - if (conn->bufev) { - do_bufferevent_stuff; - } else { - do non-bufferevent stuff; - } - and if we aren't using bufferevents, they expand more or less to: - { do non-bufferevent stuff; } -*/ -#ifdef USE_BUFFEREVENTS -#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL) -#define IF_HAS_BUFFEREVENT(c, stmt) \ - if ((c)->bufev) do { \ - stmt ; \ - } while (0) -#define ELSE_IF_NO_BUFFEREVENT ; else -#define IF_HAS_NO_BUFFEREVENT(c) \ - if (NULL == (c)->bufev) -#else -#define HAS_BUFFEREVENT(c) (0) -#define IF_HAS_BUFFEREVENT(c, stmt) (void)0 -#define ELSE_IF_NO_BUFFEREVENT ; -#define IF_HAS_NO_BUFFEREVENT(c) \ - if (1) -#endif - -/** What action type does an address policy indicate: accept or reject? */ -typedef enum { - ADDR_POLICY_ACCEPT=1, - ADDR_POLICY_REJECT=2, -} addr_policy_action_t; - -/** A reference-counted address policy rule. */ -typedef struct addr_policy_t { - int refcnt; /**< Reference count */ - /** What to do when the policy matches.*/ - ENUM_BF(addr_policy_action_t) policy_type:2; - unsigned int is_private:1; /**< True iff this is the pseudo-address, - * "private". */ - unsigned int is_canonical:1; /**< True iff this policy is the canonical - * copy (stored in a hash table to avoid - * duplication of common policies) */ - maskbits_t maskbits; /**< Accept/reject all addresses a such that the - * first maskbits bits of a match - * addr. */ - /** Base address to accept or reject. - * - * Note that wildcards are treated - * differntly depending on address family. An AF_UNSPEC address means - * "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means - * "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means - * "All IPv6 addresses". - **/ - tor_addr_t addr; - uint16_t prt_min; /**< Lowest port number to accept/reject. */ - uint16_t prt_max; /**< Highest port number to accept/reject. */ -} addr_policy_t; - -/** A cached_dir_t represents a cacheable directory object, along with its - * compressed form. */ -typedef struct cached_dir_t { - char *dir; /**< Contents of this object, NUL-terminated. */ - char *dir_z; /**< Compressed contents of this object. */ - size_t dir_len; /**< Length of dir (not counting its NUL). */ - size_t dir_z_len; /**< Length of dir_z. */ - time_t published; /**< When was this object published. */ - digests_t digests; /**< Digests of this object (networkstatus only) */ - int refcnt; /**< Reference count for this cached_dir_t. */ -} cached_dir_t; - -/** Enum used to remember where a signed_descriptor_t is stored and how to - * manage the memory for signed_descriptor_body. */ -typedef enum { - /** The descriptor isn't stored on disk at all: the copy in memory is - * canonical; the saved_offset field is meaningless. */ - SAVED_NOWHERE=0, - /** The descriptor is stored in the cached_routers file: the - * signed_descriptor_body is meaningless; the signed_descriptor_len and - * saved_offset are used to index into the mmaped cache file. */ - SAVED_IN_CACHE, - /** The descriptor is stored in the cached_routers.new file: the - * signed_descriptor_body and saved_offset fields are both set. */ - /* FFFF (We could also mmap the file and grow the mmap as needed, or - * lazy-load the descriptor text by using seek and read. We don't, for - * now.) - */ - SAVED_IN_JOURNAL -} saved_location_t; - -/** Enumeration: what kind of download schedule are we using for a given - * object? */ -typedef enum { - DL_SCHED_GENERIC = 0, - DL_SCHED_CONSENSUS = 1, - DL_SCHED_BRIDGE = 2, -} download_schedule_t; - -/** Information about our plans for retrying downloads for a downloadable - * object. */ -typedef struct download_status_t { - time_t next_attempt_at; /**< When should we try downloading this descriptor - * again? */ - uint8_t n_download_failures; /**< Number of failures trying to download the - * most recent descriptor. */ - ENUM_BF(download_schedule_t) schedule : 8; -} download_status_t; - -/** If n_download_failures is this high, the download can never happen. */ -#define IMPOSSIBLE_TO_DOWNLOAD 255 - -/** The max size we expect router descriptor annotations we create to - * be. We'll accept larger ones if we see them on disk, but we won't - * create any that are larger than this. */ -#define ROUTER_ANNOTATION_BUF_LEN 256 - -/** Information need to cache an onion router's descriptor. */ -typedef struct signed_descriptor_t { - /** Pointer to the raw server descriptor, preceded by annotations. Not - * necessarily NUL-terminated. If saved_location is SAVED_IN_CACHE, this - * pointer is null. */ - char *signed_descriptor_body; - /** Length of the annotations preceding the server descriptor. */ - size_t annotations_len; - /** Length of the server descriptor. */ - size_t signed_descriptor_len; - /** Digest of the server descriptor, computed as specified in - * dir-spec.txt. */ - char signed_descriptor_digest[DIGEST_LEN]; - /** Identity digest of the router. */ - char identity_digest[DIGEST_LEN]; - /** Declared publication time of the descriptor. */ - time_t published_on; - /** For routerdescs only: digest of the corresponding extrainfo. */ - char extra_info_digest[DIGEST_LEN]; - /** For routerdescs only: Status of downloading the corresponding - * extrainfo. */ - download_status_t ei_dl_status; - /** Where is the descriptor saved? */ - saved_location_t saved_location; - /** If saved_location is SAVED_IN_CACHE or SAVED_IN_JOURNAL, the offset of - * this descriptor in the corresponding file. */ - off_t saved_offset; - /** What position is this descriptor within routerlist->routers or - * routerlist->old_routers? -1 for none. */ - int routerlist_index; - /** The valid-until time of the most recent consensus that listed this - * descriptor. 0 for "never listed in a consensus, so far as we know." */ - time_t last_listed_as_valid_until; - /* If true, we do not ever try to save this object in the cache. */ - unsigned int do_not_cache : 1; - /* If true, this item is meant to represent an extrainfo. */ - unsigned int is_extrainfo : 1; - /* If true, we got an extrainfo for this item, and the digest was right, - * but it was incompatible. */ - unsigned int extrainfo_is_bogus : 1; - /* If true, we are willing to transmit this item unencrypted. */ - unsigned int send_unencrypted : 1; -} signed_descriptor_t; - -/** A signed integer representing a country code. */ -typedef int16_t country_t; - -/** Information about another onion router in the network. */ -typedef struct { - signed_descriptor_t cache_info; - char *address; /**< Location of OR: either a hostname or an IP address. */ - char *nickname; /**< Human-readable OR name. */ - - uint32_t addr; /**< IPv4 address of OR, in host order. */ - uint16_t or_port; /**< Port for TLS connections. */ - uint16_t dir_port; /**< Port for HTTP directory connections. */ - - /** A router's IPv6 address, if it has one. */ - /* XXXXX187 Actually these should probably be part of a list of addresses, - * not just a special case. Use abstractions to access these; don't do it - * directly. */ - tor_addr_t ipv6_addr; - uint16_t ipv6_orport; - - crypto_pk_t *onion_pkey; /**< Public RSA key for onions. */ - crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */ - /** Public curve25519 key for onions */ - curve25519_public_key_t *onion_curve25519_pkey; - - char *platform; /**< What software/operating system is this OR using? */ - - /* link info */ - uint32_t bandwidthrate; /**< How many bytes does this OR add to its token - * bucket per second? */ - uint32_t bandwidthburst; /**< How large is this OR's token bucket? */ - /** How many bytes/s is this router known to handle? */ - uint32_t bandwidthcapacity; - smartlist_t *exit_policy; /**< What streams will this OR permit - * to exit on IPv4? NULL for 'reject *:*'. */ - /** What streams will this OR permit to exit on IPv6? - * NULL for 'reject *:*' */ - struct short_policy_t *ipv6_exit_policy; - long uptime; /**< How many seconds the router claims to have been up */ - smartlist_t *declared_family; /**< Nicknames of router which this router - * claims are its family. */ - char *contact_info; /**< Declared contact info for this router. */ - unsigned int is_hibernating:1; /**< Whether the router claims to be - * hibernating */ - unsigned int caches_extra_info:1; /**< Whether the router says it caches and - * serves extrainfo documents. */ - unsigned int allow_single_hop_exits:1; /**< Whether the router says - * it allows single hop exits. */ - - unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be - * a hidden service directory. */ - unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this - * router rejects everything. */ - /** True if, after we have added this router, we should re-launch - * tests for it. */ - unsigned int needs_retest_if_added: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 - * uploaded it. */ -#define ROUTER_PURPOSE_GENERAL 0 -/** Tor should avoid using this router for circuit-building: we got it - * from a crontroller. If the controller wants to use it, it'll have to - * ask for it by identity. */ -#define ROUTER_PURPOSE_CONTROLLER 1 -/** Tor should use this router only for bridge positions in circuits: we got - * it via a directory request from the bridge itself, or a bridge - * authority. x*/ -#define ROUTER_PURPOSE_BRIDGE 2 -/** Tor should not use this router; it was marked in cached-descriptors with - * a purpose we didn't recognize. */ -#define ROUTER_PURPOSE_UNKNOWN 255 - - /* In what way did we find out about this router? One of ROUTER_PURPOSE_*. - * Routers of different purposes are kept segregated and used for different - * things; see notes on ROUTER_PURPOSE_* macros above. - */ - uint8_t purpose; -} routerinfo_t; - -/** Information needed to keep and cache a signed extra-info document. */ -typedef struct extrainfo_t { - signed_descriptor_t cache_info; - /** The router's nickname. */ - char nickname[MAX_NICKNAME_LEN+1]; - /** True iff we found the right key for this extra-info, verified the - * signature, and found it to be bad. */ - unsigned int bad_sig : 1; - /** If present, we didn't have the right key to verify this extra-info, - * so this is a copy of the signature in the document. */ - char *pending_sig; - /** Length of pending_sig. */ - size_t pending_sig_len; -} extrainfo_t; - -/** Contents of a single router entry in a network status object. - */ -typedef struct routerstatus_t { - time_t published_on; /**< When was this router published? */ - char nickname[MAX_NICKNAME_LEN+1]; /**< The nickname this router says it - * has. */ - char identity_digest[DIGEST_LEN]; /**< Digest of the router's identity - * key. */ - /** Digest of the router's most recent descriptor or microdescriptor. - * If it's a descriptor, we only use the first DIGEST_LEN bytes. */ - char descriptor_digest[DIGEST256_LEN]; - 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; /**